C# Web开发教程(十二)数据校验机制

内置数据校验机制

  • 框架内置了对数据校验的支持
- 在System.ComponentModel.DataAnnotations这个命名空间下:
	- 比如[Required]、[EmailAddress] 、[RegularExpression] 、 CustomValidationAttribute、IValidatableObject等待

  • demo演示
// LoginRequest1.cs

using System.ComponentModel.DataAnnotations;

namespace WebApplicationAboutDataValidate
{
    public class LoginRequest1
    {
        [Required]
        [EmailAddress]
        [RegularExpression("^.*@(qq|163)\\.com$", ErrorMessage = "只支持QQ和163邮箱")]
        public string Email { get; set; }

        [Required]
        [StringLength(10, MinimumLength = 3)]
        public string Password { get; set; }
        [Compare(nameof(Password2), ErrorMessage = "两次密码必须一致")]
        public string Password2 { get; set; }
    }
}

  • 测试效果
- 提交如下字段

{
  "email": "user@example.com",
  "password": "string",
  "password2": "string"
}

- 返回:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-840f868ea1c247a51f659d0092a68ae9-a7f61df6b9a99cd3-00",
  "errors": {
    "Email": [
      "只支持QQ和163邮箱"
    ]
  }
}

- 提交如下字段:

{
  "email": "user@qq.com",
  "password": "string123",
  "password2": "string"
}

- 返回:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-e635ad8de5684008becbe67b9be814e8-fea276f49eca5228-00",
  "errors": {
    "Password2": [
      "两次密码必须一致"
    ]
  }
}
  • 内置数据校验机制的问题点
- 校验规则都是和模型类耦合在一起,违反“单一职责原则”;
- 很多常用的校验都需要编写自定义校验规则,而且写起来麻烦。

数据校验机制第三方库--- FluentValidation1

  • FluentValidation在很多项目中都可以使用(比如WinForm,WPF)

  • 注意事项: FluentValidation可以和内置的数据校验机制混用,但最好不要这么干,避免混淆和未知的坑.

  • 安装库

- Install-Package Microsoft.EntityFrameworkCore.Tools -Version 6.0.0
-  Install-Package FluentValidation.AspNetCore -Version 10.3.4 // 这里不要搞成6的版本,有坑
  • Program.cs注册一下
// LoginRequest1.cs
using System.ComponentModel.DataAnnotations;

namespace WebApplicationAboutDataValidate
{
    public class LoginRequest1
    {
       
        public string Email { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public string Password2 { get; set; }
    }
}


// AddNewUserRequestValidator.cs---自定义校验器

using FluentValidation;

namespace WebApplicationAboutDataValidate
{
    public class AddNewUserRequestValidator : AbstractValidator<LoginRequest1>
    {
        public AddNewUserRequestValidator()
        {
            RuleFor(x => x.Email).NotNull().EmailAddress().WithMessage("邮箱必须是合法地址").Must(x => x.EndsWith("@163.com") || x.EndsWith("@qq.com")).WithMessage("支持163或者QQ邮箱");
            RuleFor(x => x.UserName).NotNull().Length(3,10);
            RuleFor(x => x.Password).Equal(x => x.Password2).WithMessage("两次密码必须一致");
        }
    }
}

// Program.cs
......
builder.Services.AddSwaggerGen();
// 注册全局验证器
builder.Services.AddFluentValidation(fv => {
    Assembly assembly = Assembly.GetExecutingAssembly();
    fv.RegisterValidatorsFromAssembly(assembly);
});
var app = builder.Build();
......


// 测试接口

using Microsoft.AspNetCore.Mvc;

namespace WebApplicationAboutDataValidate.Controllers
{
    [ApiController]
    [Route("[controller]/[action]")]
    public class ValidateDataController : ControllerBase
    {

        [HttpPost]
        public ActionResult Login(LoginRequest1 req)
        {
            return Ok(req);
        }
    }
}



- 测试: https://localhost:7263/ValidateData/Login

{
  "email": "string",
  "userName": "string",
  "password": "string",
  "password2": "string111"
}

- 返回结果

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-7666f45f49e7afcab428d6bbd34d5bfb-1d6ed200ab8f73e3-00",
  "errors": {
    "Email": [
      "邮箱必须是合法地址",
      "支持163或者QQ邮箱"
    ],
    "Password": [
      "两次密码必须一致"
    ]
  }
}
  • FluentValidation + DI高级功能---依赖注入: 可以通过构造方法来向校验器注入服务
using FluentValidation;
using Microsoft.AspNetCore.Identity;

namespace WebApplicationAboutDataValidate
{
    public class AddNewUserRequestValidator : AbstractValidator<LoginRequest1>
    {
        public AddNewUserRequestValidator(UserManager<LoginRequest1> userManager)
        {
            RuleFor(x => x.Email).NotNull().EmailAddress().WithMessage("邮箱必须是合法地址").Must(x => x.EndsWith("@163.com") || x.EndsWith("@qq.com")).WithMessage("支持163或者QQ邮箱");
            RuleFor(x => x.UserName).NotNull().Length(3,10).MustAsync(async (x,_) => await userManager.FindByNameAsync(x) == null).WithMessage("用户名已存在");
            //RuleFor(x => x.Password).Equal(x => x.Password2).WithMessage("两次密码必须一致");
            RuleFor(x => x.Password).Equal(x => x.Password2).WithMessage(x => $"密码{x.Password}和{x.Password2}不一致");
        }
    }
}

核心要点总结

  1. 分离关注点:FluentValidation 将校验逻辑从模型类中分离出来
  2. 链式编程:使用流畅的接口设计,代码可读性更强
  3. 灵活性强:支持复杂的业务逻辑校验,包括异步校验
  4. 错误信息定制:可以灵活定制错误消息,支持动态消息
  5. 依赖注入支持:可以在校验器中注入服务,实现更复杂的业务校验
posted @ 2025-11-04 14:39  清安宁  阅读(17)  评论(0)    收藏  举报