【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 开发的最佳实践。
相关推荐
uElY ITER1 小时前
Spring全家桶简介
java·后端·spring
lay_liu8 小时前
springboot 文件下载
java·spring boot·后端
Flittly8 小时前
【SpringAIAlibaba新手村系列】(11)Embedding 向量化与向量数据库
java·笔记·spring·ai·springboot
热爱生活的猴子9 小时前
Tokenizer 与 Embedding 核心笔记
笔记·embedding
武藤一雄12 小时前
C# 异步回调与等待机制
前端·microsoft·设计模式·微软·c#·.netcore
杰尼龟36812 小时前
Convince Develop 学习笔记
笔记·学习
不早睡不改名@12 小时前
Netty源码分析---Reactor线程模型深度解析(二)
java·网络·笔记·学习·netty
2601_9498161612 小时前
spring.profiles.active和spring.profiles.include的使用及区别说明
java·后端·spring
2501_9381768812 小时前
股指期货的交易成本全解析
笔记
中屹指纹浏览器12 小时前
2026多账号运营的零信任架构:指纹浏览器与网络安全的深度融合实践
经验分享·笔记