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)求失敗)