ApiController

在上節(jié)中,講到如何選擇并激活對(duì)應(yīng)的IHttpController,而一般我們?cè)陂_發(fā)中使用的是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中,我們看到通過內(nèi)置的DI容器選擇出對(duì)應(yīng)的HttpActionDescriptor.本節(jié)重點(diǎn)內(nèi)容就是介紹SelectAction方法.

HttpActionDescriptor

在介紹IHttpActionSelector前,我們需要了解HttpActionDescriptor

public abstract class HttpActionDescriptor{    //相關(guān)聯(lián)的HttpControllerDescriptor
    public HttpControllerDescriptor ControllerDescriptor { get; } { set; }    //Action名稱(通過使用ActionName特性,Action名稱可以和方法名稱不同)
    public abstract string ActionName { get; }    //Action返回值類型(void為null)
    public abstract Type ReturnType { get; }    //支持的HttpMethod(默認(rèn)一個(gè)Action方法支持一種HttpMethod,且默認(rèn)為Post Method,可以使用AcceptVerbs特性支持多個(gè))
    public virtual Collection<HttpMethod> SupportedHttpMethods { get; }    //Action方法所有參數(shù)
    public abstract Collection<HttpParameterDescriptor> Parameters { get; };
}

而HttpActionDescriptor默認(rèn)實(shí)現(xiàn)為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{    //選擇符合標(biāo)準(zhǔn)的Action
    HttpActionDescriptor SelectAction(HttpControllerContext controllerContext);    //獲取HttpController所有Action
    ILookup<string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor);
}

IHttpActionSelector默認(rèn)的實(shí)現(xiàn)為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會(huì)在請(qǐng)求每個(gè)HttpController第一次的時(shí)候 緩存當(dāng)前所有HttpActionDescriptor

當(dāng)我們找到HttpController下所有HttpActionDescriptor,還需要最后一步通過SelectAction篩選出最終的Action

由于這塊源碼稍微復(fù)雜,這里把關(guān)鍵的幾步及對(duì)應(yīng)的方法名說明下

public virtual HttpActionDescriptor SelectAction(HttpControllerContext controllerContext){    1. Action名稱過濾

    如果路由中配置Action變量,則會(huì)先過濾ActionName.調(diào)用方法GetInitialCandidateList    2. Http方法過濾

    對(duì)于Get Put Post默認(rèn)都通過,調(diào)用方法FindActionsForVerb    3. 必須的參數(shù)能綁定上

    在Action方法上的必須參數(shù),在路由參數(shù)和Request參數(shù)中能獲取到,調(diào)用方法FindActionMatchRequiredRouteAndQueryParameters    4. 參數(shù)個(gè)數(shù)符合最多的匹配

    當(dāng)多個(gè)Action方法都滿足以上3個(gè)條件時(shí),取最多參數(shù)符合的那個(gè),調(diào)用方法FindActionMatchMostRouteAndQueryParameters    5. 取唯一匹配
    最終匹配結(jié)果為1個(gè)Action方法則成功,多個(gè)或零失敗,在SelectAction方法本身調(diào)用
}

這里稍微舉個(gè)例子來解釋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)
}

分別請(qǐng)求如下地址,并列出對(duì)應(yīng)的匹配方法

  • 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表示請(qǐng)求成功,Erro表示請(qǐng)求失敗)