使用 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM