[C#][.Net MVC] 06. 驗證參數- 透過FluentValidation
【MVC教學】驗證參數- 透過FluentValidation
Demo範例 : Git位置
上一篇簡介了 Controller 如何安排程式邏輯流程、驗證參數,卻也發現了驗證參數導致程式寫得冗長不易維護,這一篇要介紹【FluentValidation】這個套件來解決這個問題。
首先先透過Nuget來安裝套件,因為我們用的版本是MVC5,所以安裝這個版本。
(註:安裝完後應該會在參考中看到多出兩個 FluentValidation 的組件)
設定
把之前的程式碼稍作整理,將 Account 與 Password 新建一個類型(Class)來封裝起來,原本 Controller裡面的程式整理一下。
將驗證的邏輯透過 FluentValidation 來做,建立一個 Class 命名為 UserSignUpParameterValidation。
將 UserSignUpParameter 掛上Validator Attribute,意思是 UserSignUpParameter 請幫我用UserSignUpParameterValidation 裡面的邏輯來驗證。
\
將原本的驗證邏輯慢慢的移入 UserSignUpParameterValidation 之中
1. 帳號密碼不能為空值
/// Validation UserSignUpParameter
/// </summary>
/// <seealso
cref="FluentValidation.AbstractValidator{HelloDotNetMVC.Parameters.UserSignUpParameter}
" />
public class UserSignUpParameterValidation:AbstractValidator<UserSignUpParameter>
{
public UserSignUpParameterValidation()
{
//AbstractValidator為FluentValidation提供的抽象類別
//目的是讓使用者可以透過繼承這個抽象類別後,實作自己的驗證邏輯
//而泛型T, AbstractValidator<T> ,則帶入你想驗證的類別
//所以這邊帶入UserSignUpParameter,這個我們剛剛製作的Class
//Account
RuleFor(x => x.Account)
.NotEmpty()
.WithMessage("帳號不能為Empty")
.NotNull()
.WithMessage("帳號不能為Null");
//Password
RuleFor(x=>x.Password)
.NotEmpty()
.WithMessage("密碼不能為Empty")
.NotNull()
.WithMessage("密碼不能為Null");
}
}
RuleFor 是 FluentValidation 提供的方法,意思是我為 Account 這個屬性建立驗證的 Rule,而 Rule分 別是,NotEmpty(不能為空值)、NotNull(不能為Null),而緊接在每個驗證邏輯下面的 WithMessage 則是表示,當發生它上面的錯誤時,錯誤訊息請回傳這行。接著我們可以將 Controller 的這塊驗證邏輯拿掉,交給UserSignUpParameterValidation 專責處理就好。
2. 帳號必須包含@字元,Password必須大於6個字
因為判斷字元@並不像判斷空值與Null一樣,官方有實做好的方法,必須自己透過Must來定義,這邊提供兩種寫法給讀者參考
上面是匿名方法的寫法,但如果你對於匿名委派還不是很熟悉,也可以用比較簡單的方式寫
先寫好驗證的方法,然後在Must中帶入方法。
延伸閱讀 :
MSDN : Func<T,TResult> 委派
MSDN : 匿名方法
/// Validation UserSignUpParameter
/// </summary>
/// <seealso
cref="FluentValidation.AbstractValidator{HelloDotNetMVC.Parameters.UserSignUpParameter}
" />
public class UserSignUpParameterValidation:AbstractValidator<UserSignUpParameter>
{
public UserSignUpParameterValidation()
{
//AbstractValidator為FluentValidation提供的抽象類別
//目的是讓使用者可以透過繼承這個抽象類別後,實作自己的驗證邏輯
//而泛型T, AbstractValidator<T> ,則帶入你想驗證的類別
//所以這邊帶入UserSignUpParameter,這個我們剛剛製作的Class
//Account
RuleFor(x => x.Account)
.NotEmpty()
.WithMessage("帳號不能為Empty")
.NotNull()
.WithMessage("帳號不能為Null")
.Must(IncludeAccountKeyword)
.WithMessage("帳號不符合格式");
//Password
RuleFor(x=>x.Password)
.NotEmpty()
.WithMessage("密碼不能為Empty")
.NotNull()
.WithMessage("密碼不能為Null")
.MinimumLength(7)
.WithMessage("密碼必須大於6個字元");
}
/// <summary>
/// Accounts 必須包含@.
/// </summary>
/// <param name="account">The account.</param>
/// <returns></returns>
private bool IncludeAccountKeyword(string account)
{
if (!string.IsNullOrWhiteSpace(account))
{
return account.Contains("@")
}
return false;
}
}
整理後的Controller
透過 ModelState 來得知參數是否符合規則 ModelState 是本來MVC就有提供的參數驗證方式,而使用方法這邊提供幾篇文章給有興趣的讀者參考
What is the ModelState? - ASP.NET MVC Demystified
而因為開發習慣的關係,我習慣用FluentValidation的方式來取代原本ModelState驗證方法與設定方式。接著我們在Global.asax中呼叫FluentValidationModelValidatorProvider.Configure()
public ActionResult SignUp(UserSignUpParameter parameter)
{
if (!ModelState.IsValid)
{
//IsValida為False時,表示驗證參數不過
//取出第一筆錯誤訊息回傳
var Error = ModelState.Values.SelectMany(x => x.Errors).First();
TempData["Message"] = Error.ErrorMessage;
return RedirectToAction("signup", "user");
}
//這邊沒有搬到FluentValidation去驗證的原因是
//使用者存在與否比較屬於服務層的事情,通常需要讀取資料庫才能判斷
//開發上習慣盡量讓驗證參數越乾淨簡單越好,而不是在裡面呼叫很多外部服務(例如資料庫)後做驗證
//這會讓職責過於複雜
if (parameter.Account != "[email protected]")
{
TempData["Message"] = "註冊成功!!";
//註冊成功,導到首頁
return RedirectToAction("index", "home");
}
else
{
TempData["Message"] = "帳號已經存在";
return RedirectToAction("signup", "user");
}
}
接著執行程式,驗證看看是否之前的驗證邏輯都還是正常運作。
比較一下最一開始與套用 FluentValidation 後的程式碼,應該可以明顯感受到差異,這不僅僅只是讓程式變乾淨,更重要的是驗證參數的職責被分離到 UserSignUpParameterValidation 這個類別中,以後要改參數驗證的邏輯只要到這調整即可,不用再擔心 Controller 會被不小心改壞。
延伸閱讀
1. FluentValidation : 官方文件、各種API使用方法
3. FluentValidation : 繼承父類別的驗證
最後,如果你喜歡我們的文章,別忘了到我們的FB粉絲團按讚喔!!