《ASP.NET Core技术内幕与项目实战》精简集-高级组件4.3:请求数据校验
本节内容,涉及到8.3(P269-P272),以WebApi说明为主。主要NuGet包:
- 内置命名空间:System.ComponentModel.DataAnnotations
- FluentValidation.AspNetCore(数据检验框架)
一、请求数据检验的作用
1、客户端和服务端进行数据交互时,需要对请求数据进行检验,以保证数据安全和用户体验。现代的前端框架,一般都会在提交数据前,在客户端先进行一次检验。但为了提高安全性,在服务端应该进行一次数据检验兜底。
2、服务端检验有两个方案,一是使用AspNetCore内置的数据检验,在System.ComponentModel.DataAnnotations,以特性标注的方式进行。二是使用第三方库FluentValidation,FluentValidation不仅可以用于AspNetCore,还可以用于控制台、WPF、Blazor等项目中。实际开发中,也主要使用FluentValidation,一是检验规则各丰富,二是实现模型与规则的解耦。
3、FluentValidation的校验规则非常丰富,可以查看官方文档
二、AspNetCore内置的数据检验
1 //创建一个模型类,标注校验特性 2 //LoginRequest.cs 3 public class LoginRequest: IValidatableObject 4 { 5 [Required(ErrorMessage ="邮箱必填")] 6 [EmailAddress(ErrorMessage ="请输入正确的邮箱")] 7 public string Email { get; set; } 8 9 [Required] 10 public string Password { get; set; } 11 [Required] 12 public string Password2 { get; set; } 13 14 public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 15 { 16 if (Password != Password2) 17 { 18 yield return new ValidationResult("密码必须一致!"); 19 } 20 } 21 } 22 23 24 //创建一个action,将LoginRequest作为请求参数 25 //在Swagger中输入不符合要求的json,测试检验结果 26 //TestController.cs 27 [ApiController] 28 [Route("api/[controller]/[action]")] 29 public class TestController: ControllerBase 30 { 31 [HttpPost] 32 public void Login(LoginRequest loginRequest) 33 { 34 Console.WriteLine($"Email:{loginRequest.Email},Password:{loginRequest.Password}"); 35 } 36 }
代码解读:
3行:实现IValidatableObject接口,主要目的是自定义一个检验规则(在14-20行)。如果不自定义检验规则,可以不需要实现这个接口
5,6,9,11行:分别对属性进行了必填、EMAIL格式等检验标注
14-20行:实现Validate方法,返回值是ValidationResult集合,我们通过yield return迭代器方式返回验证结果
三、FluentValidation数据校验
1 //第一步:安装Nuget包,FluentValidation.AspNetCore 2 3 //第二步:注册FluentValidation服务 4 //Program.cs 5 builder.Services.AddFluentValidation(fv => 6 { 7 Assembly assembly = Assembly.GetExecutingAssembly(); 8 fv.RegisterValidatorsFromAssembly(assembly); 9 }); 10 11 //第三步:创建模型类,同上例(注意,不需要特性标注) 12 //LoginRequest.cs 13 14 //第四步,创建模型类的校验类 15 //LoginRequestValidator.cs 16 public class LoginRequestValidator: AbstractValidator<LoginRequest> 17 { 18 public LoginRequestValidator() 19 { 20 RuleFor(l => l.Email).NotNull().EmailAddress() 21 .Must(v => v.EndsWith("@qq.com") || v.EndsWith("@163.com")) 22 .WithMessage("只支持QQ和163邮箱"); 23 24 RuleFor(l => l.Password).NotNull().Length(3, 10).WithMessage("密码长度必须介于3-10之间") 25 .Equal(l => l.Password2).WithMessage("两次密码必须一致"); 26 27 } 28 } 29 30 //第五步:创建一个action来验证 31 [ApiController] 32 [Route("api/[controller]/[action]")] 33 public class TestController: ControllerBase 34 { 35 private readonly IValidator<LoginRequest> _validator; 36 private readonly ILogger<TestController> _logger; 37 public TestController(IValidator<LoginRequest> validator, ILogger<TestController> logger) 38 { 39 _validator = validator; 40 _logger = logger; 41 } 42 43 [HttpPost] 44 public async void Login(LoginRequest loginRequest) 45 { 46 var result = await _validator.ValidateAsync(loginRequest); 47 if (!result.IsValid) 48 { 49 foreach (var failure in result.Errors) 50 { 51 _logger.LogError("属性 " + failure.PropertyName + " 错误信息: " + failure.ErrorMessage); 52 } 53 } 54 55 } 56 }
代码解读:
7-8行:通过反射获得程序集,RegisterValidatorsFromAssembly方法把指定程序集中,继承AbstractValidator<T>泛型类的检验类都注册到容器中
16行:校验类的命名约定为“模型类+Validator”,继承AbstractValidator<T>泛型类,泛型参数为需要检验的模型类
18-27行:在构造函数中,创建校验规则。每个需要检验的属性,对应一组RuleFor调用,可以链式调用。WithMessage为错误信息,对应它之前的规则检验结果。
18行:在构造函数中,可以参数方式直接注入其它系统服务,如可以注入DbContext,如下所示:
public LoginRequestValidator(TestDbContext ctx){ RuleFor(l=>l.UserName).Must(name=>ctx.Users.Any(u=>u.UserName = name)); }
特别说明:
1、本系列内容主要基于杨中科老师的书籍《ASP.NET Core技术内幕与项目实战》及配套的B站视频视频教程,同时会增加极少部分的小知识点
2、本系列教程主要目的是提炼知识点,追求快准狠,以求快速复习,如果说书籍学习的效率是视频的2倍,那么“简读系列”应该做到再快3-5倍