Validating non-nullable property in Body as Required - AspNetCore 3.1

天涯浪子 提交于 2020-06-17 09:08:38

问题


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 .HasValue and .Value every 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.


回答1:


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



来源:https://stackoverflow.com/questions/62279845/validating-non-nullable-property-in-body-as-required-aspnetcore-3-1

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!