MVC中Action参数绑定的过程

一、题外话

上一篇:MVC中Action的执行过程

ControllerContext

封装有了与指定的 RouteBase ControllerBase 实例匹配的 HTTP 请求的信息。

二、Model绑定者

2.1相关说明

http请求中的参数绑定到Model,是由实现了IModelBinder的类来完成的。我们称这样的类叫做Model绑定者

using System;
namespace System.Web.Mvc
{
    /// <summary>Defines the methods that are required for a model binder.</summary>
    public interface IModelBinder
    {
        /// <summary>Binds the model to a value by using the specified controller context and binding context.</summary>
        /// <returns>The bound value.</returns>
        /// <param name="controllerContext">The controller context.</param>
        /// <param name="bindingContext">The binding context.</param>
        object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext);
    }
}

2.2Model绑定者实现绑定的途径

1)直接在参数上绑定

using System;
using System.Web;
using System.Web.Mvc;

namespace MVC_ST_2.Controllers
{
    
    public class Person
    {
        
        public string Name { get; set; }

        public int Age { get; set; }

    }
    
    public class PersonModelBinder : IModelBinder
    {

        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            HttpRequestBase request = controllerContext.HttpContext.Request;
            var p = new Person();
            p.Name = request["Name"];
            p.Age =int.Parse( request["Age"]);
            return p;
        }


    }
    
    public class HomeController : Controller
    {
        //通过参数标记
        public ActionResult Index([ModelBinder(typeof(PersonModelBinder))] Person p)
        {
            return View();
        }
        
        public ActionResult Index2(Person p)
        {
            return View();
        }
    }
}

2)在model上加标记

    [ModelBinder(typeof(PersonModelBinder))] 
    public class Person
    {
        
        public string Name { get; set; }

        public int Age { get; set; }

    }

3)ModelBinders.Binders中注册

protected void Application_Start()

{
    ModelBinders.Binders[typeof(Person)] = new PersonModelBinder();
}

 

2.3 Action中是如何调用绑定者的?

以下是

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

说明:通过参数标记的方式优先,如果没有则使用this.Binders.GetBinder(parameterDescriptor.ParameterType);

this.Binders的定义

protected internal ModelBinderDictionary Binders
{
    get
    {
        if (this._binders == null)
        {
            this._binders = ModelBinders.Binders;
        }
        return this._binders;
    }
    set
    {
        this._binders = value;
    }
}

从上图可以看出,最终的绑定操作交给了ModelBinderDictionary(注意了,下面会接着讲)

正好我们回到前两章讲的ControllerActionInvoker,其中的参数绑定赋值过程GetParameterValues如何获取的过程即绑定的过程

 

 

protected virtual object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor)
{
    Type parameterType = parameterDescriptor.ParameterType;
    IModelBinder modelBinder = this.GetModelBinder(parameterDescriptor);
    IValueProvider valueProvider = controllerContext.Controller.ValueProvider;
    string modelName = parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName;
    Predicate<string> propertyFilter = ControllerActionInvoker.GetPropertyFilter(parameterDescriptor);
    ModelBindingContext bindingContext = new ModelBindingContext
    {
        FallbackToEmptyPrefix = parameterDescriptor.BindingInfo.Prefix == null,
        ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, parameterType),
        ModelName = modelName,
        ModelState = controllerContext.Controller.ViewData.ModelState,
        PropertyFilter = propertyFilter,
        ValueProvider = valueProvider
    };
    object obj = modelBinder.BindModel(controllerContext, bindingContext);
    return obj ?? parameterDescriptor.DefaultValue;
}
private IModelBinder GetModelBinder(ParameterDescriptor parameterDescriptor)
{
    return parameterDescriptor.BindingInfo.Binder ?? this.Binders.GetBinder(parameterDescriptor.ParameterType);
}
 this.Binders 其类型正好是ModelBinderDictionary(上面提到过),他的方法如下
    public IModelBinder GetBinder(Type modelType)
        {
            return this.GetBinder(modelType, true);
        }
        /// <summary>Retrieves the model binder for the specified type or retrieves the default model binder.</summary>
        /// <returns>The model binder.</returns>
        /// <param name="modelType">The type of the model to retrieve.</param>
        /// <param name="fallbackToDefault">true to retrieve the default model binder.</param>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="modelType" /> parameter is null.</exception>
        public virtual IModelBinder GetBinder(Type modelType, bool fallbackToDefault)
        {
            if (modelType == null)
            {
                throw new ArgumentNullException("modelType");
            }
            return this.GetBinder(modelType, fallbackToDefault ? this.DefaultBinder : null);
       //this.DefaultBinder 是下一章我们需要讲的内容。也是整个MVC核心的默认的绑定者
}
private IModelBinder GetBinder(Type modelType, IModelBinder fallbackBinder) { IModelBinder modelBinder = this._modelBinderProviders.GetBinder(modelType); if (modelBinder != null) { return modelBinder; } if (this._innerDictionary.TryGetValue(modelType, out modelBinder)) { return modelBinder; } modelBinder = ModelBinders.GetBinderFromAttributes(modelType, () => string.Format(CultureInfo.CurrentCulture, MvcResources.ModelBinderDictionary_MultipleAttributes, new object[] { modelType.FullName })); return modelBinder ?? fallbackBinder;//DefaultBinder 很多情况下,前面的几个if都不会执行,所以使用系统默认的绑定者 }

 三、下一章接着介绍DefaultModelBinder

posted @ 2014-06-24 13:23  [秦时明月]  阅读(3685)  评论(3编辑  收藏  举报