【ASP.NET CORE】 10. 数据校验

本系列专栏基于杨中科老师的《ASP.NET Core技术内幕与项目 实战》,本人记录梳理的学习笔记,有部分的增补和省略。更全面系统的讲解,请看杨老师的视频课:【.NET教程,.Net Core视频教程,杨中科主讲】

一、数据校验

数据校验是接口开发的第一道防线,用于校验前端/客户端传入的请求参数合法性,避免非法数据入库、业务逻辑异常。ASP.NET Core 提供内置校验方案,同时支持FluentValidation第三方库实现更灵活、解耦的校验逻辑。

1. 内置数据校验

(1)核心特性

ASP.NET Core内置数据校验基于System.ComponentModel.DataAnnotations命名空间,通过特性标签标注模型属性,实现自动校验,无需编写额外逻辑,简单场景开箱即用。

(2)常用校验特性

  • Required\]:必填字段,禁止为null/空字符串;

  • RegularExpression\]:正则表达式自定义校验;

  • StringLength\]:字符串长度限制;

  • CustomValidationAttribute:自定义校验特性;

  • IValidatableObject:模型级自定义校验。

(3)使用示例

cs 复制代码
// 模型类标注校验特性
public class LoginRequest
{
    [Required(ErrorMessage = "邮箱不能为空")]
    [EmailAddress(ErrorMessage = "邮箱格式不合法")]
    public string Email { get; set; }

    [Required(ErrorMessage = "密码不能为空")]
    [StringLength(10, MinimumLength = 3, ErrorMessage = "密码长度3-10位")]
    public string Password { get; set; }
}

// Controller中自动校验
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
    [HttpPost("login")]
    public IActionResult Login(LoginRequest request)
    {
        // 内置自动校验,校验失败返回400
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
        return Ok("登录成功");
    }
}

(4)内置校验的缺陷

  • 违反单一职责原则:校验规则与模型类强耦合,模型既要承载数据,又要管理校验逻辑,不利于维护;
  • 灵活性差:复杂校验(如业务规则校验、跨属性联动校验)需编写大量自定义代码,语法繁琐;
  • 复用性低:相同校验规则无法跨模型复用,需重复编写;
  • 异步校验支持弱:内置校验难以实现异步校验(如校验用户名是否已存在)。

2. FluentValidation

FluentValidation是.NET生态最流行的第三方校验库,采用Fluent API流式语法,将校验逻辑与模型解耦,支持异步校验、依赖注入、规则复用,完美弥补内置校验的缺陷。

(1)核心优势

  • 解耦模型与校验逻辑,符合单一职责原则;
  • 流式语法简洁易懂,可读性、可维护性极强;
  • 支持同步/异步校验、自定义校验、规则复用;
  • 完美适配ASP.NET Core DI,支持注入服务实现业务校验;
  • 自定义错误消息,适配多语言、个性化提示。

(2)使用方法

步骤1:安装NuGet包
bash 复制代码
Install-Package FluentValidation.AspNetCore
// .NET CLI
dotnet add package FluentValidation.AspNetCore
步骤2:Program.cs注册FluentValidation
cs 复制代码
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();

// 注册FluentValidation,自动扫描程序集注册校验器
builder.Services.AddFluentValidation(fv =>
{
    // 自动注册当前程序集所有继承AbstractValidator的校验类
    Assembly assembly = Assembly.GetExecutingAssembly();
    fv.RegisterValidatorsFromAssembly(assembly);
    // 关闭内置DataAnnotations校验,避免冲突
    fv.DisableDataAnnotationsValidation = true;
});

var app = builder.Build();
app.MapControllers();
app.Run();
步骤3:编写请求模型类
cs 复制代码
// 纯数据模型,无任何校验特性,实现解耦
public record Login2Request(string Email, string Password, string Password2);
步骤4:编写校验器类(核心)

校验器继承AbstractValidator<T>,在构造函数中通过流式语法配置校验规则,支持多条件校验、自定义校验、错误消息定制。

