.Net高級進階,教你如何構建企業模型數據攔截層,動態控制字段驗證

如今,你有一個MVC架構的web項目,你要完成一個註冊功能。html

前臺傳了3個值到你的控制器,分別是帳號、密碼、郵箱。web

如圖:如今你要在控制器裏面判斷,帳號名稱、密碼、郵箱不能爲空,而且名稱和密碼不超過16位。正則表達式

上面這個圖只是個理想中的小例子,實際開發狀況是,可能一次性要傳十幾個字段甚至更多。c#

那麼在實際開發中,一般爲了複用性,咱們將這3個參數用一個實體類來代替。設計模式

即以下所示。api

注:這一步會有個知識點,叫作模型驗證,不懂的童鞋能夠百度下,MVC會經過必定規則自動直接將參數反序列化成所對應的實體類,可是由於我這個示例是webapi模式的,寫法略有不一樣,因此還要在參數前加個[FromBody]才能自動反序列化。架構

至於具體爲何會自動反序列化,在本篇並非我要講的主題,因此感興趣的童鞋能夠百度下:MVC下的ModelBinder    。mvc

攔截層的解耦

如今,我認爲把實體類驗證給帶到控制器裏去寫的這種方式有點不美,若是業務規則多的話,那麼這樣的驗證代碼就很是龐大,而且若是整個項目都採用這種驗證模式,那麼在我往後的維護階段中就顯得有點臃腫的感受,實體類依賴於控制器方法去驗證,我得先找到這個實體類,而後仔細想一想有哪些方法用到了該實體類,又作了哪些驗證判斷,而後維護。框架

那麼我能不能在控制器方法中 驗證明體類這一步 給挪掉,不寫到控制器的方法當中,寫在另外一個地方,統一進行管理,實現實體類的驗證與控制器中的方法業務邏輯分除。ide

這種行爲操做有點像httpModule,思想上就是設計模式所謂的下降耦合性了。

那麼怎麼作呢?

咱們能夠直接在實體類中加驗證,如圖

上面看到[Required],[StringLength],[RegularExpression]的這些叫作驗證特性,是.net框架已經封裝好的,它會對標註特性的字段採起驗證。

[Required]限制了必須輸入,[Required(ErrorMessage = "請輸入用戶名")]  

[StringLength]限制了規定的長度,[StringLength(10, ErrorMessage = "長度不能超過10個字符")]  

[Range] 限制了值的範圍,[Range(0, 120, ErrorMessage = "年齡範圍在0到120歲之間")]  

[RegularExpression] 限制了必須知足正則表達式,[RegularExpression(@"^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$", ErrorMessage = "請輸入Email格式")]  

[Compare]限制了與之對應的字段相等,[Compare("pwd", ErrorMessage = "兩次密碼要一致")]  //該特性標註的字段值必須與pwd字段值相等

.net也就封裝了幾個,這5個用的最多(固然,也能夠自定義這種驗證特性,對這塊想深刻了解的請百度:mvc ValidationAttribute)。

那麼我標註了特性後如何進行判斷呢?

  

咱們看下控制器方法中的寫法:

如圖,用 ModelState.IsValid  這段話來對驗證結果進行斷定,若是實體類上的被標註的特性知足條件的話,就爲true,不然爲false。

那麼,由於這種模型驗證是種模式,是全局的,因此應該單獨拿出來在攔截層進行註冊。

如圖:

這段代碼的意思就是:每當進入控制器方法以前,會判斷這個方法的名稱,若是包含的有Insert、check、update這三者的任意一個,都會進行攔截驗證(對模型驗證的結果進行斷定),若是爲false,那麼就返回給客戶端一個400狀態碼。

而後註冊一下:(註冊的地方只是個範例,由於我是webapi,只對http進行攔截)

 

model負責填寫規則,驗證由專門的驗證人員去作,邏輯由專門的邏輯人員去寫,這樣就各司其職了。

 不過,這才只是第一步!

(隨着你平常的開發,你確定會遇到這種狀況)

user實體類,是專一於註冊方法,說白了,就是爲註冊方法所寫的,

我如今還要寫個登陸方法。

可是登陸的時候,我不須要填寫email,只須要填寫帳號和密碼,對這兩個字段進行驗證。

但是個人實體類裏面對email作了[Required]和[RegularExpression]驗證,那麼這樣就致使了 若是我登陸方法繼續使用這個user實體類,那麼確定會報錯,會返回個400驗證碼。

這種狀況我該怎麼解決?難道從新建個model?再從新給一遍規則?這還僅僅只有3個字段,萬一有的表中有十幾個字段,二十幾個字段甚至更多怎麼辦?

從新建個model確定不行,這樣已經失去了   複用性、各司其職  的初衷。

求解決方案!在線等!

...

