前言
一个在实际应用中 EF Core 集成 FluentValidation 进行数据校验的例子。
Step By Step 步骤
-
创建一个 Asp.Net Core WebApi 项目
-
引用以下 Nuget 包
FluentValidation.AspNetCore
Microsoft.AspNetCore.Identity.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Relational
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools -
创建 Login 操作方法的请求参数模型类 LoginRequest
// LoginRequest 类只是一个普通的C#类, // 没有标注任何的Attribute或者实现任何的接口, // 它的唯一责任就是传递数据 public record LoginRequest(string Email, string Password, string PasswordConfirm);
-
修改 appsettings.json,添加数据库连接字符串
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", "ConnectionStrings": { "Default": "Server=(localdb)\\mssqllocaldb;Database=IdentityTestDB;Trusted_Connection=True;MultipleActiveResultSets=true" } }
-
创建用户实体类User和Role
using Microsoft.AspNetCore.Identity; public class User : IdentityUser<long> { public DateTime CreationTime { get; set; } public string? NickName { get; set; } } public class Role : IdentityRole<long> { }
-
创建继承自IdentityDbContext的上下文类
using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; public class TestDbContext : IdentityDbContext<User, Role, long> { public TestDbContext(DbContextOptions<TestDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly); } }
-
打开 Program.cs,注册 Identity 和 FluentValidation
using FluentValidation; using FluentValidation.AspNetCore; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using System.Reflection; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); // 注册标识框架相关的服务,并配置相关选项 IServiceCollection services = builder.Services; services.AddDbContext<TestDbContext>(opt => { string connStr = builder.Configuration.GetConnectionString("Default")!; opt.UseSqlServer(connStr); }); services.AddDataProtection(); services.AddIdentityCore<User>(options => { options.Password.RequireDigit = false; options.Password.RequireLowercase = false; options.Password.RequireNonAlphanumeric = false; options.Password.RequireUppercase = false; options.Password.RequiredLength = 6; options.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultEmailProvider; options.Tokens.EmailConfirmationTokenProvider = TokenOptions.DefaultEmailProvider; }); var idBuilder = new IdentityBuilder(typeof(User), typeof(Role), services); idBuilder.AddEntityFrameworkStores<TestDbContext>() .AddDefaultTokenProviders() .AddRoleManager<RoleManager<Role>>() .AddUserManager<UserManager<User>>(); // 注册 FluentValidation 服务 Assembly assembly = Assembly.GetExecutingAssembly(); builder.Services.AddFluentValidationAutoValidation() .AddFluentValidationClientsideAdapters() .AddValidatorsFromAssembly(assembly); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
-
编写请求参数模型类 LoginRequest
public record LoginRequest(string UserName, string Password);
-
编写继承自AbstractValidator的数据校验类,留意注释
using FluentValidation; using Microsoft.EntityFrameworkCore; public class LoginRequestValidator : AbstractValidator<LoginRequest> { // 通过构造方法注入了TestDbContext public LoginRequestValidator(TestDbContext dbCtx) { RuleFor(x => x.UserName) .NotNull() // 使用TestDbContext服务检查用户名是否存在 // 同步方式 .Must(name => dbCtx.Users.Any(u => u.UserName == name)) // 异步方式,但使用异步后出错,暂时未能找到解决方案 //.MustAsync((name,_) => dbCtx.Users.AnyAsync(u => u.UserName == name)) // 用Lambda表达式的形式使用模型类中的属性对报错信息进行格式化 .WithMessage(c => $"用户名{c.UserName}不存在"); } }
-
打开登录请求控制器,编写 Login API
using Microsoft.AspNetCore.Mvc; namespace FluentValidationSample2.Controllers { [ApiController] [Route("[controller]/[action]")] public class TestController : ControllerBase { [HttpPost] public ActionResult Login(LoginRequest req) { return Ok(); } } }
-
在 Postman 或 Swagger 测试 Login API,如果请求的用户名不存在,即会返回代码中定义的错误信息