zoukankan      html  css  js  c++  java
  • [Web API] Web API 2 深入系列(4) Action的选择

    目录

    1. ApiController

    2. HttpActionDescriptor

    3. IHttpActionSelector

    ApiController

    在上节中,讲到如何选择并激活对应的IHttpController,而一般我们在开发中使用的是ApiController

    public abstract class ApiController : IHttpController, IDisposable
    {
        public virtual Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)
        {
            this.Initialize(controllerContext);
            HttpActionDescriptor actionDescriptor = services.GetActionSelector().SelectAction(controllerContext);
            HttpActionContext actionContext = new HttpActionContext(controllerContext,actionDescriptor);
            return services.GetActionInvoker().InvokeActionAsync(actionContext, cancellationToken);
        }
    }
    

    在ApiController中,我们看到通过内置的DI容器选择出对应的HttpActionDescriptor.本节重点内容就是介绍SelectAction方法.

    HttpActionDescriptor

    在介绍IHttpActionSelector前,我们需要了解HttpActionDescriptor

    public abstract class HttpActionDescriptor
    {
        //相关联的HttpControllerDescriptor
        public HttpControllerDescriptor ControllerDescriptor { get; } { set; }
        //Action名称(通过使用ActionName特性,Action名称可以和方法名称不同)
        public abstract string ActionName { get; }
        //Action返回值类型(void为null)
        public abstract Type ReturnType { get; }
        //支持的HttpMethod(默认一个Action方法支持一种HttpMethod,且默认为Post Method,可以使用AcceptVerbs特性支持多个)
        public virtual Collection<HttpMethod> SupportedHttpMethods { get; }
        //Action方法所有参数
        public abstract Collection<HttpParameterDescriptor> Parameters { get; };
    }
    

    而HttpActionDescriptor默认实现为ReflectedHttpActionDescriptor,这里稍微展示下初始化HttpActionDescriptor过程

    public class ReflectedHttpActionDescriptor : HttpActionDescriptor
    {
        //初始化HttpActionDescriptor的属性
        public ReflectedHttpActionDescriptor(HttpControllerDescriptor controllerDescriptor, MethodInfo methodInfo)
          : base(controllerDescriptor)
        {
          this.InitializeProperties(methodInfo);
          this._parameters = new Lazy<Collection<HttpParameterDescriptor>>(() => this.InitializeParameterDescriptors());
        }
    
        private Collection<HttpParameterDescriptor> InitializeParameterDescriptors()
        {
            return this.ParameterInfos.Select(item => new ReflectedHttpParameterDescriptor((HttpActionDescriptor) this, item)).ToList();
        }
    }
    

    IHttpActionSelector

    IHttpActionSelector是选择Action最核心的类

    public interface IHttpActionSelector
    {
        //选择符合标准的Action
        HttpActionDescriptor SelectAction(HttpControllerContext controllerContext);
    
        //获取HttpController所有Action
        ILookup<string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor);
    }
    

    IHttpActionSelector默认的实现为ApiControllerActionSelector

    public class ApiControllerActionSelector : IHttpActionSelector
    {
        ILookup<string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor);
        {
            //找到所有符合要求的方法
            var methods = controllerDescriptor.ControllerType
            .GetMethods(BindingFlags.Instance | BindingFlags.Public), methodInfo => 
            !methodInfo.IsSpecialName && !methodInfo.GetBaseDefinition().DeclaringType.IsAssignableFrom(TypeHelper.ApiControllerType) 
            && methodInfo.GetCustomAttribute<NonActionAttribute>() == null);
            return methods.Select(method => new HttpActionDescriptor(controllerDescriptor,method))...;
        }
    }
    

    注意:GetActionMapping会在请求每个HttpController第一次的时候 缓存当前所有HttpActionDescriptor

    当我们找到HttpController下所有HttpActionDescriptor,还需要最后一步通过SelectAction筛选出最终的Action

    由于这块源码稍微复杂,这里把关键的几步及对应的方法名说明下

    public virtual HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
    {
        1. Action名称过滤
    
        如果路由中配置Action变量,则会先过滤ActionName.调用方法GetInitialCandidateList
    
        2. Http方法过滤
    
        对于Get Put Post默认都通过,调用方法FindActionsForVerb
    
        3. 必须的参数能绑定上
    
        在Action方法上的必须参数,在路由参数和Request参数中能获取到,调用方法FindActionMatchRequiredRouteAndQueryParameters
    
        4. 参数个数符合最多的匹配
    
        当多个Action方法都满足以上3个条件时,取最多参数符合的那个,调用方法FindActionMatchMostRouteAndQueryParameters
    
        5. 取唯一匹配
        最终匹配结果为1个Action方法则成功,多个或零失败,在SelectAction方法本身调用
    }
    

    这里稍微举个例子来解释SelectAction

    public class DemoController : ApiController
    {
        public string Get(int x)
    
        public string Get(int x, int y)
    
        public string Get(string x, string y)
    
        public string Get(string x, string y, string z)
    }
    

    分别请求如下地址,并列出对应的匹配方法

    • demo?x=1 OK

      public string Get(int x)

    • demo?x=1&y=2 Erro

      public string Get(int x, int y)

      public string Get(string x, string y)

    • demo?x=1&y=2&z=3 OK

      public string Get(string x, string y, string z)

    (OK表示请求成功,Erro表示请求失败)

    备注:
    - Action的选择明显比Controller的激活要复杂

    - 文章中的代码并非完整WebAPI代码,一般是经过自己精简后的.
    
    - 本篇内容使用MarkDown语法编辑
    

    首发地址:http://neverc.cnblogs.com/p/5956432.html

  • 相关阅读:
    git 多人协作
    git 版本管理
    git 安装配置
    git 忽略文件
    git 分支管理
    linux文件管理 文件操作
    linux文件管理 文件搜索
    linux文件管理 文件权限
    linux系统管理 基本指令
    003.html
  • 原文地址:https://www.cnblogs.com/neverc/p/5956432.html
Copyright © 2011-2022 走看看