本文内容来自书籍: Marinko Spasojevic - Ultimate ASP.NET Core Web API - From Zero To Six-Figure Backend Developer (2nd edition)
只需要对输入验证,而不是输出,以及在验证参数不正确的时候,如何返回一个合适的响应给客户端
使用数据注释特性,会引入一个概念:ModelState
这是一个字典,包含着模型的状态和模型绑定验证器
注意:模型的验证是发生在模型绑定之后,如果验证失败,那么会报错,在请求到达action
之前,模型绑定和验证就做好了,然后在action
中,可以使用ModelState.IsValid
可以看到模型的验证是否通过
一般,在Web API
中,我们不会使用ModelState.IsValid
来验证结果,因为在前面的章节中提到,
[ApiController]
特性,已经帮我们对验证结果已经做了包装,默认会返回400 – BadRequest
,如果模型验证不通过,缺点就是,不允许返回我们自定义的状态码和信息,所以在前面已经在主项目中配置禁止了这种行为
Build-in Attributes
[ValidateNever] [Compare] [EmailAddress] [Phone] [Range] [RegularExpression] [Required] [StringLength]
有时候默认提供的Attributes
对一些场景支持不够好,所以我们可以自定义Attributes,两种方法:
例子:
public class ScienceBookAttribute : ValidationAttribute { public BookGenre Genre { get; set; } public string Error => $"The genre of the book must be {BookGenre.Science}"; public ScienceBookAttribute(BookGenre genre) { Genre= genre; } protected override ValidationResult? IsValid(object? value, ValidationContext validationContext) { var book = (Book)validationContext.ObjectInstance; if (!book.Genre.Equals(Genre.ToString())) return new ValidationResult(Error); return ValidationResult.Success; } }
public class Book : IValidatableObject { public int Id { get; set; } [Required] public string? Name { get; set; } [Range(10, int.MaxValue)] public int Price { get; set; } public string? Genre { get; set; } public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { var errorMessage = $"The genre of the book must be {BookGenre.Science}"; if (!Genre.Equals(BookGenre.Science.ToString())) yield return new ValidationResult(errorMessage, new[] { nameof(Genre) }); } }
在验证PATCH
方法的时候,基本和其他的方法一样,但是有一点需要注意的是,在验证remove
的时候,会将基本数据类型还原为默认的值,比如
[Range(18, int.MaxValue, ErrorMessage = "Age is required and it can't be lower than 18")] public int Age { get; init; }
这个的验证规则是,这个值不允许小于18,那么问题来了,当PATCH
是remove
这个字段的时候,这个字段的值会变成0,但是在最开始的验证中,是没有问题的,但是当数据存入数据库的时候,是不应该的,因为会修改为0,所以需要第二次验证,比如
// 第一次验证,PATCH的验证是发生在这个方法的 patchDoc.ApplyTo(result.employeeToPatch, ModelState); // 需要第二次验证,这时候的0会触发验证规则 TryValidateModel(result.employeeToPatch);