public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo) { return String.Equals(actionName, Name, StringComparison.OrdinalIgnoreCase); }
ASP.NETMVC 定義了以下7 個基於相應HTTP 方法(GET 、POST 、PUT 、DELETE 、Head 、Options 和Patch) 的ActionMethodSelectorAttribute 類型.當將它們應用到某個Action 方法上時,只有在當前請求的HTTP 方法與之相匹配的狀況下目標Action 方法纔會被選擇。他們的內部也是用AcceptVerbsAttribute 實現的. html
public sealed class HttpGetAttribute : ActionMethodSelectorAttribute { private static readonly AcceptVerbsAttribute _innerAttribute = new AcceptVerbsAttribute(HttpVerbs.Get); public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { return _innerAttribute.IsValidForRequest(controllerContext, methodInfo); } }
[Flags] //指示能夠將枚舉做爲位域(即一組標誌)處理。 public enum HttpVerbs { Get = 1 << 0,//移位 Post = 1 << 1, Put = 1 << 2, Delete = 1 << 3, Head = 1 << 4, Patch = 1 << 5, Options = 1 << 6, }
public class AcceptVerbsAttribute : ActionMethodSelectorAttribute { public ICollection<string> Verbs { get; private set; }//支持的請求類型 public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } string incomingVerb = controllerContext.HttpContext.Request.GetHttpMethodOverride(); return Verbs.Contains(incomingVerb, StringComparer.OrdinalIgnoreCase); } }//過濾的方法體
private ActionDescriptorCreator GetActionDescriptorDelegate(MethodInfo entryMethod) { // Does the action return a Task? if (entryMethod.ReturnType != null && typeof(Task).IsAssignableFrom(entryMethod.ReturnType)) { return (actionName, controllerDescriptor) => new TaskAsyncActionDescriptor(entryMethod, actionName, controllerDescriptor); } // Is this the FooAsync() / FooCompleted() pattern? if (IsAsyncSuffixedMethod(entryMethod)) { string completionMethodName = entryMethod.Name.Substring(0, entryMethod.Name.Length - "Async".Length) + "Completed"; MethodInfo completionMethod = GetMethodByName(completionMethodName); if (completionMethod != null) { return (actionName, controllerDescriptor) => new ReflectedAsyncActionDescriptor(entryMethod, completionMethod, actionName, controllerDescriptor); } else { throw Error.AsyncActionMethodSelector_CouldNotFindMethod(completionMethodName, ControllerType); } } // Fallback to synchronous method return (actionName, controllerDescriptor) => new ReflectedActionDescriptor(entryMethod, actionName, controllerDescriptor); }
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; }
Controller 使用的ValueProvider 能夠經過定義在ContollerBase 中的ValueProvider 屬性進行獲取和設置。數組
public interface IValueProvider { bool ContainsPrefix(string prefix); ValueProviderResult GetValue(string key); }
ValueProviderResult RawValue表示提供的原始數據,AttemptedValue表示數據值的字符串表示。ConvertTo進行數據轉換異步
public virtual string Get(int index) { ArrayList list = (ArrayList)base.BaseGet(index); return GetAsOneString(list); } private static string GetAsOneString(ArrayList list) { int num = (list != null) ? list.Count : 0; if (num == 1) { return (string)list[0]; } if (num <= 1) { return null; } StringBuilder builder = new StringBuilder((string)list[0]); for (int i = 1; i < num; i++) { builder.Append(','); builder.Append((string)list[i]); } return builder.ToString(); }
經過NameValueCollection ValueProvider 提供的數據源將保存在一個NameValueCollection對象中, DictionaryValueProvider 天然將數據源存放在一個真正的字典對象之中。它們之間的不一樣之處在於NameValueCollection 中的元素僅限於字符串,而且不對Key 做惟一性約束(每一個Key對應的是一個存儲值的列表):字典中的Key 則是惟一的, Value 也不只僅侷限於字符串。 public DictionaryValueProvider(IDictionary<string, TValue> dictionary, CultureInfo culture)ide
public RouteDataValueProvider(ControllerContext controllerContext) : base(controllerContext.RouteData.Values, CultureInfo.InvariantCulture) { }
this.Request.Files類型是HttpFileCollectionBase,元素類型是HttpPostedFileBase函數
當咱們根據當前ControllerContext 構建一個H即FileCollectionValueProvider 的時候,ASP.NETMVC 會從當前HTTP 請求的Files 屬性中獲取全部的HttpPostedFileBase 對象。多個HttpPostedFileBase 能夠共享相同的名稱,做爲數據源容器的字典將HttpPostedFileBase 的名稱做爲Key ,具備相同名稱的一個或者多個HttpPostedFileBase 對象構成一個數組做爲對應的Value 。post
子Action 和普通Action 的不一樣之處在於它不能用於響應來自客戶端的請求,只是在某個View 中被調用以生成某個部分的HTML.HtmIHelper.Action方法調用指定名稱的Action生成相應的Html的代碼.ui
做爲子Action 方法參數的數據來源與普通Action 方法有所不一樣, Model 綁定過程當中具體的數據提供由一個類型爲System. Web.Mvc.ChildAction ValueProvider 的對象來完成。this
public ChildActionValueProvider(ControllerContext controllerContext) : base(controllerContext.RouteData.Values, CultureInfo.InvariantCulture) {根據路由數據的Values生成數據容器 }
可是ChildActionValueProvider 的GetValue 方法針對給定的Key 獲取的值卻並非簡單地來源於原始的路由數據,否則ChildActionValueProvider 就和RouteDataValueProvider 沒有什麼分別了。實際上Chil dActionValueProvider 的GetValue 方法獲取的值來源於調用spa
HtmHelper 的擴展方法Action 時,經過參數routeValues 指定的RouteValueDictionary 對象。code
當咱們經過HtmIHelper 的擴展方法Action 調用某個指定的子Action 時,若是參數routeValues 指定的RouteValueDictionary 不爲空, HtmIHelper 會據此建立一個DictionaryValueProvider<Object>對象,並將這個對象添加到經過routeValues 參數表示的原始的RouteValueDictionary 對象中,對應的Key 就是Chil dActionValueProvider 的靜態屬性_ childActionValuesKey 所表示的GUID 。
這個RouteValueDictionary 被進一步封裝成表示請求上下文的RequestContext 對象,隨後被調子Action 所在的Controller 會在該請求上下文中被激活,在Controller 激活過程當中表示ControllerContext 的ControllerContext 被建立出來,毫無疑問它包含了以前建立的
Route ValueDictionary 對象。當咱們針對當前ControllerContext 建立ChildActionValueProvider的時候,做爲數據源的RouteValueDictionary 就是這麼一個對象。
當調用ChildActionValueProvider 的GetValue 方法獲取指定Key 的值時,實際上它並不會直接根據指定的Key 去獲取對應的值,而是根據經過其靜態字段_childAction ValuesKey 值去獲取對應的DictionaryValueProvider<objec t>對象,而後再調用該對象的GetValue 根據指定的Key 去得到相應的值。代碼以下
public override ValueProviderResult GetValue(string key) { if (key == null) { throw new ArgumentNullException("key"); } ValueProviderResult explicitValues = base.GetValue(ChildActionValuesKey); if (explicitValues != null) { DictionaryValueProvider<object> rawExplicitValues = explicitValues.RawValue as DictionaryValueProvider<object>; if (rawExplicitValues != null) { return rawExplicitValues.GetValue(key); } } return null; }
在調用HtmlHelper.Action時,代碼以下
internal static void ActionHelper(HtmlHelper htmlHelper, string actionName, string controllerName, RouteValueDictionary routeValues, TextWriter textWriter) { if (htmlHelper == null) { throw new ArgumentNullException("htmlHelper"); } if (String.IsNullOrEmpty(actionName)) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "actionName"); } RouteValueDictionary additionalRouteValues = routeValues; routeValues = MergeDictionaries(routeValues, htmlHelper.ViewContext.RouteData.Values); routeValues["action"] = actionName; if (!String.IsNullOrEmpty(controllerName)) { routeValues["controller"] = controllerName; } bool usingAreas; VirtualPathData vpd = htmlHelper.RouteCollection.GetVirtualPathForArea(htmlHelper.ViewContext.RequestContext, null /* name */, routeValues, out usingAreas); if (vpd == null) { throw new InvalidOperationException(MvcResources.Common_NoRouteMatched); } if (usingAreas) { routeValues.Remove("area"); if (additionalRouteValues != null) { additionalRouteValues.Remove("area"); } } if (additionalRouteValues != null) { routeValues[ChildActionValueProvider.ChildActionValuesKey] = new DictionaryValueProvider<object>(additionalRouteValues, CultureInfo.InvariantCulture); } RouteData routeData = CreateRouteData(vpd.Route, routeValues, vpd.DataTokens, htmlHelper.ViewContext); HttpContextBase httpContext = htmlHelper.ViewContext.HttpContext; RequestContext requestContext = new RequestContext(httpContext, routeData); ChildActionMvcHandler handler = new ChildActionMvcHandler(requestContext);//經過這個Handle(繼承MVCHandle)去處理請求 httpContext.Server.Execute(HttpHandlerUtil.WrapForServerExecute(handler), textWriter, true /* preserveForm */); }
//System. Web.Mvc. ValueProviderCollection 表示一個元素類型爲IValueProvider 的集合,除此以外,它自己也是一個ValueProvider public virtual ValueProviderResult GetValue(string key, bool skipValidation)//循環遍歷其中的Provider查找,返回第一個. { return (from provider in this let result = GetValueFromProvider(provider, key, skipValidation) where result != null select result).FirstOrDefault(); }
public override IValueProvider GetValueProvider(ControllerContext controllerContext) { if (controllerContext == null) { throw new ArgumentNullException("controllerContext"); } return new FormValueProvider(controllerContext, _unvalidatedValuesAccessor(controllerContext)); }
public static class ValueProviderFactories { private static readonly ValueProviderFactoryCollection _factories = new ValueProviderFactoryCollection() { new ChildActionValueProviderFactory(), new FormValueProviderFactory(), new JsonValueProviderFactory(), new RouteDataValueProviderFactory(), new QueryStringValueProviderFactory(), new HttpFileCollectionValueProviderFactory(), }; public static ValueProviderFactoryCollection Factories { get { return _factories; } } }
ValueProviderFactoryCollection的方法:返回全部的Provider.他們的順序決定了使用的優先級.
public IValueProvider GetValueProvider(ControllerContext controllerContext) { var valueProviders = from factory in _serviceResolver.Current let valueProvider = factory.GetValueProvider(controllerContext) where valueProvider != null select valueProvider; return new ValueProviderCollection(valueProviders.ToList()); }
public override IModelBinder Binder { get { IModelBinder binder = ModelBinders.GetBinderFromAttributes(_parameterInfo, () => String.Format(CultureInfo.CurrentCulture, MvcResources.ReflectedParameterBindingInfo_MultipleConverterAttributes, _parameterInfo.Name, _parameterInfo.Member)); return binder; } }
private void ReadSettingsFromBindAttribute() { BindAttribute attr = (BindAttribute)Attribute.GetCustomAttribute(_parameterInfo, typeof(BindAttribute)); if (attr == null) { return; } _exclude = new ReadOnlyCollection<string>(AuthorizeAttribute.SplitString(attr.Exclude)); _include = new ReadOnlyCollection<string>(AuthorizeAttribute.SplitString(attr.Include)); _prefix = attr.Prefix; }
public static class ModelBinders { private static readonly ModelBinderDictionary _binders = CreateDefaultBinderDictionary(); public static ModelBinderDictionary Binders { get { return _binders; } } private static ModelBinderDictionary CreateDefaultBinderDictionary() { // We can't add a binder to the HttpPostedFileBase type as an attribute, so we'll just // prepopulate the dictionary as a convenience to users. ModelBinderDictionary binders = new ModelBinderDictionary() { //值會放入innerDictionary { typeof(HttpPostedFileBase), new HttpPostedFileBaseModelBinder() }, { typeof(byte[]), new ByteArrayModelBinder() }, { typeof(Binary), new LinqBinaryModelBinder() }, { typeof(CancellationToken), new CancellationTokenModelBinder() } }; return binders; } } public class ModelBinderDictionary : IDictionary<Type, IModelBinder> { private readonly Dictionary<Type, IModelBinder> _innerDictionary = new Dictionary<Type, IModelBinder>(); private IModelBinder _defaultBinder; private ModelBinderProviderCollection _modelBinderProviders; public ModelBinderDictionary() : this(ModelBinderProviders.BinderProviders) { } }
//查找binder的過程
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 //1. IModelBinder binder = _modelBinderProviders.GetBinder(modelType); if (binder != null) { return binder; } //2. if (_innerDictionary.TryGetValue(modelType, out binder)) { return binder; } //3.讀取應用在參數類型上的CustomModelBinderAttribute binder = ModelBinders.GetBinderFromAttributes(modelType, () => String.Format(CultureInfo.CurrentCulture, MvcResources.ModelBinderDictionary_MultipleAttributes, modelType.FullName)); return binder ?? fallbackBinder; }
根據類型查找對應的默認的ModelBinder.最高優先級是,在參數上定義的ModelBindAttribute,其次是上邊的三個.書中有錯誤
能夠經過註冊對應數據類型的IModelBinder,代碼ModelBinders.Binders.Add(typeof(Baz) , new BazModelBinder());
也能夠添加Provider 代碼ModelBinderProviders.BinderProviders.Add(new MyModelBinderProvider())i
public static ModelBinderProviderCollection BinderProviders
{
get { return _binderProviders; }
}
public class ModelState { private ModelErrorCollection _errors = new ModelErrorCollection(); public ModelErrorCollection Errors { get { return this._errors; } } public ValueProviderResult Value { get; set; } }
protected virtual object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) { // collect all of the necessary binding properties Type parameterType = parameterDescriptor.ParameterType; IModelBinder binder = GetModelBinder(parameterDescriptor); IValueProvider valueProvider = controllerContext.Controller.ValueProvider; string parameterName = parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName; Predicate<string> propertyFilter = GetPropertyFilter(parameterDescriptor); // finally, call into the binder ModelBindingContext bindingContext = new ModelBindingContext() { FallbackToEmptyPrefix = (parameterDescriptor.BindingInfo.Prefix == null), // only fall back if prefix not specified ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, parameterType), ModelName = parameterName, ModelState = controllerContext.Controller.ViewData.ModelState, PropertyFilter = propertyFilter, ValueProvider = valueProvider }; object result = binder.BindModel(controllerContext, bindingContext); return result ?? parameterDescriptor.DefaultValue; }
若是沒有利用BindAttribute 特性爲參數設置一個前綴,默認狀況下會將參數名稱做爲前綴。經過前面的介紹咱們知道,這個前綴會被ValueProvider 用於數據的匹配。若是ValueProvider 經過此前綴找不到匹配的數據,將剔除前綴再次進行數據獲取。針對以下定義的Action 方法AddContacts ,在請求數據並不包含基於參數名("foo" 和"bar")前綴的狀況下,兩個參數最終將被綁定上相同的值。
public class ContactController
public void AddContacts(Contact foo , Contact bar)
反之,若是咱們應用BindAttribute 特性顯式地設置了一個前綴,這種去除前綴再次實施Model 綁定的後備機制將不會被採用,是否採用後備Model 綁定策略經過ModelBindingContext 的FallbackToEmptyPrefix 屬性來控制。
對於簡單數據類型,應該沒有這個用法.由於前綴置爲空的話,屬性名就好是空的了
protected virtual object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) { Type typeToCreate = modelType; // we can understand some collection interfaces, e.g. IList<>, IDictionary<,> if (modelType.IsGenericType) { Type genericTypeDefinition = modelType.GetGenericTypeDefinition(); if (genericTypeDefinition == typeof(IDictionary<,>)) { typeToCreate = typeof(Dictionary<,>).MakeGenericType(modelType.GetGenericArguments()); } else if (genericTypeDefinition == typeof(IEnumerable<>) || genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IList<>)) { typeToCreate = typeof(List<>).MakeGenericType(modelType.GetGenericArguments()); } } // fallback to the type's default constructor return Activator.CreateInstance(typeToCreate); }
list.CopyTo(array, 0);