目錄 1. ApiController 2. HttpActionDescriptor 3. IHttpActionSelector ApiController 在上節中,講到如何選擇並激活對應的IHttpController,而一般我們在開發中使用的是ApiController 在ApiContr ...
目錄
ApiController
HttpActionDescriptor
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