我是伊只雄熊

导航

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));
        }
    }

 

posted on 2017-12-30 13:08  我是伊只雄熊  阅读(600)  评论(0编辑  收藏  举报