由表單驗證提及,關於在C#中嘗試鏈式編程的實踐

在web開發中必不可少的會遇到表單驗證的問題,爲避免數據在寫入到數據庫時出現異常,通常比較安全的作法是前端會先作一次驗證,經過後把數據提交到後端再驗證一次,由於僅僅靠前端驗證是不安全的,有太多的http請求工具能夠輕鬆繞過你的前端驗證把危險數據提交到後端,因此,以前不作後端參數驗證的同窗趕快檢查一下你的代碼~別中招了css

 

那麼,故事就是有關於後端驗證。html

這裏舉一個項目中真實的註冊場景,帳號註冊主要包含2個信息:手機號和驗證碼,由於我這裏是用webapipost方式從前端拿數據,因此封裝成了一個MemberRegister對象。以最基礎的非空驗證爲例,一般要寫以下代碼:前端

若是還要加上手機號格式驗證,還得再來一個if。一旦要驗證的信息多的話代碼行就會不少,看着很冗餘。想着既然作的都是同一件事,那能不能封裝一下減小代碼行?架構師allen說能夠試一下鏈式編程,也就是相似Jqueryxxxx.attr().css().html().show()這樣,看起來還不錯的樣子,那就幹吧。web

其實C#裏也有相似的用法,好比Linq裏面的xxxx.Where().OrderBy().Select()這種,可是這種實際上每次返回的都是不一樣的對象,而後執行對象裏的方法,這並不適合個人需求,由於我執行的驗證方法確定都是同一個,好比validate().validate().validate()這種,因而決定用擴展方法來實現。先定義一個被擴展的對象:數據庫

public class ValidateResult<T>
    {
        public List<string> Errors { get; set; }
        public T Entity { get; private set; }
 
        public ValidateResult(T entity)
        {
            Errors = new List<string>();
            Entity = entity;
        }
}

定義擴展方法:編程

public static ValidateResult<T> Validate<T>(this ValidateResult<T> target, Predicate<T> predicate, string errorMessage)
        {
            if (!predicate(target.Entity))
            {
                target.Errors.Add(errorMessage) ;
            }
            return target;
        }

使用辦法:後端

var error = new ValidateResult<MemberRegister>(model)
                .Validate(m => m != null, ResponseTip.ParamError)
                .Validate(m => !string.IsNullOrEmpty(m.Phone), ResponseTip.PhoneRequired)
                .Validate(m => !string.IsNullOrEmpty(m.CodeValue), ResponseTip.ValidateCodeRequired)
                .Errors;

理想中的狀況是,能夠判斷error裏面有沒有錯誤信息,若是有的話就返回錯誤信息,沒有就作後面的操做。但實際上碰到一個問題,當modelnull的時候,第一步驗證沒有問題,但第二步的時候就報錯了,未將對象引用到實例,緣由是model已是null了再取model.Phone不出錯纔怪。問題找到了,那就想着若是modelnull就不執行後面的驗證了,想法不錯但想了好久就是沒找到辦法實現。不知所措的時候,斷點跟了一下出錯的代碼,發現報錯的地方是在執行if (!predicate(target.Entity))的時候,因而換了一個思路,改進一下代碼:api

    public class ValidateResult<T>
    {
        public string Error { get; set; }
        public T Entity { get; private set; }
 
        public ValidateResult(T entity)
        {
            Entity = entity;
        }
    }

擴展方法:安全

        public static ValidateResult<T> Validate<T>(this ValidateResult<T> target, Predicate<T> predicate, string errorMessage)
        {
            if (string.IsNullOrEmpty(target.Error))
            {
                if (!predicate(target.Entity))
                {
                    target.Error = errorMessage;
                }
            }
            return target;
        }

改進後的代碼把ValidateResult裏的Errors取消了換成了string類型的Error(要那麼多錯誤提示也沒什麼用,一個就夠了),而後驗證失敗後就更新這個屬性,驗證的時候若是這個屬性string.IsNullOrEmpty(target.Error)就表示前面的驗證都經過了本次能夠繼續驗證,若是! string.IsNullOrEmpty(target.Error)就表示前面的驗證已經失敗了本次不用驗證,要驗證的對象原封不動的返回。這樣子就不會報錯了,而後調用結果判斷Error是否NullOrEmpty再作相應操做。測試一下,沒有問題。代碼演變爲:架構

 

優勢

可讀性我的以爲並不比直接if差,分行顯示的話仍是能很清晰看出具體的驗證項。

省去了每次判斷的if語句和return,支持自定義驗證規則和錯誤提示。

減小了代碼的行數。

 

缺點

某次驗證失敗不能中斷後面的驗證,多執行了沒必要要的代碼,這點用if能夠避免。

 

總結

完了之後去網上找了一些C#鏈式編程的問題,有支持的也有反對的,反對的人說代碼可讀性不太好、簡單的問題複雜化等等。通過實際實踐,我以爲這個問題偏向於我的喜愛,談不上好壞,怎樣用着爽、開發效率高就行。不喜歡的還請輕點拍磚。

固然,關於這個問題有更好解決方案的但願能交流一下。

相關文章
相關標籤/搜索