ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

使用 FluentValidation 实现数据校验、验重

2020-01-11 11:53:36  阅读:375  来源: 互联网

标签:RuleFor ModelState FluentValidation string 验证 验重 校验 class public


来源: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

标签:RuleFor,ModelState,FluentValidation,string,验证,验重,校验,class,public
来源: https://www.cnblogs.com/yibinboy/p/12179472.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有