cs 复制代码
public class Login2RequestValidator : AbstractValidator<Login2Request>
{
    public Login2RequestValidator()
    {
        // 邮箱校验:非空+格式+域名限制
        RuleFor(x => x.Email)
            .NotNull().WithMessage("邮箱不能为空")
            .EmailAddress().WithMessage("邮箱格式不合法")
            .Must(v => v.EndsWith("@qq.com") || v.EndsWith("@163.com"))
            .WithMessage("仅支持QQ、163邮箱注册");

        // 密码校验:非空+长度+两次密码一致
        RuleFor(x => x.Password)
            .NotNull().WithMessage("密码不能为空")
            .Length(3, 10).WithMessage("密码长度必须介于3-10位")
            .Equal(x => x.Password2).WithMessage("两次输入密码不一致");

        // 确认密码非空校验
        RuleFor(x => x.Password2)
            .NotNull().WithMessage("确认密码不能为空");
    }
}
步骤5:Controller中使用
cs 复制代码
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
    [HttpPost("register")]
    public IActionResult Register(Login2Request request)
    {
        // FluentValidation自动校验,校验失败返回400
        return Ok("注册校验通过");
    }
}

二、依赖注入与异步校验

实际业务中常需校验数据唯一性(如用户名、邮箱是否已存在),FluentValidation支持在校验器中注入服务,实现异步数据库校验。

1.注入服务至校验器

cs 复制代码
public class Login2RequestValidator : AbstractValidator<Login2Request>
{
    private readonly AppDbContext _dbCtx;

    // 构造函数注入EF Core上下文(Scoped服务,FluentValidation自动适配)
    public Login2RequestValidator(AppDbContext dbCtx)
    {
        _dbCtx = dbCtx;

        RuleFor(x => x.Email)
            .NotNull()
            .EmailAddress()
            .MustAsync(CheckEmailExistsAsync)
            .WithMessage(c => $"邮箱{c.Email}已被注册,请更换");
    }

    // 异步校验:校验邮箱是否已存在
    private async Task<bool> CheckEmailExistsAsync(string email, CancellationToken cancellationToken)
    {
        // 不存在返回true,校验通过;存在返回false,校验失败
        return !await _dbCtx.Users.AnyAsync(u => u.Email == email, cancellationToken);
    }
}

2.异步校验要点

  • 使用MustAsync方法实现异步校验,替代同步Must;
  • 校验方法需返回Task<bool>,true代表校验通过,false代表校验失败;
  • 支持传递CancellationToken,适配异步取消操作;
  • 错误消息支持动态拼接参数,个性化提示更友好。

总结

  1. 数据校验是接口安全的基础。简单场景可使用 .NET 内置的 DataAnnotations 快速实现。
  2. 企业级、复杂业务场景强烈推荐使用 FluentValidation ,它解耦校验逻辑、支持异步与依赖注入,代码更优雅、可维护性更强,是 ASP.NET Core 开发的最佳实践。
相关推荐
DJ斯特拉1 小时前
SpringBoot项目的基本构建
java·spring boot·后端
小小心愿家1 小时前
初识 maven,Spring boot,Spring MVC
java·后端·spring
zzh0811 小时前
LNMP环境部署笔记
笔记
Victor3561 小时前
MongoDB(41)如何使用$group阶段?
后端
Victor3561 小时前
MongoDB(40)如何使用$match阶段?
后端
不会写DN1 小时前
Go中的泛型与any、interface有什么区别?
开发语言·后端·golang
智者知已应修善业2 小时前
【不用第三变量交换2个数】2024-10-18
c语言·数据结构·c++·经验分享·笔记·算法
升职佳兴2 小时前
Excel 学习笔记整理:常用操作、数据清洗与公式应用实战
笔记·学习·excel
悠哉悠哉愿意2 小时前
【单片机学习笔记】math库函数补充
c语言·笔记·单片机·学习