上篇文章中分析了Custom Binder的弊端: 由於Custom Binder是和具體的類型相關,比如指定類型A由我們的Custom Binder解析,那么導致系統運行中的所有Action的訪問參數,只要是類型A, 都會使用Custom Binder. 這篇文章將會介紹Binder Attribute方式擴展MVC的Model Binder, 這種方式更加的靈活和可操控。
本篇文章參考了ModelBinder——ASP.NET MVC Model綁定的核心, 結合Artech的這篇文章,對於MVC的Model Binder一定能夠理解的更加深刻。
閱讀目錄:
一. Asp.net MVC Model Binder的源碼分析
二. Model Binder的優先順序以及擴展點
三. 自定義Binder Attribute - SessionUserBinderAttribute
四. 應用SessionUserBinderAttribute
五. 總結
一,Asp.net MVC Model Binder的源碼分析
在實際使用Binder Attribute之前,我們先來看看Model Binder在MVC中是如何工作的。
1.1 首先, 在MVC中, 當一個請求發送到服務器,先是要經過Route匹配, 找到對應的Controller和Action, 然后才是構建Action中的參數,也就是Model Binder的過程。這個可以從MVC的源碼, ControllerActionInvoker中看出來。
在ControllerActionInvoker.cs的函數GetParameterValue函數中,調用了GetModelBinder方法來獲取構建該Action參數的Binder.
1.2 GetModelBinder方法,將會優先使用ParameterDescriptor描述中指定的Binder。這是本篇文章介紹的Model Binder擴展點 -- 為Action參數指定Binder.
1.3 在ParameterDescriptor沒有指定Binder的情況下,按照這個優先順序獲取Binder
a. 從BinderProvoder中獲取
b. 從global table中獲取. 第一篇中的Custom Model Binder就是這里的擴展,定義一個Model Binder注冊到global table中
c. 從參數類型Attribute上指定的Binder獲取。參數類型定義類的定義處,也可以使用Binder Attribute,指定該類的Action參數由指定的Binder處理。
d. 使用MVC默認的Default Binder
二,Model Binder的優先順序以及擴展點
從上面MVC中Model Binder的源代碼分析中,可以知道,參數如何構建取決於Binder,而獲取Binder的源的優先順序是:
1. PrameterDescriptor
2. Binder Provider
3. Global table
4. 參數類型定義的Binder Attribute
5. MVC的DefaultBinder
所以Model Binder的擴展點也是上面5處, 第一篇文章中介紹的是擴展點3, 這篇中介紹的是擴展點1, 下篇文章中,將會介紹擴展點5,也就是DefaultBinder.
三,自定義Binder Attribute - SessionUserBinderAttribute
還是以解決上篇文章中的Session依賴問題為入手。我們定義SessionUserBinderAttribute 繼承自CustomModelBinderAttribute,重寫方法GetBinder(),該Binder將從Session中獲取值來構建Action參數。
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] public class SessionUserBinderAttribute : CustomModelBinderAttribute { public override IModelBinder GetBinder() { return new UserAccountModelBinder(); } } public class UserAccountModelBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { if (controllerContext.HttpContext.Session["UserAccuont"] != null) { return controllerContext.HttpContext.Session["UserAccuont"]; } return null; } }
四,應用SessionUserBinderAttribute
使用我們的SessionUserBinderAttribute改造Index方法
原有的Index方法是這樣的:
public ActionResult Index() { var user = Session["UserAccuont"] as UserAccount; //從Session中獲取當前登錄用戶的信息 //send email var email = user.Email; return new EmptyResult(); }
改造后的函數如下, 使用SessionUserBinder指定user參數的值,是從session中獲取。
public ActionResult Index([SessionUserBinder]UserAccount user) { //send email var email = user.Email; return new EmptyResult(); }
同時, 如果在新增一個UserAccount表單提交的時候,由於沒有使用SessionUserBinderAttribute, 該useraccount將會由MVC中的DefaultBinder處理,也就是從form中提取值。
[HttpPost] public ActionResult Create(UserAccount useraccount) { return View(); }
五,總結
這篇文章中,介紹了MVC中的model binder流程,以及羅列了model binder中5個的擴展點:
1. PrameterDescriptor
2. Binder Provider
3. Global table
4. 參數類型定義的Binder Attribute
5. MVC的DefaultBinder
其中這篇文章涉及的是擴展點1.
擴展點2 Artech的文章ModelBinder——ASP.NET MVC Model綁定的核心中有介紹。
擴展點3在文章Asp.net MVC的Model Binder工作流程以及擴展方法(1) - Custom Model Binder中
擴展點4也是利用Binder Attribute,不同的地方是,它關聯的不是參數,而是類型定義。作用和擴展點3范圍一樣,不在寫文章展開。
擴展點5是MVC中的DefaultBinder, 下篇文章中,將會詳細介紹。
Custom Model Binder是以類型指定binder, 我們比喻為全火力覆蓋,這里介紹的CustomModelBinderAttribute可以達到定點清除的效果,也就是只有我指定的參數才由特定的binder處理。理解兩者應用范圍的不同,有助於我們選擇使用哪種方式擴展。