模型驗證進階:自由控制須要驗證的字段

百度了一下,網上沒有該方面的教程,博客園中也沒找到,羣裏也沒交流出個結果,但這種狀況卻常常遇到!

 梳理下思路,大體有幾種,第一種是用某種手段控制類中的這些驗證特性,或者控制類中的屬性字段,如啓用或停用,可是c#不能對屬性字段進行停啓用,而控制類中的這些驗證特性也有點天方夜譚,自己就是微軟封裝好的,你得反編譯一下看下源碼,而後重構?或者你直接不用這些框架封裝好的驗證特性,使用本身定義自定義驗證特性,而後把控制方法都寫在裏面?這樣太麻煩,並且違背初衷。自定義ModelBinder ?更扯淡。

一番折騰無果,那麼就不能從特性自己找突破口了,這時,我把目標轉移到ModelState.IsValid上,換一種思路實現。

咱們發現其實現了GetEnumerator方法,因而對其進行遍歷,能夠獲取到特性所綁定的字段屬性的名稱以及其狀態。

由於要實現自由控制須要驗證的字段,因此不管怎樣實現,都只能經過 自定義特性 標註在方法體頭上來實現。

而理想的最終呈現效果應該是這樣的:

放圖:

 

或者

 

 

使用方式:

若是方法頭上有KeepZ特性的話,就進入自由控制驗證字段狀態。

 

[KeepZ("字段1","字段2")]  即:只對  字段1 和 字段2   進行驗證

[KeepZ(false,"字段3")]  即:除了  字段3  以外,其他字段都進行驗證

 

 

那麼咱們放下具體實現代碼:

 

 public override void OnActionExecuting(HttpActionContext actionContext)
        {
            if ((actionContext.ActionDescriptor.ActionName.ToUpper().Contains("INSERT") || actionContext.ActionDescriptor.ActionName.ToUpper().Contains("CHECK") ||
                 actionContext.ActionDescriptor.ActionName.ToUpper().Contains("UPDATE")))
            {
                var ia = actionContext.ActionDescriptor.GetCustomAttributes<KeepZ>();
                if (ia.Count != 1)
                {
                    goto result;
                }
                foreach (KeyValuePair<string, ModelState> item in actionContext.ModelState.ToArray())
                {
                    if (ia[0].Modes == false)
                    {
                        foreach (string PropertysValue in ia[0].Propertys)
                        {
                            if (item.Key.Contains(PropertysValue))
                            {
                                actionContext.ModelState.Remove(item.Key);
                            }
                        }
                    }
                    else
                    {
                        bool re = false;
                        foreach (string PropertysValue in ia[0].Propertys)
                        {
                            if (item.Key.Contains(PropertysValue))
                            {
                                re = true;
                            }
                        }
                        if (re == false)
                        {
                            actionContext.ModelState.Remove(item.Key);
                        }
                    }
                }
                result: if (!actionContext.ModelState.IsValid)
                {
                  
                    actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
                   
                      
                }
            }
        }
/// <summary>
    ///  保持
    /// </summary>
    public class KeepZ : Attribute
    {
        public string[] Propertys = null;
        public bool Modes = true;
        public KeepZ(params string[] Property)
        {
            Propertys = Property;
        }
        public KeepZ(bool Mode, params string[] Property)
        {
            Propertys = Property;
            Modes = Mode;
        }
    }

如此一來,就不用再重建Model這樣費時費力的方法了,如今MVC架構大多都用這種驗證模式,可是卻沒有  自由選擇驗證字段的解決方案,往往遇到該狀況,只能無奈從新建個實體類,對比之下,根本沒有食得這種攔截層模型驗證的精髓,只學個模子,反而弄巧成拙不成本意,因此我寫了此篇和你們一塊兒分享,加入了KeepZ來控制須要驗證的字段,就是真正的實現了  可 複用  ,邏輯與攔截分層  了。

Demo雖小,可是這種狀況下的解決方案,我在博客園中沒找到,應該是園子裏第一篇吧。

注意,BindAttribute 這個特性,是 針對賦值上的處理, 選擇賦值 和 選擇驗證 是 兩個 看似相同卻大相徑庭的兩個分支,本文是 自由控制驗證攔截 ,所適用性在某些場景要比 BindAttribute 要多,固然,不介意的話, 你也能夠 將 BindAttribute  帶入本實例中,也是能夠的, 剔除驗證的時候 剔除賦值。

 

做者:小曾
出處:http://www.cnblogs.com/1996V/p/7423834.html 歡迎轉載,但任何轉載必須保留完整文章,在顯要地方顯示署名以及原文連接。如您有任何疑問或者受權方面的協商,請給我留言
.Net交流羣, QQ羣:166843154 慾望與掙扎 
相關文章
相關標籤/搜索