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.
[Required] public string NullableString { get; set; }
public IActionResult RequiredNonNullableIntQueryString([Required]int nonNullableInt)
public IActionResult RequiredNullableStringQueryString([Required]string nullableString)
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:
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.
.HasValue
and .Value
every time one is accessing the property to avoid "Possible Null" warnings.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.
You could use two different classes for this.
One that represents what your web-application sends to the backend and another that represents your domain model (e.g. entity model).
Http Transport Object
public class YourWebAppDto
{
[Required]
public int? NonNullableInt { get; set; }
}
Domain Model Object
public class YourDomainModelObject
{
public int NonNullableInt { get; set; }
}
By separating those two concerns, you can keep your domain model clean while addressing the potential invalid input of your web application.
Of course, you need a mapping between the two. We use automapper for this.
I know, this does not directly answer your question but it might help as an other way of looking at things.
Cheers, Mike
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With