ASP.NET Web API 框架研究 Action方法介绍
在根据请求解析出匹配的Controller类型并创建实例后,要在该Controller类型中的众多Action方法中选择与请求匹配的那一个,并执行,然后返回响应。
Action方法,其元数据,主要包括,ActionName,参数列表,返回类型,支持的方法,应用其上的特性,如过滤器,HttpMethod,自定义特性。
一、涉及的类及源码分析
类主要都在System.Web.Http.Controllers命名空间下
1、HttpActionDescriptor
是一个抽象类用来描述Controller类型中的每个方法的基本元数据,主要有以下成员:
属性:
public abstract string ActionName { get; } Action名称
public abstract Type ReturnType { get; } Action方法返回类型
public HttpControllerDescriptor ControllerDescriptor { get; set; } 所属Controller类型的描述符
public virtual Collection<HttpMethod> SupportedHttpMethods { get; } Action方法支持的HttpMethod集合,用来根据请求的方法来过滤当前Action方法是否在候选范围
public HttpConfiguration Configuration { get; set; } HttpConfiguration
public virtual ConcurrentDictionary<object, object> Properties { get; } 属性字典,可以附加任何对象到该属性
方法:
public abstract Collection<HttpParameterDescriptor> GetParameters() 返回方法的所有参数描述符HttpParameterDescriptor,其是HttpActionDescriptor重要组成部分
public virtual Collection<T> GetCustomAttributes<T>() where T : class 返回应用在Action方法上的各种类型的特性,特性是反射获取的,所以会缓存,该方法就是从缓存中返回
public virtual Collection<T> GetCustomAttributes<T>(bool inherit) where T : class 返回应用在Action方法上的各种类型的特性
public abstract Task<object> ExecuteAsync(HttpControllerContext controllerContext, IDictionary<string, object> arguments, CancellationToken cancellationToken); 执行方法
public abstract class HttpActionDescriptor { private readonly ConcurrentDictionary<object, object> _properties = new ConcurrentDictionary<object, object>(); private IActionResultConverter _converter; private readonly Lazy<Collection<FilterInfo>> _filterPipeline; private FilterGrouping _filterGrouping; private Collection<FilterInfo> _filterPipelineForGrouping; private HttpConfiguration _configuration; private HttpControllerDescriptor _controllerDescriptor; private readonly Collection<HttpMethod> _supportedHttpMethods = new Collection<HttpMethod>(); private HttpActionBinding _actionBinding; private static readonly ResponseMessageResultConverter _responseMessageResultConverter = new ResponseMessageResultConverter(); private static readonly VoidResultConverter _voidResultConverter = new VoidResultConverter(); protected HttpActionDescriptor() { _filterPipeline = new Lazy<Collection<FilterInfo>>(InitializeFilterPipeline); } protected HttpActionDescriptor(HttpControllerDescriptor controllerDescriptor) : this() { if (controllerDescriptor == null) { throw Error.ArgumentNull("controllerDescriptor"); } _controllerDescriptor = controllerDescriptor; _configuration = _controllerDescriptor.Configuration; } public abstract string ActionName { get; } public HttpConfiguration Configuration { get { return _configuration; } set { if (value == null) { throw Error.PropertyNull(); } _configuration = value; } } public virtual HttpActionBinding ActionBinding { get { if (_actionBinding == null) { ServicesContainer controllerServices = _controllerDescriptor.Configuration.Services; IActionValueBinder actionValueBinder = controllerServices.GetActionValueBinder(); HttpActionBinding actionBinding = actionValueBinder.GetBinding(this); _actionBinding = actionBinding; } return _actionBinding; } set { if (value == null) { throw Error.PropertyNull(); } _actionBinding = value; } } public HttpControllerDescriptor ControllerDescriptor { get { return _controllerDescriptor; } set { if (value == null) { throw Error.PropertyNull(); } _controllerDescriptor = value; } } public abstract Type ReturnType { get; } public virtual IActionResultConverter ResultConverter { get { if (_converter == null) { _converter = GetResultConverter(ReturnType); } return _converter; } } public virtual Collection<HttpMethod> SupportedHttpMethods { get { return _supportedHttpMethods; } } public virtual ConcurrentDictionary<object, object> Properties { get { return _properties; } } public virtual Collection<T> GetCustomAttributes<T>() where T : class { return GetCustomAttributes<T>(inherit: true); } public virtual Collection<T> GetCustomAttributes<T>(bool inherit) where T : class { return new Collection<T>(); } public virtual Collection<IFilter> GetFilters() { return new Collection<IFilter>(); } public abstract Collection<HttpParameterDescriptor> GetParameters(); internal static IActionResultConverter GetResultConverter(Type type) { if (type != null && type.IsGenericParameter) { throw Error.InvalidOperation(SRResources.HttpActionDescriptor_NoConverterForGenericParamterTypeExists, type); } if (type == null) { return _voidResultConverter; } else if (typeof(HttpResponseMessage).IsAssignableFrom(type)) { return _responseMessageResultConverter; } else if (typeof(IHttpActionResult).IsAssignableFrom(type)) { return null; } else { Type valueConverterType = typeof(ValueResultConverter<>).MakeGenericType(type); return TypeActivator.Create<IActionResultConverter>(valueConverterType).Invoke(); } } public abstract Task<object> ExecuteAsync(HttpControllerContext controllerContext, IDictionary<string, object> arguments, CancellationToken cancellationToken); public virtual Collection<FilterInfo> GetFilterPipeline() { return _filterPipeline.Value; } internal FilterGrouping GetFilterGrouping() { Collection<FilterInfo> currentFilterPipeline = GetFilterPipeline(); if (_filterGrouping == null || _filterPipelineForGrouping != currentFilterPipeline) { _filterGrouping = new FilterGrouping(currentFilterPipeline); _filterPipelineForGrouping = currentFilterPipeline; } return _filterGrouping; } private Collection<FilterInfo> InitializeFilterPipeline() { IEnumerable<IFilterProvider> filterProviders = _configuration.Services.GetFilterProviders(); IEnumerable<FilterInfo> filters = filterProviders.SelectMany(fp => fp.GetFilters(_configuration, this)).OrderBy(f => f, FilterInfoComparer.Instance); filters = RemoveDuplicates(filters.Reverse()).Reverse(); return new Collection<FilterInfo>(filters.ToList()); } private static IEnumerable<FilterInfo> RemoveDuplicates(IEnumerable<FilterInfo> filters) { Contract.Assert(filters != null); HashSet<Type> visitedTypes = new HashSet<Type>(); foreach (FilterInfo filter in filters) { object filterInstance = filter.Instance; Type filterInstanceType = filterInstance.GetType(); if (!visitedTypes.Contains(filterInstanceType) || AllowMultiple(filterInstance)) { yield return filter; visitedTypes.Add(filterInstanceType); } } } private static bool AllowMultiple(object filterInstance) { IFilter filter = filterInstance as IFilter; return filter == null || filter.AllowMultiple; } }
2、ReflectedHttpActionDescriptor
通过对目标Action方法进行反射来获取元数据,是对一个反射获得的MethodInfo对象的封装。
继承自抽象类HttpActionDescriptor,重写了属性和方法。具体看代码:
public class ReflectedHttpActionDescriptor : HttpActionDescriptor { private static readonly object[] _empty = new object[0]; private readonly Lazy<Collection<HttpParameterDescriptor>> _parameters; private ParameterInfo[] _parameterInfos; private Lazy<ActionExecutor> _actionExecutor; private MethodInfo _methodInfo; private Type _returnType; private string _actionName; private Collection<HttpMethod> _supportedHttpMethods; //反射,缓冲提高性能 private object[] _attributeCache; private object[] _declaredOnlyAttributeCache; private static readonly HttpMethod[] _supportedHttpMethodsByConvention = { HttpMethod.Get, HttpMethod.Post, HttpMethod.Put, HttpMethod.Delete, HttpMethod.Head, HttpMethod.Options, new HttpMethod("PATCH") }; public ReflectedHttpActionDescriptor() { _parameters = new Lazy<Collection<HttpParameterDescriptor>>(() => InitializeParameterDescriptors()); _supportedHttpMethods = new Collection<HttpMethod>(); } public ReflectedHttpActionDescriptor(HttpControllerDescriptor controllerDescriptor, MethodInfo methodInfo) : base(controllerDescriptor) { if (methodInfo == null) { throw Error.ArgumentNull("methodInfo"); } InitializeProperties(methodInfo); _parameters = new Lazy<Collection<HttpParameterDescriptor>>(() => InitializeParameterDescriptors()); } public override string ActionName { get { return _actionName; } } public override Collection<HttpMethod> SupportedHttpMethods { get { return _supportedHttpMethods; } } public MethodInfo MethodInfo { get { return _methodInfo; } set { if (value == null) { throw Error.PropertyNull(); } InitializeProperties(value); } } private ParameterInfo[] ParameterInfos { get { if (_parameterInfos == null) { _parameterInfos = _methodInfo.GetParameters(); } return _parameterInfos; } } /// <inheritdoc/> public override Type ReturnType { get { return _returnType; } } /// <inheritdoc/> public override Collection<T> GetCustomAttributes<T>(bool inherit) { object[] attributes = inherit ? _attributeCache : _declaredOnlyAttributeCache; return new Collection<T>(TypeHelper.OfType<T>(attributes)); } public override Task<object> ExecuteAsync(HttpControllerContext controllerContext, IDictionary<string, object> arguments, CancellationToken cancellationToken) { if (controllerContext == null) { throw Error.ArgumentNull("controllerContext"); } if (arguments == null) { throw Error.ArgumentNull("arguments"); } if (cancellationToken.IsCancellationRequested) { return TaskHelpers.Canceled<object>(); } try { object[] argumentValues = PrepareParameters(arguments, controllerContext); return _actionExecutor.Value.Execute(controllerContext.Controller, argumentValues); } catch (Exception e) { return TaskHelpers.FromError<object>(e); } } public override Collection<IFilter> GetFilters() { return new Collection<IFilter>(GetCustomAttributes<IFilter>().Concat(base.GetFilters()).ToList()); } public override Collection<HttpParameterDescriptor> GetParameters() { return _parameters.Value; } private void InitializeProperties(MethodInfo methodInfo) { _methodInfo = methodInfo; _parameterInfos = null; _returnType = GetReturnType(methodInfo); _actionExecutor = new Lazy<ActionExecutor>(() => InitializeActionExecutor(_methodInfo)); _declaredOnlyAttributeCache = _methodInfo.GetCustomAttributes(inherit: false); _attributeCache = _methodInfo.GetCustomAttributes(inherit: true); _actionName = GetActionName(_methodInfo, _attributeCache); _supportedHttpMethods = GetSupportedHttpMethods(_methodInfo, _attributeCache); } internal static Type GetReturnType(MethodInfo methodInfo) { Type result = methodInfo.ReturnType; if (typeof(Task).IsAssignableFrom(result)) { result = TypeHelper.GetTaskInnerTypeOrNull(methodInfo.ReturnType); } if (result == typeof(void)) { result = null; } return result; } private Collection<HttpParameterDescriptor> InitializeParameterDescriptors() { Contract.Assert(_methodInfo != null); List<HttpParameterDescriptor> parameterInfos = ParameterInfos.Select( (item) => new ReflectedHttpParameterDescriptor(this, item)).ToList<HttpParameterDescriptor>(); return new Collection<HttpParameterDescriptor>(parameterInfos); } private object[] PrepareParameters(IDictionary<string, object> parameters, HttpControllerContext controllerContext) { // This is on a hotpath, so a quick check to avoid the allocation if we have no parameters. if (_parameters.Value.Count == 0) { return _empty; } ParameterInfo[] parameterInfos = ParameterInfos; int parameterCount = parameterInfos.Length; object[] parameterValues = new object[parameterCount]; for (int parameterIndex = 0; parameterIndex < parameterCount; parameterIndex++) { parameterValues[parameterIndex] = ExtractParameterFromDictionary(parameterInfos[parameterIndex], parameters, controllerContext); } return parameterValues; } private object ExtractParameterFromDictionary(ParameterInfo parameterInfo, IDictionary<string, object> parameters, HttpControllerContext controllerContext) { object value; if (!parameters.TryGetValue(parameterInfo.Name, out value)) { // the key should always be present, even if the parameter value is null throw new HttpResponseException(controllerContext.Request.CreateErrorResponse( HttpStatusCode.BadRequest, SRResources.BadRequest, Error.Format(SRResources.ReflectedActionDescriptor_ParameterNotInDictionary, parameterInfo.Name, parameterInfo.ParameterType, MethodInfo, MethodInfo.DeclaringType))); } if (value == null && !TypeHelper.TypeAllowsNullValue(parameterInfo.ParameterType)) { // tried to pass a null value for a non-nullable parameter type throw new HttpResponseException(controllerContext.Request.CreateErrorResponse( HttpStatusCode.BadRequest, SRResources.BadRequest, Error.Format(SRResources.ReflectedActionDescriptor_ParameterCannotBeNull, parameterInfo.Name, parameterInfo.ParameterType, MethodInfo, MethodInfo.DeclaringType))); } if (value != null && !parameterInfo.ParameterType.IsInstanceOfType(value)) { // value was supplied but is not of the proper type throw new HttpResponseException(controllerContext.Request.CreateErrorResponse( HttpStatusCode.BadRequest, SRResources.BadRequest, Error.Format(SRResources.ReflectedActionDescriptor_ParameterValueHasWrongType, parameterInfo.Name, MethodInfo, MethodInfo.DeclaringType, value.GetType(), parameterInfo.ParameterType))); } return value; } private static string GetActionName(MethodInfo methodInfo, object[] actionAttributes) { ActionNameAttribute nameAttribute = TypeHelper.OfType<ActionNameAttribute>(actionAttributes).FirstOrDefault(); return nameAttribute != null ? nameAttribute.Name : methodInfo.Name; } private static Collection<HttpMethod> GetSupportedHttpMethods(MethodInfo methodInfo, object[] actionAttributes) { Collection<HttpMethod> supportedHttpMethods = new Collection<HttpMethod>(); ICollection<IActionHttpMethodProvider> httpMethodProviders = TypeHelper.OfType<IActionHttpMethodProvider>(actionAttributes); if (httpMethodProviders.Count > 0) { // Get HttpMethod from attributes foreach (IActionHttpMethodProvider httpMethodSelector in httpMethodProviders) { foreach (HttpMethod httpMethod in httpMethodSelector.HttpMethods) { supportedHttpMethods.Add(httpMethod); } } } else { // Get HttpMethod from method name convention for (int i = 0; i < _supportedHttpMethodsByConvention.Length; i++) { if (methodInfo.Name.StartsWith(_supportedHttpMethodsByConvention[i].Method, StringComparison.OrdinalIgnoreCase)) { supportedHttpMethods.Add(_supportedHttpMethodsByConvention[i]); break; } } } if (supportedHttpMethods.Count == 0) { // Use POST as the default HttpMethod supportedHttpMethods.Add(HttpMethod.Post); } return supportedHttpMethods; } public override int GetHashCode() { if (_methodInfo != null) { return _methodInfo.GetHashCode(); } return base.GetHashCode(); } /// <inheritdoc /> public override bool Equals(object obj) { if (_methodInfo != null) { ReflectedHttpActionDescriptor otherDescriptor = obj as ReflectedHttpActionDescriptor; if (otherDescriptor == null) { return false; } return _methodInfo.Equals(otherDescriptor._methodInfo); } return base.Equals(obj); } private static ActionExecutor InitializeActionExecutor(MethodInfo methodInfo) { if (methodInfo.ContainsGenericParameters) { throw Error.InvalidOperation(SRResources.ReflectedHttpActionDescriptor_CannotCallOpenGenericMethods, methodInfo, methodInfo.ReflectedType.FullName); } return new ActionExecutor(methodInfo); } }
其还有个内部类,先列出,平时多看看:
private sealed class ActionExecutor { private readonly Func<object, object[], Task<object>> _executor; private static MethodInfo _convertOfTMethod = typeof(ActionExecutor).GetMethod("Convert", BindingFlags.Static | BindingFlags.NonPublic); public ActionExecutor(MethodInfo methodInfo) { Contract.Assert(methodInfo != null); _executor = GetExecutor(methodInfo); } public Task<object> Execute(object instance, object[] arguments) { return _executor(instance, arguments); } // Method called via reflection. private static Task<object> Convert<T>(object taskAsObject) { Task<T> task = (Task<T>)taskAsObject; return task.CastToObject<T>(); } private static Func<object, Task<object>> CompileGenericTaskConversionDelegate(Type taskValueType) { Contract.Assert(taskValueType != null); return (Func<object, Task<object>>)Delegate.CreateDelegate(typeof(Func<object, Task<object>>), _convertOfTMethod.MakeGenericMethod(taskValueType)); } private static Func<object, object[], Task<object>> GetExecutor(MethodInfo methodInfo) { // Parameters to executor ParameterExpression instanceParameter = Expression.Parameter(typeof(object), "instance"); ParameterExpression parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); // Build parameter list List<Expression> parameters = new List<Expression>(); ParameterInfo[] paramInfos = methodInfo.GetParameters(); for (int i = 0; i < paramInfos.Length; i++) { ParameterInfo paramInfo = paramInfos[i]; BinaryExpression valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); UnaryExpression valueCast = Expression.Convert(valueObj, paramInfo.ParameterType); // valueCast is "(Ti) parameters[i]" parameters.Add(valueCast); } // Call method UnaryExpression instanceCast = (!methodInfo.IsStatic) ? Expression.Convert(instanceParameter, methodInfo.ReflectedType) : null; MethodCallExpression methodCall = methodCall = Expression.Call(instanceCast, methodInfo, parameters); // methodCall is "((MethodInstanceType) instance).method((T0) parameters[0], (T1) parameters[1], ...)" // Create function if (methodCall.Type == typeof(void)) { // for: public void Action() Expression<Action<object, object[]>> lambda = Expression.Lambda<Action<object, object[]>>(methodCall, instanceParameter, parametersParameter); Action<object, object[]> voidExecutor = lambda.Compile(); return (instance, methodParameters) => { voidExecutor(instance, methodParameters); return TaskHelpers.NullResult(); }; } else { // must coerce methodCall to match Func<object, object[], object> signature UnaryExpression castMethodCall = Expression.Convert(methodCall, typeof(object)); Expression<Func<object, object[], object>> lambda = Expression.Lambda<Func<object, object[], object>>(castMethodCall, instanceParameter, parametersParameter); Func<object, object[], object> compiled = lambda.Compile(); if (methodCall.Type == typeof(Task)) { // for: public Task Action() return (instance, methodParameters) => { Task r = (Task)compiled(instance, methodParameters); ThrowIfWrappedTaskInstance(methodInfo, r.GetType()); return r.CastToObject(); }; } else if (typeof(Task).IsAssignableFrom(methodCall.Type)) { // for: public Task<T> Action() // constructs: return (Task<object>)Convert<T>(((Task<T>)instance).method((T0) param[0], ...)) Type taskValueType = TypeHelper.GetTaskInnerTypeOrNull(methodCall.Type); var compiledConversion = CompileGenericTaskConversionDelegate(taskValueType); return (instance, methodParameters) => { object callResult = compiled(instance, methodParameters); Task<object> convertedResult = compiledConversion(callResult); return convertedResult; }; } else { // for: public T Action() return (instance, methodParameters) => { var result = compiled(instance, methodParameters); // Throw when the result of a method is Task. Asynchronous methods need to declare that they // return a Task. Task resultAsTask = result as Task; if (resultAsTask != null) { throw Error.InvalidOperation(SRResources.ActionExecutor_UnexpectedTaskInstance, methodInfo.Name, methodInfo.DeclaringType.Name); } return Task.FromResult(result); }; } } } private static void ThrowIfWrappedTaskInstance(MethodInfo method, Type type) { // Throw if a method declares a return type of Task and returns an instance of Task<Task> or Task<Task<T>> // This most likely indicates that the developer forgot to call Unwrap() somewhere. Contract.Assert(method.ReturnType == typeof(Task)); // Fast path: check if type is exactly Task first. if (type != typeof(Task)) { Type innerTaskType = TypeHelper.GetTaskInnerTypeOrNull(type); if (innerTaskType != null && typeof(Task).IsAssignableFrom(innerTaskType)) { throw Error.InvalidOperation(SRResources.ActionExecutor_WrappedTaskInstance, method.Name, method.DeclaringType.Name, type.FullName); } } } }
3、ActionNameAttribute
一般Acton方法的名字默认作为Action名称,其作为路由模板一部分,为了优化,经常要修改方法名,为了方便灵活修改,提供一个ActionNameAttribute自定义特性来定义一个与方法名不同的Action名称。
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public sealed class ActionNameAttribute : Attribute { public ActionNameAttribute(string name) { Name = name; } public string Name { get; private set; } }
使用:
[ActionName("MyActionName")]
public void ActionMethod()
4、AcceptVerbsAttribute IActionHttpMethodProvider
“Action方法名称决定支持的Http方法“ ----- Action支持的Http方法的默认策略。
默认情况一个Action方法默认只支持一个HttpMethod,不指定,默认就是Post。
根据默认策略,Action方法名称前缀是某个HttpMethod(不区分大小写),就默认支持对应的HttpMethod方法,如GetABC,就默认支持Get(唯一),没有这种前缀,就默认是Post,如Action方法名为Other。
而IActionHttpMethodProvider提供一种自定义支持方法(可多个)的方式,它是通过自定义特性的方式,用在Action方法上。
public interface IActionHttpMethodProvider { Collection<HttpMethod> HttpMethods { get; } }
ASP.NET Web API提供了一个默认实现AcceptVerbsAttribute
public sealed class AcceptVerbsAttribute : Attribute, IActionHttpMethodProvider { private readonly Collection<HttpMethod> _httpMethods; public AcceptVerbsAttribute(string method) : this(new string[] { method }) { } public AcceptVerbsAttribute(params string[] methods) { _httpMethods = methods != null ? new Collection<HttpMethod>(methods.Select(method => HttpMethodHelper.GetHttpMethod(method)).ToArray()) : new Collection<HttpMethod>(new HttpMethod[0]); } internal AcceptVerbsAttribute(params HttpMethod[] methods) { _httpMethods = new Collection<HttpMethod>(methods); } public Collection<HttpMethod> HttpMethods { get { return _httpMethods; } } }
使用方式:
[AcceptVerbsAttribute("PUT","POST")]
public void ActionMethod()
为了字符串书写错误,框架里又提供了七种方法对应的特性,HttpGetAtrribute,HttpPostAtrribute,HttpPutAtrribute,HttpDeleteAtrribute,HttpHeadAtrribute,HttpOptionsAtrribute,HttpPatchAtrribute。
同样可以指定多个:
[HttpPut]
[HttpPost]
public void ActionMethod()
5、HttpParameterDescriptor
是个抽象类,第三个描述对象,前边介绍了HttpControllerDescriptor和HttpActionDescriptor,对Action方法的每个参数都通过HttpParameterDescriptor进行描述,主要成员如下:
属性:
public HttpActionDescriptor ActionDescriptor 所在Action方法的描述符
public HttpConfiguration Configuration { get; set; } 与HttpActionDescriptor 的HttpActionDescriptor同名属性有相同引用
public ConcurrentDictionary<object, object> Properties 字典属性,附加任何对象
public virtual object DefaultValue { get; } 默认值
public abstract string ParameterName { get; } 参数名称
public abstract Type ParameterType { get; } 参数类型
public virtual bool IsOptional { get; } 是否可选参数,默认不是
方法:
public virtual Collection<T> GetCustomAttributes<T>() where T : class 返回参数上定义的一个或多个特性
public abstract class HttpParameterDescriptor { private readonly ConcurrentDictionary<object, object> _properties = new ConcurrentDictionary<object, object>(); private ParameterBindingAttribute _parameterBindingAttribute; private bool _searchedModelBinderAttribute; private HttpConfiguration _configuration; private HttpActionDescriptor _actionDescriptor; protected HttpParameterDescriptor() { } protected HttpParameterDescriptor(HttpActionDescriptor actionDescriptor) { if (actionDescriptor == null) { throw Error.ArgumentNull("actionDescriptor"); } _actionDescriptor = actionDescriptor; _configuration = _actionDescriptor.Configuration; } public HttpConfiguration Configuration { get { return _configuration; } set { if (value == null) { throw Error.PropertyNull(); } _configuration = value; } } public HttpActionDescriptor ActionDescriptor { get { return _actionDescriptor; } set { if (value == null) { throw Error.PropertyNull(); } _actionDescriptor = value; } } public ConcurrentDictionary<object, object> Properties { get { return _properties; } } public virtual object DefaultValue { get { return null; } } public abstract string ParameterName { get; } public abstract Type ParameterType { get; } public virtual string Prefix { get { ParameterBindingAttribute attribute = ParameterBinderAttribute; ModelBinderAttribute modelAttribute = attribute as ModelBinderAttribute; return modelAttribute != null ? modelAttribute.Name : null; } } public virtual bool IsOptional { get { return false; } } /// <summary> /// Return a <see cref="ParameterBindingAttribute"/> if present on this parameter's signature or declared type. /// Returns null if no attribute is specified. /// </summary> public virtual ParameterBindingAttribute ParameterBinderAttribute { get { if (_parameterBindingAttribute == null) { if (!_searchedModelBinderAttribute) { _searchedModelBinderAttribute = true; _parameterBindingAttribute = FindParameterBindingAttribute(); } } return _parameterBindingAttribute; } set { _parameterBindingAttribute = value; } } public virtual Collection<T> GetCustomAttributes<T>() where T : class { return new Collection<T>(); } private ParameterBindingAttribute FindParameterBindingAttribute() { // Can be on parameter itself or on the parameter's type. Nearest wins. return ChooseAttribute(GetCustomAttributes<ParameterBindingAttribute>()) ?? ChooseAttribute(ParameterType.GetCustomAttributes<ParameterBindingAttribute>(false)); } private static ParameterBindingAttribute ChooseAttribute(IList<ParameterBindingAttribute> list) { if (list.Count == 0) { return null; } if (list.Count > 1) { // Multiple attributes specified at the same level return new AmbiguousParameterBindingAttribute(); } return list[0]; } private sealed class AmbiguousParameterBindingAttribute : ParameterBindingAttribute { public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter) { string message = Error.Format(SRResources.ParameterBindingConflictingAttributes, parameter.ParameterName); return parameter.BindAsError(message); } } }
6、ReflectedHttpParameterDescriptor
继承自抽象类HttpParameterDescriptor,通过反射提取描述参数的元数据,主要是通过反射获取的ParameterInfo对象提供,通过构造函数传入。ParameterInfo是关键,参数的基本数据(ParamterName, ParameterType,DefaultValue,IsOptional),以及定义在参数上的特性(GetCustomAttributes<T>)方法都是通过其提供。
另外,ReflectedHttpActionDescriptor的GetParameters返回的是ReflectedHttpParameterDescriptor列表,且ParameterInfo是通过反射出来的MethodInfo的GetParameters方法获取的。
public class ReflectedHttpParameterDescriptor : HttpParameterDescriptor { private ParameterInfo _parameterInfo; public ReflectedHttpParameterDescriptor(HttpActionDescriptor actionDescriptor, ParameterInfo parameterInfo) : base(actionDescriptor) { if (parameterInfo == null) { throw Error.ArgumentNull("parameterInfo"); } ParameterInfo = parameterInfo; } public ReflectedHttpParameterDescriptor() { } public override object DefaultValue { get { object value; if (ParameterInfo.TryGetDefaultValue(out value)) { return value; } else { return base.DefaultValue; } } } public ParameterInfo ParameterInfo { get { return _parameterInfo; } set { if (value == null) { throw Error.PropertyNull(); } _parameterInfo = value; } } public override bool IsOptional { get { return ParameterInfo.IsOptional; } } public override string ParameterName { get { return ParameterInfo.Name; } } public override Type ParameterType { get { return ParameterInfo.ParameterType; } } public override Collection<TAttribute> GetCustomAttributes<TAttribute>() { return new Collection<TAttribute>((TAttribute[])ParameterInfo.GetCustomAttributes(typeof(TAttribute), inherit: false)); } }