zoukankan      html  css  js  c++  java
  • 使用 FluentValidation 实现数据校验、验重

    来源:https://blog.csdn.net/zl33842902/article/details/90313537

    最近项目里用到了 FluentValidation 对网站用户输入的数据进行了验证,使用起来比较舒服,下面整理一下项目中集成的过程。

    需要集成的项目是一个 asp.net core 2.1 版本的项目。第一步,安装 FluentValidation.AspNetCore,VS会自动安装依赖的 FluentValidation、DI 等包。安装完成后,找到你要验证的数据类,比如我这里是一个修改密码的场景,类名是 UserPassword:

    public class UserPassword
    {
    /// <summary>
    /// 用户名
    /// </summary>
    public string UserName { get; set; }
    /// <summary>
    /// 旧密码
    /// </summary>
    public string OldPassword { get; set; }
    /// <summary>
    /// 新密码
    /// </summary>
    public string NewPassword { get; set; }
    /// <summary>
    /// 重复密码
    /// </summary>
    public string NewPasswordRe { get; set; }
    }
    找到类后,为这个类写验证规则类,需要继承 AbstractValidator<UserPassword> 泛型类,其中 UserPassword 泛型是你要验证的类。在验证类的构造方法里写验证规则,代码如下:

    public class UserPasswordValid : AbstractValidator<UserPassword>
    {
    public UserPasswordValid()
    {
    CascadeMode = CascadeMode.StopOnFirstFailure;
    RuleFor(x => x.UserName).NotNull().WithName("用户名");
    RuleFor(x => x.OldPassword).NotEmpty().Length(4, 32).WithMessage("旧密码不能为空且长度必须符合规则");
    RuleFor(x => x.NewPassword).NotEmpty().Length(4, 32).WithMessage("新密码不能为空且长度必须符合规则")
    .Must(NewNotEqualsOld).WithMessage("新密码不能跟旧密码一样");
    RuleFor(x => x.NewPasswordRe).NotEmpty().WithMessage("重复密码不能为空").Must(ReEqualsNew).WithMessage("重复密码必须跟新密码一样");
    }

    /// <summary>
    /// 判断新旧密码是否一样
    /// </summary>
    /// <param name="model">实体对象</param>
    /// <param name="newPwd">新密码</param>
    /// <returns>结果</returns>
    private bool NewNotEqualsOld(UserPassword model, string newPwd)
    {
    return model.OldPassword != newPwd;
    }
    /// <summary>
    /// 判断新密码与重复密码是否一样
    /// </summary>
    /// <param name="model"></param>
    /// <param name="newPwdRe"></param>
    /// <returns></returns>
    private bool ReEqualsNew(UserPassword model, string newPwdRe)
    {
    return model.NewPassword == newPwdRe;
    }
    }
    FluentValidation 的语法很人性化,初次接触的人大概都能看懂。如 RuleFor(x => x.UserName).NotNull().WithName("用户名");
    这句就是 UserName 这个字段不能为 null ,为 null 时,会报 “用户名”不应为 null,WithName 就是给字段指定一个名字。
    NotEmpty 就是不能为空,包括 null ;WithMessage 就是不符合条件时的提示。

    编写完验证规则,要在 StartUp 里注册一下。在 services.AddMvc 这个子句的后边(注意还是在这句中)加入AddFluentValidation。

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
    .AddFluentValidation(cfg =>
    {
    cfg.RegisterValidatorsFromAssemblyContaining<UserPasswordValid>();
    cfg.RunDefaultMvcValidationAfterFluentValidationExecutes = false;
    });
    其中 cfg.RegisterValidatorsFromAssemblyContaining<UserPasswordValid>(); 这句注册你的验证类,如果有多个,就每个都要注册。cfg.RunDefaultMvcValidationAfterFluentValidationExecutes = false; 这句是告诉程序,用 FluentValidation 验证完,不要再使用 Mvc 的验证了。

    最后就是在控制器里写处理错误的代码了,FluentValidation 会把验证的结果写入 ModelState,我们拿 ModelState 来验证就可以。

    public IActionResult Index([FromBody]UserPassword userPassword)
    {
    if (!ModelState.IsValid)
    {
    return Json(new { Success = false, Item = ModelState.GetErrors() });
    }
    else
    {
    return Json(new { Success = true });
    }
    }
    其中 ModelState.GetErrors() 是我写的一个扩展方法,为了把错误返回给前端,代码如下:

    public static class ExtMethods
    {
    public static List<ValidationError> GetErrors(this Microsoft.AspNetCore.Mvc.ModelBinding.ModelStateDictionary ModelState)
    {
    var errors = new List<ValidationError>();
    foreach (var pair in ModelState)
    {
    foreach (var error in pair.Value.Errors)
    {
    errors.Add(new ValidationError(pair.Key, error.ErrorMessage));
    }
    }
    return errors;
    }
    public static string ToSingleString(this IEnumerable<ValidationError> validations)
    {
    return validations.Select(x => x.Message).ToStringDot();
    }
    }
    /// <summary>
    /// 数据验证错误
    /// </summary>
    public class ValidationError
    {
    public ValidationError(string name, string message)
    {
    this.Name = name;
    this.Message = message;
    }
    public string Name { get; set; }
    public string Message { get; set; }
    }
    代码中的 ToStringDot 方法是我写的扩展方法,功能和 string.Join 一样。我在前端使用的 element-ui 作为前端展示库,效果如下:

    对于数据的基本验证这样就可以,但是我们还有一些数据验证逻辑是需要走数据库的,这时就需要我们把验证的方法告诉 FluentValidation。比如在另一个场景中,我需要验证当前数据和数据库现有数据是否重复,验证的方法在一个叫 RoleService 的服务里, RoleService 实现了 IRoleService 接口,并且使用 aspnetcore 自带的微软DI进行注入。 那么我们需要在 Validator 的构造方法里把 Service 注入进来并在 Must 方法里使用这个 Service 进行验证。

    /// <summary>
    /// 角色创建的验证器
    /// </summary>
    public class RoleEditDtoValidator : AbstractValidator<RoleEditDto>
    {
    public RoleEditDtoValidator(IRoleService roleService)
    {
    RuleFor(x => x.RoleId).NotEmpty().WithName("角色编码");
    RuleFor(x => x.RoleName).NotEmpty().WithName("角色名称");
    RuleFor(x => x).Must(x => roleService.ExistCheck(x.Model))
    .When(x => !x.RoleId.NullOrEmpty())
    .WithMessage("数据存在重复,请检查!");
    }
    }
    然而这样在提示时,不知道提交的数据与数据库里哪条数据发生了重复,所以在提示里要把ID带出来。这个想了半天才想出办法来,代码如下:

    /// <summary>
    /// 角色创建的验证器
    /// </summary>
    public class RoleEditDtoValidator : AbstractValidator<RoleEditDto>
    {
    public RoleEditDtoValidator(IRoleService roleService)
    {
    RuleFor(x => x.RoleId).NotEmpty().WithName("角色编码");
    RuleFor(x => x.RoleName).NotEmpty().WithName("角色名称");
    RuleFor(x => x.WithExistId()).Must(x => { var rst = roleService.ExistCheck(x.Model, out var existId); x.ExistId = existId; return !rst; })
    .When(x => !x.RoleId.NullOrEmpty())
    .WithMessage((x, y) => "与 ID 为 " + y.ExistId.ToStringBy("、") + " 的数据存在重复,请检查!");
    }
    }
    其中 NullOrEmpty 是我写的一个扩展方法,功能和 string.IsNullOrEmpty 一样, ToStringBy 也是我的扩展方法,和 string.Join 功能一样;这两个扩展方法在 xLiAd.ExtMethods 的包里有,有兴趣的童鞋可以 Nuget 一下。WithExistId 方法是生成一个新的类,功能就是把数据库返回的重复 ID 给带上。代码如下:

    public class ModelWithExistId<T> where T : class
    {
    readonly T t;
    public ModelWithExistId(T t)
    {
    this.t = t;
    }
    public T Model => t;
    public IEnumerable<int> ExistId { get; set; }
    }

    public static class ExtMethods
    {
    public static ModelWithExistId<T> WithExistId<T>(this T model) where T : class
    {
    return new ModelWithExistId<T>(model);
    }
    }
    前台提示如下

    OK了,这样就差不多了,完美实现需求。

    需要注意的是,当你传的参数不符合 MODEL 的规范时 ModelState.IsValid 也会为假,比如你接收 类中的一个字段是 int 型,但你传来个空字符串,ModelState.IsValid 就会报值无效。 但是这个无效和 FluentValidation 是没有关系的,这一点容易使人产生困惑,要注意一下。
    ————————————————
    版权声明:本文为CSDN博主「zl33842902」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/zl33842902/article/details/90313537

  • 相关阅读:
    C#中使用$"{}"替换string.Format()
    js==>id-pid转children【】树结构数据
    React使用模板
    C#--fetch配置使用
    ...扩展运算符解释
    java之死锁
    Stream流
    lambda 之 forEach( ->{} )
    java基础之递归(九九乘法表)
    java读取 .xlsx格式文件转成list
  • 原文地址:https://www.cnblogs.com/yibinboy/p/12179472.html
Copyright © 2011-2022 走看看