【问题标题】:Validate values with invalid format in ASP.NET Core API在 ASP.NET Core API 中验证格式无效的值
【发布时间】:2019-09-19 08:48:43
【问题描述】:

在 ASP.NET Core 2.2 API 上,我有以下操作:

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

其中Model 如下:

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

属性PublishedAt 是必需的,并且必须是过去的。

调用该操作时,我能够预测 2 种不同的场景:

  1. 发送到操作的数据不包括 PublishedAtPublishedAt 为 NULL。

    通过使用DateTime?,我可以检查它是否为 NULL 或过去是否已定义。

    这有意义吗?

  2. 发送到操作的数据包含无效的 PublishedAt 日期 (2019-20-40)。

    在这种情况下,我意识到Model 变为空,因此我无法验证它。

    我也无法发回友好消息,例如:

    “发布日期格式无效”

    如何在 DateTime 格式错误时返回友好消息?

    我想避免使用字符串作为PublishedAt 的数据类型。

    也许使用自定义模型绑定器?

【问题讨论】:

    标签: c# asp.net-core asp.net-core-2.2


    【解决方案1】:

    你可以做的是把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 documentation 有一个关于如何做到这一点的示例。

    【讨论】:

    • 这不是重点......如果定义了 PublishedAt 但类似“2018400sdsd”的内容无效,我想发送一条错误消息。在这种情况下,验证不会触发,因为错误发生在之前。在绑定中。
    • 如果您想接受任何内容并定义自己的消息,您可以将类型更改为字符串并验证其格式是否正确,尽管可能有更好的方法。虽然如果你需要一个日期时间,你不会得到一个空值,但是会返回一个错误给客户端,因为一个参数是必需的,但它不能被解析
    • 是的,这正是我需要的,但不使用字符串... :-) ... 似乎在使用 FromBody 时,JSonParser 负责绑定,如果它一旦停止就会失败并且它不会创建具有有效值的部分模型......我正在尝试解决这个问题。有什么想法吗?
    • 如果我发送错误的输入,我会收到此错误:“无法将字符串转换为日期时间:2019-01dd-01。路径‘publishedAt’,第 2 行,位置 31。”这还不够吗?您要返回自定义消息吗?
    • 尝试向您的模型添加另一个属性,例如“字符串标题”。如果您提交具有无效 PublishedAt 日期的有效标题,您将看到该模型变为空。我想返回 2 个错误,说“PublishedAt 数据格式无效”并且仍然能够验证 Title。
    【解决方案2】:

    恕我直言,我喜欢下面的方法,并且已经广泛使用它,没有任何问题。这种方法的好处是它可以让你的模型保持干净并且可以分离关注点。 Model 的验证逻辑是完全独立的。

    尝试使用FluentValidation。您可以详细阅读here。这是一个 NuGet 包,您可以通过 NuGet.org download。安装后,您可以在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 继承的任何公共、非抽象类型,并将它们注册到容器中。然后,您为Model 定义您的AbstractValidator,如下所示

    在创建 AbstractValidator 之前

    我知道您提到您希望避免将 PublishedAt 类型更改为字符串。不过,我建议你考虑一下。这将使参数的验证变得容易,否则,自动模型绑定可能会以不同的格式绑定它,并且自定义模型绑定比以下更棘手。

    如果你真的想避免将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,并返回带有ModelState 的ObjectResult

    【讨论】:

    • 我正在使用您正在使用的内容,包括 FluentValidation,但我想避免将 String 用于 DateTime 属性,例如 PublishedAt。所以我试图将 ASP.NET Core ModelBinder 错误与 FluentValidation 错误合并。但我认为有一个问题是 Newtonsoft Json 不创建部分模型。因此,在出现错误时,它不会进入 Fluent Validation。
    • 好的,尝试将PublishedAt 更改为简单的DateTime 而不是DateTime?。在验证器中,添加一个检查 MinDate 的规则,而不是 null 或空。所以如果是Minimum,说明还没有传递给请求。
    猜你喜欢
    • 2019-08-03
    • 2022-11-14
    • 2020-05-07
    • 2016-01-23
    • 2011-12-07
    • 2016-12-22
    • 2021-04-09
    • 2017-09-19
    • 1970-01-01
    相关资源
    最近更新 更多