• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

ASP.NETWebAPI框架研究Action方法介绍

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

  在根据请求解析出匹配的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) =>
                        {
                             
                       
                    
                    

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap