在web開發中必不可少的會遇到表單驗證的問題,爲避免數據在寫入到數據庫時出現異常,通常比較安全的作法是前端會先作一次驗證,經過後把數據提交到後端再驗證一次,由於僅僅靠前端驗證是不安全的,有太多的http請求工具能夠輕鬆繞過你的前端驗證把危險數據提交到後端,因此,以前不作後端參數驗證的同窗趕快檢查一下你的代碼~別中招了css
那麼,故事就是有關於後端驗證。html
這裏舉一個項目中真實的註冊場景,帳號註冊主要包含2個信息:手機號和驗證碼,由於我這裏是用webapi的post方式從前端拿數據,因此封裝成了一個MemberRegister對象。以最基礎的非空驗證爲例,一般要寫以下代碼:前端
若是還要加上手機號格式驗證,還得再來一個if。一旦要驗證的信息多的話代碼行就會不少,看着很冗餘。想着既然作的都是同一件事,那能不能封裝一下減小代碼行?架構師allen說能夠試一下鏈式編程,也就是相似Jquery的xxxx.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裏面有沒有錯誤信息,若是有的話就返回錯誤信息,沒有就作後面的操做。但實際上碰到一個問題,當model爲null的時候,第一步驗證沒有問題,但第二步的時候就報錯了,未將對象引用到實例,緣由是model已是null了再取model.Phone不出錯纔怪。問題找到了,那就想着若是model爲null就不執行後面的驗證了,想法不錯但想了好久就是沒找到辦法實現。不知所措的時候,斷點跟了一下出錯的代碼,發現報錯的地方是在執行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#鏈式編程的問題,有支持的也有反對的,反對的人說代碼可讀性不太好、簡單的問題複雜化等等。通過實際實踐,我以爲這個問題偏向於我的喜愛,談不上好壞,怎樣用着爽、開發效率高就行。不喜歡的還請輕點拍磚。
固然,關於這個問題有更好解決方案的但願能交流一下。