Проверьте значения с неверным форматом в ASP.NET Core API - PullRequest
1 голос
/ 30 апреля 2019

В API ASP.NET Core 2.2 у меня есть следующее действие:

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

Где Model является следующим:

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

Свойство PublishedAt требуется и должно быть в прошлом.

При вызове действия я могу предсказать 2 разных сценария:

  1. Данные, отправляемые на действие, не включают PublishedAt или PublishedAt НЕДЕЙСТВИТЕЛЕН.

    Используя DateTime?, я могу проверить, имеет ли он значение NULL или в прошлом, если он определен.

    Имеет ли это смысл?

  2. Данные, отправляемые на мероприятие, включают недействительную PublishedAt дату (2019-20-40).

    В этом случае я понял, что Model становится нулевым, поэтому я не могу его проверить.

    Я также не могу отправить дружеское сообщение, например:

    «Опубликовать при неверном формате даты»

    Как я могу вернуть дружеские сообщения, когда DateTime имеет неправильный формат?

    Я бы не хотел использовать String в качестве типа данных для PublishedAt.

    Может быть, использовать пользовательскую модель Binder?

Ответы [ 2 ]

0 голосов
/ 01 мая 2019

ИМХО, мне нравится подход, приведенный ниже, и он широко используется без проблем. Преимущество этого подхода в том, что он поддерживает вашу модель в чистоте и позволяет разделить проблемы. Ваша логика проверки для Model полностью независима.

Попробуйте использовать FluentValidation. Вы можете прочитать об этом здесь подробно. Это пакет NuGet, который вы можете скачать через NuGet.org. После установки вы можете зарегистрировать его в ConfigureServices, как показано ниже:

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

Строки с номерами 5 и 6 автоматически найдут все открытые неабстрактные типы, которые наследуются от AbstractValidator, и зарегистрируют их в контейнере. Затем вы определяете AbstractValidator для Model, как показано ниже

Перед созданием AbstractValidator

Я знаю, что вы упомянули, что не хотите менять тип опубликованного атрибута на строку. Тем не менее, я бы предложил вам рассмотреть это. Это упростит проверку параметра, в противном случае автоматическое связывание модели может связать его в другом формате, а привязка пользовательской модели немного сложнее, чем следующие.

Если вы действительно хотите избежать изменения PublishedAt на string, Вы можете попробовать тот же подход, слегка изменив правила и посмотреть если это работает для вас

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");
    }
}

Приведенный выше валидатор будет выполнен после процесса привязки модели, и если проверка не пройдена, операторы WithMessage добавят ошибку в ModelState. Как у вас есть [ApiController] атрибут. Ваша модель будет проверена и вернет сообщения, указанные вами в операторах WithMessage.

Или вы можете вручную проверить, находится ли ModelState.IsValid внутри метода действия, и вернуть ObjectResult с ModelState.

0 голосов
/ 30 апреля 2019

Что вы можете сделать, это поставить атрибут Required на PublishedAt

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

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

Если на контроллере есть атрибут ApiController, он должен автоматически ответить BadRequest, если его нет.

Аналогичным образом вы можете добавить свой собственный атрибут проверки с сообщением. Asp.Net документация содержит пример того, как это сделать.

...