asp.net mvc源码分析-Action篇 IModelBinder
-
我们首先还是看看ReflectedParameterBindingInfo的Binder属性吧:
public override IModelBinder Binder {
get {
IModelBinder binder = ModelBinders.GetBinderFromAttributes(_parameterInfo,
() => String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedParameterBindingInfo_MultipleConverterAttributes,
_parameterInfo.Name, _parameterInfo.Member));return binder;
}
}在ModelBinders中有一个属性public static ModelBinderDictionary Binders,这个binders内容如下
ModelBinderDictionary binders =new ModelBinderDictionary(){
{ typeof(HttpPostedFileBase), new HttpPostedFileBaseModelBinder()},
{ typeof(byte[]), new ByteArrayModelBinder()},
{ typeof(Binary), new LinqBinaryModelBinder()}
};
说明默认的情况下就提供者3个Binder,但是这个属性是共有静态的,所以我们可以往里面添加自己的binder类,和前面文章讲的Filiter以及后面要讲的ValueProvider一样,如在 Application_Start()方法中 ModelBinders.Binders.Add(xxx,xxxx)很不是很方便扩展了。
ModelBinders的GetBinderFromAttributes这个方法一看我们就能猜到它的逻辑了,
CustomModelBinderAttribute[] attrs = (CustomModelBinderAttribute[])element.GetCustomAttributes(typeof(CustomModelBinderAttribute), true /* inherit */);
获取 当前参数的CustomModelBinderAttribute特性,如果有该特性就调用第一个特性的GetBinder()方法并返回其值,没有特性则返回null,如果有多个特性则抛出异常,说明一个参数上是不可以有多个CustomModelBinderAttribute特性的,正样 ReflectedParameterBindingInfo的binder属性就设置好了。
下面 该轮到ControllerActionInvoker的Binders属性,
protected internal ModelBinderDictionary Binders {
get {
if (_binders == null) {
_binders = ModelBinders.Binders;
}
return _binders;
}
set {
_binders = value;
}
}可以 看到默认他返回的是ModelBinders.Binders。
接下来看看 IModelBinder binder = GetModelBinder(parameterDescriptor)这句究竟怎么返回的binder,
return parameterDescriptor.BindingInfo.Binder ?? Binders.GetBinder(parameterDescriptor.ParameterType);
太简单了 ,首先看看参数是否有binder特性,如果有就返回相应的binder,否者根据参数类型获取对应的binder。
其 方法如下:
[csharp]
public IModelBinder GetBinder(Type modelType) {
return GetBinder(modelType, true /* fallbackToDefault */);
}
public virtual IModelBinder GetBinder(Type modelType, bool fallbackToDefault) {
if (modelType == null) {
throw new ArgumentNullException("modelType");
}
return GetBinder(modelType, (fallbackToDefault) ? DefaultBinder : null);
}
private IModelBinder GetBinder(Type modelType, IModelBinder fallbackBinder) {
// Try to look up a binder for this type. We use this order of precedence:
// 1. Binder returned from provider
// 2. Binder registered in the global table
// 3. Binder attribute defined on the type
// 4. Supplied fallback binder
IModelBinder binder = _modelBinderProviders.GetBinder(modelType);
if (binder != null) {
return binder;
}
if (_innerDictionary.TryGetValue(modelType, out binder)) {
return binder;
}
binder = ModelBinders.GetBinderFromAttributes(modelType,
() => String.Format(CultureInfo.CurrentCulture, MvcResources.ModelBinderDictionary_MultipleAttributes, modelType.FullName));
return binder ?? fallbackBinder;
}public IModelBinder GetBinder(Type modelType) {
return GetBinder(modelType, true /* fallbackToDefault */);
}public virtual IModelBinder GetBinder(Type modelType, bool fallbackToDefault) {
if (modelType == null) {
throw new ArgumentNullException("modelType");
}return GetBinder(modelType, (fallbackToDefault) ? DefaultBinder : null);
}private IModelBinder GetBinder(Type modelType, IModelBinder fallbackBinder) {
// Try to look up a binder for this type. We use this order of precedence:
// 1. Binder returned from provider
// 2. Binder registered in the global table
// 3. Binder attribute defined on the type
// 4. Supplied fallback binderIModelBinder binder = _modelBinderProviders.GetBinder(modelType);
if (binder != null) {
return binder;
}if (_innerDictionary.TryGetValue(modelType, out binder)) {
return binder;
}binder = ModelBinders.GetBinderFromAttributes(modelType,
() => String.Format(CultureInfo.CurrentCulture, MvcResources.ModelBinderDictionary_MultipleAttributes, modelType.FullName));return binder ?? fallbackBinder;
}
这里需要注意binder选着的优先顺序,(1)从_modelBinderProviders里面找相应的binderprivate ModelBinderProviderCollection _modelBinderProviders;
public ModelBinderDictionary()
: this(ModelBinderProviders.BinderProviders) {
}
internal ModelBinderDictionary(ModelBinderProviderCollection modelBinderProviders) {
_modelBinderProviders = modelBinderProviders;
}public static class ModelBinderProviders {
private readonly static ModelBinderProviderCollection _binderProviders = new ModelBinderProviderCollection {
};
public static ModelBinderProviderCollection BinderProviders {
get {
return _binderProviders;
}
}
}从这些代码我们可以得知 默认情况下_modelBinderProviders里面是没有数据 ,那么什么时候这个集合有数据了,当我们在Application_Start()中调用ModelBinderProviders.BinderProviders.Add(xxx)就有数据了,
(2)从_innerDictionary中取数据,这个数据时什么时候添加上去的了,看了ModelBinderDictionary的add放就明白了
public void Add(Type key, IModelBinder value) {
_innerDictionary.Add(key, value);
}其实 ModelBinderDictionary内部很多方法都是围绕着_innerDictionary集合操作的。
(3)从参数数据类型上获取binder
(4)返货默认的DefaultBinder,该属性默认= new DefaultModelBinder()
现在 我们可以总结一下binder的优先顺序(1)参数上的CustomModelBinderAttribute特性;(2)ModelBinderProviders.BinderProviders.Add(xxx)注册的IModelBinderProvider;(3)ModelBinders的Binders;(4)参数类型上的CustomModelBinderAttribute特性;(5)返回默认的DefaultModelBinder
IValueProvider valueProvider = controllerContext.Controller.ValueProvider; 这句的讲解我们放到后面的文章中吧,
string parameterName = parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName;这句获取参数名称,
Predicate<string> propertyFilter = GetPropertyFilter(parameterDescriptor);这个就只控制该参数时候需要绑定对应的值。
private static Predicate<string> GetPropertyFilter(ParameterDescriptor parameterDescriptor) {
ParameterBindingInfo bindingInfo = parameterDescriptor.BindingInfo;
return propertyName => BindAttribute.IsPropertyAllowed(propertyName, bindingInfo.Include.ToArray(), bindingInfo.Exclude.ToArray());
}BindAttribute.IsPropertyAllowed如下:
internal static bool IsPropertyAllowed(string propertyName, string[] includeProperties, string[] excludeProperties) {
// We allow a property to be bound if its both in the include list AND not in the exclude list.
// An empty include list implies all properties are allowed.
// An empty exclude list implies no properties are disallowed.
bool includeProperty = (includeProperties == null) || (includeProperties.Length == 0) || includeProperties.Contains(propertyName, StringComparer.OrdinalIgnoreCase);
bool excludeProperty = (excludeProperties != null) && excludeProperties.Contains(propertyName, StringComparer.OrdinalIgnoreCase);
return includeProperty && !excludeProperty;
}现在 终于看到了BindAttribute在申明地方用到了。
现在 我们来做一个自定的ModelBinder类怎么做。
[csharp]
public class UserInfo
{
public string Name { set; get; }
public string Age { set; get; }
}
public class UserInfoModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
object obj = Activator.CreateInstance(bindingContext.ModelType);
foreach (PropertyInfo p in bindingContext.ModelType.GetProperties())
{
ValueProviderResult vpResult= bindingContext.ValueProvider.GetValue(p.Name);
if (vpResult != null)
{
object value = vpResult.ConvertTo(p.PropertyType);
p.SetValue(obj, value, null);
}
}
return obj;
}
}
public class HomeController : Controller
{
public ActionResult Index([ModelBinder(typeof(UserInfoModelBinder))]UserInfo userInfo)
{
return Content("Name:" + userInfo.Name + " Age:" + userInfo.Age);
// return View();
}
}public class UserInfo
{
public string Name { set; get; }
public string Age { set; get; }
}
public class UserInfoModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
object obj = Activator.CreateInstance(bindingContext.ModelType);
foreach (PropertyInfo p in bindingContext.ModelType.GetProperties())
{
ValueProviderResult vpResult= bindingContext.ValueProvider.GetValue(p.Name);
if (vpResult != null)
{
object value = vpResult.ConvertTo(p.PropertyType);
p.SetValue(obj, value, null);
}
}
return obj;
}
}
public class HomeController : Controller
{public ActionResult Index([ModelBinder(typeof(UserInfoModelBinder))]UserInfo userInfo)
{
return Content("Name:" + userInfo.Name + " Age:" + userInfo.Age);
// return View();}
}运行结果如图:
asp.net mvc源码分析-Action篇 IModelBinder的更多相关文章
- ASP.NET MVC 源码分析(一)
ASP.NET MVC 源码分析(一) 直接上图: 我们先来看Core的设计: 从项目结构来看,asp.net.mvc.core有以下目录: ActionConstraints:action限制相关 ...
- ASP.NET MVC源码分析
MVC4 源码分析(Visual studio 2012/2013) HttpModule中重要的UrlRoutingModule 9:this.OnApplicationPostResolveReq ...
- asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证
原文:asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证 在前面的文章中我们曾经涉及到ControllerActionInvoker类GetPara ...
- asp.net mvc源码分析-ModelValidatorProviders 客户端的验证
几年写过asp.net mvc源码分析-ModelValidatorProviders 当时主要是考虑mvc的流程对,客户端的验证也只是简单的提及了一下,现在我们来仔细看一下客户端的验证. 如图所示, ...
- MVC源码分析 - Action/Result 过滤器执行时机
前面 的篇章, 解析了Action方法的查找, 以及 Authorize, Action, Result, Error 过滤器的加载时机. 也花了两篇去看授权和错误过滤器的使用. 但是对于 Actio ...
- MVC源码分析 - Action查找和过滤器的执行时机
接着上一篇, 在创建好Controller之后, 有一个 this.ExecuteCore()方法, 这部分是执行的. 那么里面具体做了些什么呢? //ControllerBaseprotected ...
- asp.net MVC 源码分析
先上一张图吧 asp.net请求机制的图 by传智播客邹华栋老师 然后是 邹老师添加MVC请求过程的图 其实MVC 是在.netframework上加了一个过滤器 HttpModule 在C:\W ...
- asp.net mvc源码分析-Route的GetRouteData
我知道Route这里东西应该算路由,这里把它放到mvc里面有些不怎么合适,但是我想大家多数遇到路由都是在mvc的时候吧.首先我们还是来看看GetRouteData方法吧 [csharp] public ...
- MVC源码分析 - Action/Result 过滤器(续)
上一篇 看到了Action/Result过滤器的执行顺序: OnActionExecuting -> Action -> OnActionExecuted -> OnResultEx ...
随机推荐
- Index & Statistics ->> Rebuild Index会不会覆盖原先Index的WITH选项设置
昨天因为工作中遇到要对某个数据库的表通通启用data_compression,突然有个念头,就是如果我当初用"ALTER INDEX XXX ON YYY REBUILD WITH (DAT ...
- INDIGO STUDIO神器!快速创建WEB、移动应用的交互原型工具【转】
转自:http://www.uisdc.com/indigo-studio-wireframe-interactive-uis 这套最新的设计工具出自Indigo工作室,永久免费,有mac版本和WIN ...
- java中String类学习
java中String类的相关操作如下: (1)初始化:例如,String s = “abc”; (2)length:返回字符串的长度. (3)charAT:字符操作,按照索引值获得字符串中的指定字符 ...
- Java NIO读书笔记2
一.选择器(Selector) Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件.这样,一个单独的线程可以管理多个channel ...
- 一个发光的搜索边框(纯CSS3)
这是效果图,边框会不停的闪,兼容各种浏览器 HTML代码: <body> <div class="container"> <form method=& ...
- jsp之jstl标签
常用jstl标签 一.<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> ...
- BZOJ 4597 随机序列
一定要想到,对于一个空位如果填了+,那么一定有一个表达式这里填-号使得后面的全部抵消掉.这点十分重要. 于是发现这个答案只和前缀积有关,线段树维护即可. #include<iostream> ...
- NBUT 1122 Shameimaru's Candid Camera(水)
题意: 给n*m个格子,初始时每个格子中有个数值为0,部分格子中含有炸弹,每个炸弹爆炸可以将周围的8个非炸弹格子中的数值加1,求全部炸弹炸完后那些非0且非炸弹格子中的数是多少. 思路: 另开一个矩阵, ...
- HDU 1496 Train Problem I 火车问题1(桟,水)
题意: 给出两个串,串中的数字i 代表编号为i的火车进入车站的顺序,车站如桟一样,先进后出.第二个串是火车出站的顺序,问若按照第一个串那样进站,是否有可能如第二个串一样的出站顺序?火车顶多9辆,即1- ...
- zipline
history 多只股票时会返回某几只股票停牌没数据 if not symbol(stock) in data: 聚宽 多只股票如果某几只没有发行 600485: nan 多只股票如果某几只停牌 60 ...