Asp.net web Api源码分析-HttpActionDescriptor的创建

紧接着上文Asp.net web Api源码分析-HttpControllerDispatcher (Controller的创建)这里已经创建好了IHttpController,现在让我们来看看它的ExecuteAsync方法,这个方法很是复杂啊。

 

首先调用Initialize方法初始化ControllerContext、_request、_configuration,紧接着就创建一个HttpActionDescriptor

 HttpActionDescriptor actionDescriptor = controllerServices.GetActionSelector().SelectAction(controllerContext);

   在DefaultServices中有这么一句SetSingle<IHttpActionSelector>(new ApiControllerActionSelector());, 所以我们就知道controllerServices.GetActionSelector()返回的是一个 ApiControllerActionSelector实例。其中ApiControllerActionSelector的SelectAction 实现也比较简单:

    public virtual HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
        {
            ActionSelectorCacheItem internalSelector = GetInternalSelector(controllerContext.ControllerDescriptor);
            return internalSelector.SelectAction(controllerContext);

        }

我们首先来看看GetInternalSelector方法实现:、

   private ActionSelectorCacheItem GetInternalSelector(HttpControllerDescriptor controllerDescriptor)
        {
            // First check in the local fast cache and if not a match then look in the broader
            // HttpControllerDescriptor.Properties cache
            if (_fastCache == null)
            {
                ActionSelectorCacheItem selector = new ActionSelectorCacheItem(controllerDescriptor);
                Interlocked.CompareExchange(ref _fastCache, selector, null);
                return selector;
            }
            else if (_fastCache.HttpControllerDescriptor == controllerDescriptor)
            {
                // If the key matches and we already have the delegate for creating an instance then just execute it
                return _fastCache;
            }
            else
            {
                // If the key doesn't match then lookup/create delegate in the HttpControllerDescriptor.Properties for
                // that HttpControllerDescriptor instance
                ActionSelectorCacheItem selector = (ActionSelectorCacheItem)controllerDescriptor.Properties.GetOrAdd(
                    _cacheKey,
                    _ => new ActionSelectorCacheItem(controllerDescriptor));
                return selector;
            }
        }

说白了就是直接实例化一个ActionSelectorCacheItem,让我们来看看ActionSelectorCacheItem的构造函数:

 首先根据 _controllerDescriptor.ControllerType来获取所有共有、实例方法,然后过滤掉那些特殊的方法最后得到一个 可以从当Action的方法集合validMethods。循环集合中的每一个方法,依次处理。首先我们实例化一个 ReflectedHttpActionDescriptor,采用以下代码:

     ReflectedHttpActionDescriptor actionDescriptor = new ReflectedHttpActionDescriptor(_controllerDescriptor, method);

现在我们来看看ReflectedHttpActionDescriptor的构造函数有什么特别的:

 这里ReflectedHttpActionDescriptor首先调用父类HttpActionDescriptor的构造函数创建一个 FilterInfo的集合,然后调用自己的InitializeProperties来初始化一些变量,最后在调用 InitializeParameterDescriptors来设置参数。这里父类的InitializeFilterPipeline方法我们暂时忽 略它,在后面要用的时候在来说它。在InitializeProperties方法中这几句都比较重要首先设置方法的返回值(  _returnType = GetReturnType(methodInfo);)和执行函数(new Lazy<ActionExecutor>(() => InitializeActionExecutor(_methodInfo));和mvc源码一样),然后是获取方法的Attribute特性,后面的几句也没什么特别的到用的时候在说吧,默认我们是没有什么Attributer特性,其 中GetSupportedHttpMethods方法主要是检查当前的方法支持哪些http请求处理,如果方法没有 IActionHttpMethodProvider特性,那么我们就根据方法的名称来处理,看方法是以什么打头的 (Get,Post,Put,Delete、Head、Options),这里 InitializeParameterDescriptors方法也比较简单,把每个方法的每个参数有创建一个 ReflectedHttpParameterDescriptor实例,ReflectedHttpParameterDescriptor的构造方法 也没什么特殊的。

现在我们再来看看HttpActionDescriptor的ActionBinding属性,

 ServicesContainer controllerServices = _controllerDescriptor.Configuration.Services;
  IActionValueBinder actionValueBinder = controllerServices.GetActionValueBinder();
  HttpActionBinding actionBinding = actionValueBinder.GetBinding(this);

我们可以知道默认的controllerServices是DefaultServices,所以这里的actionValueBinder其实是DefaultActionValueBinder实例,这里的ActionBinding主要是涉及到参数绑定的时候需要,暂时忽略它吧。

现在让我们回到ActionSelectorCacheItem的构造方法中来,这里的_actionParameterNames、_actionNameMapping我们都知道它是什么东东了,

最后这里还有这么一句

_cacheListVerbs = new ReflectedHttpActionDescriptor[len][];
                for (int i = 0; i < len; i++)
                {
                    _cacheListVerbs[i] = FindActionsForVerbWorker(_cacheListVerbKinds[i]);
                }

这里的主要目标就是把当前的Controller中的所有Action给分成3类,它们分别支持Get、put、post请求,美一类都可能有多个方法。

现在让我们来看看ActionSelectorCacheItem的SelectAction方法:

 这里首先获取当前http请求方法(get、put、post) 然后从当前路由信息里面去查找Action参数,如果找到则通过_actionNameMapping[actionName].ToArray() 来获取当前的Action集合在过滤掉符合此次http请求的Action,否则调用调 用 FindActionsForVerb(incomingMethod)来获取Action集合,FindActionsForVerb方法默认会现在_cacheListVerbs中根据http请求类型来找Action集合,如果找到则返回,否者查找符合条件的整个Controller中的Action。 现在查找到的Action都在actionsFoundByHttpMethods里面,我们需要过滤掉这个Action集合,只能从这个集合中返回一个 Action,这里我们首先用FindActionUsingRouteAndQueryParameters方法来过滤,

 这个方法代码有点多,其实很简单的,首先整合controllerContext.RouteData.Values和 request.GetQueryNameValuePairs()中的数据为combinedParameterNames,然后看action中的每 个参数是否都能在combinedParameterNames里面找到,如果找到则把结果放到actionsFound集合中来,如果 actionsFound集合中Action多余一个,则我们取参数最后的那个Action,同样这里如果combinedParameterNames 没有数据,则我们就返回一个没有参数的Action。通过FindActionUsingRouteAndQueryParameters方法我们可以从一大堆的Action中获取一个Action
紧随其后我们采用RunSelectionFilters方法来验证我们的Action方法是否有效,如果Action没有 IActionMethodSelector特性,则直接返回,否者依次调用IActionMethodSelector的 IsValidForRequest来验证这个Action是否合法,最后返回合法的Action。其具体实现如下:

 回到我们的SelectAction方法中来,最后返回我们找到的ReflectedHttpActionDescriptor实例,如果没找到或找到多 个都抛出异常,这里个人建议我们在路由中尽力用到Action参数,且每个Action的名称不同,为了和默认的编码一直我们的Action也尽力用 Get,put、post、Delete打头。在前面我们获取ControllerType时是有缓存的,根据ControllerType来创建实例也 是有缓存的只不过缓存的是一个表达式数,这里的ActionSelectorCacheItem也是有缓存的,在mvc中也有类似的缓存。 到这里我们的HttpActionDescriptor创建就结束了。

posted on   dz45693  阅读(3710)  评论(2编辑  收藏  举报

编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构

导航

< 2012年12月 >
25 26 27 28 29 30 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示