進度條

[C#][.Net MVC] 06. 驗證參數- 透過FluentValidation

【MVC教學】驗證參數- 透過FluentValidation

作者: Toyo 更新日期:

Demo範例 : Git位置

 

 

上一篇簡介了 Controller 如何安排程式邏輯流程、驗證參數,卻也發現了驗證參數導致程式寫得冗長不易維護,這一篇要介紹【FluentValidation】這個套件來解決這個問題。

 

首先先透過Nuget來安裝套件,因為我們用的版本是MVC5,所以安裝這個版本。

 

 

 

 

(註:安裝完後應該會在參考中看到多出兩個 FluentValidation 的組件)

 

設定

把之前的程式碼稍作整理,將 Account 與 Password 新建一個類型(Class)來封裝起來,原本 Controller裡面的程式整理一下。

 

 

 

 

將驗證的邏輯透過 FluentValidation 來做,建立一個 Class 命名為 UserSignUpParameterValidation。

 


將 UserSignUpParameter 掛上Validator Attribute,意思是 UserSignUpParameter 請幫我用UserSignUpParameterValidation 裡面的邏輯來驗證。

 

\

 

將原本的驗證邏輯慢慢的移入 UserSignUpParameterValidation 之中

1. 帳號密碼不能為空值

 

 

/// <summary>
/// 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 : 匿名方法

 

/// <summary>
/// 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()

 

 

[HttpPost]
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使用方法

2. FluentValidation : 擴充驗證方法

3. FluentValidation : 繼承父類別的驗證

 

 


最後,如果你喜歡我們的文章,別忘了到我們的FB粉絲團按讚喔!!

Medium 0

Toyo

年過30在軟體業載浮載沉的工程師, 期望靠著一點一滴累積與努力, 讓自己墊起腳能勾到一點點的夢想