zoukankan      html  css  js  c++  java
  • 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));
            }
        }
  • 相关阅读:
    css 父层 透明 子层不透明Alpha
    ecshop循环foreach,iteration,key,index
    ecshop变量介绍
    ecshop 获取某个商品的 所有订单信息 或者销量
    echosp 销量排行 新增实际价格
    ecshop后台模板设置中将非可编辑区改为可编辑区
    ecshop 影响全局的标量lib_main.php
    个人js
    fixed的left:50%,漂浮
    js返回顶部
  • 原文地址:https://www.cnblogs.com/shawnhu/p/8127320.html
Copyright © 2011-2022 走看看