【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 开发的最佳实践。
相关推荐
Victor3566 小时前
MongoDB(112)如何使用MongoDB Charts进行数据可视化?
后端
三品吉他手会点灯14 小时前
C语言学习笔记 - 20.C编程预备计算机专业知识 - 变量为什么必须的初始化【重点】
c语言·笔记·学习
kobesdu14 小时前
【ROS2实战笔记-12】rosshow:终端里的盲文可视化与无头机器人的现场调试
笔记·机器人·ros·移动机器人
sakiko_14 小时前
UIKit学习笔记1-创建项目(使用UIKit)、使用组件
笔记·学习
前端一小卒15 小时前
我用 Claude Code 的 Superpowers 技能链写了个服务,部署前差点把服务器搞炸
前端·javascript·后端
智者知已应修善业15 小时前
【51单片机中的打飞机设计】2023-8-25
c++·经验分享·笔记·算法·51单片机
czhc114007566315 小时前
C# 428 线程、异步
开发语言·c#
唐青枫16 小时前
C#.NET ThreadLocal 深入解析:线程独享数据、性能收益与实战边界
c#·.net
曹牧16 小时前
Spring:@RequestMapping注解,匹配的顺序与上下文无关
java·后端·spring
智者知已应修善业17 小时前
【51单片机按键调节占空比3位数码管显示】2023-8-24
c++·经验分享·笔记·算法·51单片机