MVC源碼分析 - ModelBinder綁定 / 自訂資料繫結

來源:互聯網
上載者:User

標籤:logs   nbsp   model   這一   控制   ams   except   lin   今天   

這幾天老感覺不對, 總覺得少點什麼, 今天才發現, 前面 3 裡面, 在擷取Action參數資訊的時候,  少解析了. 裡面還有一個比較重要的東西. 今天看也是一樣的.

在 InvokeAction() 方法裡面, 有一句代碼:

IDictionary<string, object> parameterValues = this.GetParameterValues(controllerContext, actionDescriptor);

這個是用來擷取參數的. 那麼參數是不是隨便擷取呢? 在Mvc 裡面, 頁面向Action 傳參的時候, 有沒有嘗試過傳一個數組, 然後接收的時候, 也直接解析成數組呢? 或者接收更複雜的類型呢?

答案都在這一篇裡面了. 先來看源碼.

protected virtual IDictionary<string, object> GetParameterValues(ControllerContext controllerContext,
   ActionDescriptor actionDescriptor){ Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); foreach (ParameterDescriptor descriptor in actionDescriptor.GetParameters()) { dictionary[descriptor.ParameterName] = this.GetParameterValue(controllerContext, descriptor); } return dictionary;}

一、源碼解析

1. actionDescriptor.GetParameters()

//System.Web.Mvc.ReflectedActionDescriptorpublic override ParameterDescriptor[] GetParameters(){    return ActionDescriptorHelper.GetParameters(this, this.MethodInfo, ref this._parametersCache);}

這裡應該是擷取所有的參數和其描述資訊.

 

2. this.GetParameterValue() -- 主要方法

protected virtual object GetParameterValue(ControllerContext controllerContext,
   ParameterDescriptor parameterDescriptor){ Type parameterType = parameterDescriptor.ParameterType;
   //根據參數描述來擷取參數的處理介面 IModelBinder modelBinder = this.GetModelBinder(parameterDescriptor); IValueProvider valueProvider = controllerContext.Controller.ValueProvider; string str = parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName;
   //擷取參數上面的過濾器, 並在下面放入到參數解析上下文中(ModelBindingContext) Predicate<string> propertyFilter = GetPropertyFilter(parameterDescriptor); ModelBindingContext bindingContext = new ModelBindingContext { FallbackToEmptyPrefix = parameterDescriptor.BindingInfo.Prefix == null, ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, parameterType), ModelName = str, ModelState = controllerContext.Controller.ViewData.ModelState, PropertyFilter = propertyFilter, ValueProvider = valueProvider };
   //執行參數的處理常式 return (modelBinder.BindModel(controllerContext, bindingContext) ?? parameterDescriptor.DefaultValue);}

 

2.1 GetModelBinder()方法

private IModelBinder GetModelBinder(ParameterDescriptor parameterDescriptor){    return (parameterDescriptor.BindingInfo.Binder ?? 
      this.Binders.GetBinder(parameterDescriptor.ParameterType));}

這裡是根據參數描述來擷取參數的處理介面

public interface IModelBinder{    // Methods    object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext);}

 

2.2 BindModel()方法 - 這裡看的是 DefaultModelBinder, 後面會自訂一個

public virtual object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext){    EnsureStackHelper.EnsureStack();    if (bindingContext == null)    {        throw new ArgumentNullException("bindingContext");    }    bool flag = false;    if (!string.IsNullOrEmpty(bindingContext.ModelName) 
    && !bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)) { if (!bindingContext.FallbackToEmptyPrefix) { return null; } ModelBindingContext context = new ModelBindingContext { ModelMetadata = bindingContext.ModelMetadata, ModelState = bindingContext.ModelState, PropertyFilter = bindingContext.PropertyFilter, ValueProvider = bindingContext.ValueProvider }; bindingContext = context; flag = true; } if (!flag) { bool flag2 = ShouldPerformRequestValidation(controllerContext, bindingContext); bool skipValidation = !flag2; ValueProviderResult valueProviderResult = bindingContext.UnvalidatedValueProvider
        .GetValue(bindingContext.ModelName, skipValidation); if (valueProviderResult != null) {
       //為簡單對象返回參數值 return this.BindSimpleModel(controllerContext, bindingContext, valueProviderResult); } } if (!bindingContext.ModelMetadata.IsComplexType) { return null; } return this.BindComplexModel(controllerContext, bindingContext);}

這裡面的內容有點多, 也有點複雜, 其實解析到這裡, 差不多已經得到我想要的東西了.

 

二、自訂ModelBinder

1. IModelBinder -- 對於相對比較簡單的類型, 可以使用這種方式

首先建立一個稍微複雜一點的類.

public enum GenderEnum{     Female,    Male,    Unknow}public class ExtInfo{    public string QQ { get; set; }    public string Phone { get; set; }}public class User{
   //這裡的建構函式, 我建立了一個ExtInfo執行個體, 否則, 一會這個ExtInfo對象就是空的, 影響我的示範 public User() { Extension = new ExtInfo(); }
public int Id { get; set; } public string Name { get; set; } public GenderEnum Gender { get; set; } public ExtInfo Extension { get; set; }}

接下來, 看一下控制器代碼

public class ModelController : Controller{    public ActionResult Get(User user)    {        return View(user);    }    public ActionResult Index([ModelBinder(typeof(ModelIndexBinder))]User user)    {        return View(user);    }}

控制器中需要注意的部分, 我已經標紅了. 接下來看我自訂的ModelBinder

public class ModelIndexBinder : IModelBinder{    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)    {        var request = controllerContext.HttpContext.Request;        User user = new User        {            Id = Convert.ToInt32(request.Params["Id"]),            Name = request.Params["Name"].ToString(),            Gender = (GenderEnum)Convert.ToInt32(request.Params["Gender"]),            Extension = new ExtInfo            {                QQ = request.Params["QQ"].ToString(),                Phone = request.Params["Phone"].ToString()            }        };        return user;    }}

先看兩個方法執行的效果吧.

Index Get

 

 

 

注 : 這裡是可以有別的方式來達到目的的, 只需要修改一下url即可:

http://localhost:11620/model/Get?id=1&name=haha&gender=0&Extension.qq=123123123&Extension.phone=12312341234

此處是為了舉一個例子, 才這麼弄的.

對於比較簡單的, 用IModelBinder挺方便的, 但是對於稍微複雜一點的, 可以實現 DefaultModelBinder 來實現.

不過這部分實現就不解析了, 我暫時沒用過. 

一般對於Url Get請求, 不會很複雜, 只有在使用Post請求的時候, 會遇到比較複雜的處理方式. 但是對於這種複雜的處理, 還有一種比較常用的方式, 就是採用還原序列化的方式, 我常用的是 Newtonsoft.

 

參考:

  MVC擴充

  深入Asp.net Mvc

 

 目錄已同步

MVC源碼分析 - ModelBinder綁定 / 自訂資料繫結

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.