Ondrej Vencovsky
Ondrej Vencovsky

Reputation: 3508

.NET Core WebApi Action is executed even with missing properties in the request body

My .NET Core 3.1 WebApi controller PUT action is found and execute even if it should not. I have request data object with 2 properties in [FromBody] parameter. But if I call this route with absolutely different properties in the request Body, or no properties at all - it stil seems to be OK and Action is executed, just properties have default values for its types. I expected 400 Bad Request.

public class UpdateLogRequestData
{
    [Required]
    public int Records { get; set; }

    [Required]
    public int LogStatusId { get; set; }
}

Controller Action:

[HttpPut]
[Route("{logId:int}")]
public IActionResult UpdateLog(
    [FromRoute] int logId,
    [FromBody] UpdateLogRequestData requestData)
{
    if (!this.ModelState.IsValid)
    {
        return this.BadRequest(this.ModelState);
    }

    ...
}

I tried to add [Required] attributes and ModelState validation later when I noticed the Action is executed even by "bad request" - ie request with incorrectly named properties. Properties in UpdateLogRequestData just have 0 as their values (int default value).

It is dangerous behavior since I update records in the DB. And now, if someone sends the request without Records and LogStatusId properties, database will be updated with zeroes. Why controller doesn't check it? It's the first time I see something like this. Why no Bad Request happens in that case?

Upvotes: 4

Views: 1785

Answers (2)

Ondrej Vencovsky
Ondrej Vencovsky

Reputation: 3508

So after deep digging in the Microsoft documentation I have found that validation in Web API has been changed since version .NET Core 3. For those with the same problem here is how ti works.

Web API controllers don't have to check ModelState.IsValid if they have the [ApiController] attribute. In that case, an automatic HTTP 400 response containing error details is returned when model state is invalid. For more information, see Automatic HTTP 400 responses.

[Required] validation on the server. On the server, a required value is considered missing if the property is null. A non-nullable field is always valid, and the [Required] attribute's error message is never displayed.

However, model binding for a non-nullable property may fail, resulting in an error message such as The value '' is invalid. To specify a custom error message for server-side validation of non-nullable types, you have the following options:

Make the field nullable (for example, decimal? instead of decimal). Nullable value types are treated like standard nullable types.

OR

Specify the default error message to be used by model binding, as shown in the following example:

services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        options.MaxModelValidationErrors = 50;
        options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
            _ => "The field is required.");
    });

services.AddSingleton<IValidationAttributeAdapterProvider,
    CustomValidationAttributeAdapterProvider>();

See the line

options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor(
            _ => "The field is required.");

Upvotes: 3

Nkosi
Nkosi

Reputation: 247471

Make those value types nullable so that they do not default to the non-null default values when omitted in the request body.

public class UpdateLogRequestData {
    [Required]
    public int? Records { get; set; }

    [Required]
    public int? LogStatusId { get; set; }
}

Upvotes: 1

Related Questions