I am trying to validate that if a property / field is completely left off on a request that the ModelState is invalid and a BadRequest is sent back to the client, however I am struggling with handling of non-nullable types in request bodies.
Works for nullable types
[Required] public string NullableString { get; set; }
Works for non-nullable and nullable parameters
public IActionResult RequiredNonNullableIntQueryString([Required]int nonNullableInt)
public IActionResult RequiredNullableStringQueryString([Required]string nullableString)
However, it does NOT work for non-nullable types in request bodies
public IActionResult RequiredNonNullableIntBody([FromBody]NonNullablesRequest request)
public class NonNullablesRequest
{
[Required] // I have also tried [BindRequired] with the same result.
public int NonNullableInt { get; set; }
}
I have read:
- Microsoft documentation - Model Validation # [Required] validation on the server
- Good article on [Required] and [BindRequired] in ASP.NET Core MVC
The Microsoft documentation states:
The validation system in .NET Core 3.0 and later treats non-nullable parameters or bound properties as if they had a [Required] attribute. Value types such as decimal and int are non-nullable.
That's cool... However later says
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.
Why? This really doesn't seem to make sense. Why make sure all non-nullables are required but then ignore errors if they were not supplied?
I know many suggestions indicating that one can do the hacky workaround of the following, whereby one sets the required parameter as nullable. To me this does not seem like a reasonable solution.
public class NonNullablesRequest
{
[Required]
public int? NonNullableInt { get; set; }
}
This just feels wrong.
- The datatype is not accurately representing the expectations of the request arriving
- One has to use
.HasValueand.Valueevery time one is accessing the property to avoid "Possible Null" warnings. - Seems like an anti-pattern to C# 8.0's Nullable reference types (C# reference)
Is there a way to configure the ModelBinding to invalidate the ModelState if non-nullable types are not supplied?
Edit 1:
It seems there is quite some debate: ASP.NET Core [Require] non-nullable types I am not sure I agree with Chris Pratt. It is not that we expect the value to not be supplied. In fact the opposite, I want ensure the caller gives me the value. But one has to be defensive against a consumer not supplying the adequate data and therefore the system should reject the requests with a 400 BadRequest.
From this then the expected result is an int not an int?. If no data was supplied the ModelBinder should indicate that the ModelState is invalid.
I can, however, see the challenge where there are two parts 1) Deserialization and then 2) ModelBinding.