Validate values with invalid format in ASP.NET Core API

倖福魔咒の 提交于 2019-12-08 03:58:18

问题


On an ASP.NET Core 2.2 API I have the following action:

public async Task<IActionResult> Create([FromBody]Model model) {
}

Where Model is the following:

public class Model { 
  public DateTime? PublishedAt { get; set; }       
}

Property PublishedAt is required and needs to be in the past.

When calling the action I am able to predict 2 different scenarios:

  1. Data sent to action doesn't include PublishedAt or PublishedAt is NULL.

    By using DateTime? I am able to check if it is NULL or in the past in case it is defined.

    Does this make sense?

  2. Data sent to action includes an invalid PublishedAt date (2019-20-40).

    In this case I realised that Model becomes null so I cannot validate it.

    I also cannot send back a friendly message such as:

    "Publish At date format is invalid"

    How can I return friendly messages when a DateTime has wrong format?

    I would like to avoid using String as data type for PublishedAt.

    Maybe using a custom Model Binder?


回答1:


What you can do is to put Required attribute on PublishedAt

public class Model 
{ 
    [Required]
    public DateTime? PublishedAt { get; set; }       
}

[ApiController]
public class ValuesController : ControllerBase
{
   public async Task<IActionResult> Create([FromBody]Model model) 
   {
   }
}

If you have ApiController attribute on your controller it should automatically respond with BadRequest if it's not present.

Similarly you can add your own custom validation attribute with a message. Asp.Net documentation has an example on how to do that.




回答2:


IMHO, I like the approach below and have used it extensively without any problems. The good thing about this approach is that it keeps your model clean and enables separation of concerns. Your validation logic for the Model is completely independent.

Try using FluentValidation. You can read about it here in detail. It's a NuGet package that you can download via NuGet.org. Once installed you can register it in ConfigureServices like below:

1 public void ConfigureServices(IServiceCollection services)
2 {
3    services.AddMvc(setup => {
4      //...mvc setup...
5    }).AddFluentValidation(configuration => configuration
6      .RegisterValidatorsFromAssemblyContaining<Startup>());
7 }

Line number 5 and 6 will automatically find any public, non-abstract types that inherit from AbstractValidator and register them with the container. You then define your AbstractValidator for Model as below

Before you create your AbstractValidator

I know you mentioned that you would want to avoid changing the PublishedAt type to string. However, I would suggest you consider it. That will make it easy to validate the parameter, otherwise, automatic model binding may bind it in a different format, and custom model binding is little trickier than the following.

If you really want to avoid changing the PublishedAt to string, you can try the same approach by changing the rules slightly and see if that works for you

public class ModelValidator : AbstractValidator<Model>
{
    public ModelValidator()
    {
        // add a rule that Date must be in the past, shouldn't be empty
        // and in the correct format
        RuleFor(model => model.PublishedAt)
           .Cascade(CascadeMode.StopOnFirstFailure)
           .Must(date => !string.IsNullOrWhiteSpace(date))
               .WithMessage("PublishAt is a required parameter")
           .Must(arg =>
           {
               if (DateTime.TryParseExact(arg.ToString(), new[] { "dd-MMM-yyyy" }, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime date))
               {
                   return date < DateTime.Now;
               }

               return false;
            })
            .When(model => !string.IsNullOrWhiteSpace(model.PublishedAt))
            .WithMessage("Argument PublishAt is invalid. Please specify the date in dd-MMM-yyy and should be in the past");
    }
}

The above validator will be executed after the model binding process and if the validation fails, WithMessage statements will add the error to the ModelState. As you have [ApiController] attribute. Your model will be validated and it will return the messages you specified in the WithMessage statements.

Or you can manually check if the ModelState.IsValid inside the action method and return the ObjectResult with the ModelState.



来源:https://stackoverflow.com/questions/55925199/validate-values-with-invalid-format-in-asp-net-core-api

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