您的位置:首页 > 编程语言 > ASP

ASP.NET Web API是如何根据请求选择Action的?[上篇]

2013-09-12 11:38 507 查看

ASP.NET Web API是如何根据请求选择Action的?[上篇]

[code] public interface IHttpActionSelector

{

ILookup<string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor);

HttpActionDescriptor SelectAction(HttpControllerContext controllerContext);

}

[/code]
[/code]
如上面的代码片断所示,IHttpActionSelector接口中定义了两个方法。GetActionMapping方法返回定义在指定HttpController中所有Action与其名称的映射。该方法唯一的参数表示用于描述目标HttpController的HttpControllerDescriptor对象,返回类型ILookup<string, HttpActionDescriptor>,其Key和Element分别表示Action的名称和用于描述Action方法的HttpActionDescriptor对象。

由于同一个HttpController类型中可以定义多个同名的Action方法重载,我们也可以通过ActionNameAttribute特性为多个Action方法指定相同的Action名称,所以多个Action方法可以共享相同的名称,所以GetActionMapping方法才会返回一个ILookup<string, HttpActionDescriptor>对象。

针对请求对目标Action的选择实现在SelectAction方法中,作为该方法唯一参数的是表示当前HttpController上下文的HttpControllerContext对象,我们可以从中获取表示当前请求的HttpRequestMessage对象和通过ASP.NET Web API路由系统生成的HttpRouteData。该方法返回的HttpActionDescriptor正是对最终用于处理当前请求的Action方法的描述。

ApiControllerActionSelector

与我们在前面介绍的众多“标准化组件”一样,ASP.NET Web API默认用于选择目标Action的HttpActionSelector也是注册在当前的ServicesContainer中,我们可以直接通过ServicesContainer具有如下定义的扩展方法GetActionSelector得到这个注册的HttpActionSelector。

[code]
[code] public static class ServicesExtensions

{

//其他成员

public static IHttpActionSelector GetActionSelector(this ServicesContainer services);

}

[/code]
[/code]
通过分析如下所示的DefaultServices构造函数的定义我们知道默认使用的HttpActionSelector是一个类型为ApiControllerActionSelector的对象。

[code]
[code] public class DefaultServices : ServicesContainer

{

// 其他成员

public DefaultServices(HttpConfiguration configuration)

{

//其他操作

this.SetSingle<IHttpActionSelector>(new ApiControllerActionSelector());

}

}

[/code]
[/code]
ApiControllerActionSelector定义在“System.Web.Http.Controllers”命名空间下,基本的成员定义如下所示。

[code]
[code] public class ApiControllerActionSelector : IHttpActionSelector

{   

public ApiControllerActionSelector();

public virtual ILookup<string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor);

public virtual HttpActionDescriptor SelectAction(HttpControllerContext controllerContext);

}

[/code]
[/code]

有效的Action方法

现在我们来着重讨论ApiControllerActionSelector的GetActionMapping方法的实现。实现逻辑其实很简单:它通过作为参数的HttpControllerDescriptor得到HttpController的真实类型,并调用GetMethods方法获得描述所有方法成员MethodInfo列表,然后从中筛选出所有“有效”的Action方法并以此创建ReflectedHttpActionDescriptor对象。那么一个有效的Action方法具有怎样的“资质”呢?

对于一个实现了IHttpController接口的HttpController类型来说,其拥有的所有MethodInfo必须同时满足如下的条件才会被视为有效的Action方法并被用于创建对应的HttpActionDescriptor对象:

必须是公有的实例方法。

MethodInfo的IsSpecialName属性值为False(表示属性成员Getter和Setter的MethodInfo不会被用于创建HttpActionDescriptor)。

从ApiController类型中继承的方法不是有效的Action方法

所有有效的MethodInfo被选择出来并连同指定的HttpControllerDescriptor一起生成一组ReflectedHttpActionDescriptor对象集合,它被转换成一个ILookup<string, HttpActionDescriptor>对象后直接作为GetActionMapping方法的返回值。如下的代码片断基本反映了GetActionMapping方法中最初生成HttpActionDescriptor与其Action名称映射的逻辑。

[code]
[code] public class ApiControllerActionSelector : IHttpActionSelector

{

//其他成员

public ILookup<string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor)

{

IEnumerable<ReflectedHttpActionDescriptor> actionDescriptor =

from method in controllerDescriptor.ControllerType.GetMethods()

where method.IsPublic && !method.IsStatic && !method.IsSpecialName&& !method.DeclaringType.IsAssignableFrom(typeof(ApiController))

select new ReflectedHttpActionDescriptor(controllerDescriptor,method);

return actionDescriptor.ToLookup(action => action.ActionName, action => (HttpActionDescriptor)action);

}    

}

[/code]
[/code]
为了避免频繁、重复地对类型的方法成员进行反射而影响性能,定义在上面代码片断的操作仅仅会在第一次调用GetActionMapping方法时被执行。生成的ReflectedHttpActionDescriptor会被缓存起来以服务于后续调用。

ActionMethodSelector

HttpActionSelector的终极目标是根据当前请求从目标HttpController中选择出正确的Action方法,这个目标实现在它的SelectAction方法中。在系统介绍实现在ApiControllerActionSelector的SelectAction方法中的Action选择机制之前,我们还需要了解另一个与之相关的对象:ActionMethodSelector。

ActionMethodSelector的目的在于判断某个Action方法是否与当前请求相匹配,它们均实现了具有如下定义的IActionMethodSelector接口。IActionMethodSelector仅仅是定义在程序集System.Web.Http.dll中的一个内部接口而已,它定义了一个唯一的方法IsValidForRequest,方法的两个参数分别是表示当前 HttpController上下文的HttpControllerContext对象和代码目标Action方法的MethodInfo。执行该方法得到的布尔值表明目标Action方法是否能够用于处理当前请求。

[code]
[code] internal interface IActionMethodSelector

{

bool IsValidForRequest(HttpControllerContext controllerContext, MethodInfo methodInfo);

}

[/code]
[/code]
ASP.NET Web API仅仅定义了唯一一个实现了这个IActionMethodSelector接口的类型,它就是具有如下定义的NonActionAttribute。顾名思义,如果在某个方法上应用了这个特性,意味着目标方法不是一个合法的Action方法。从给出的代码片断可以看出NonActionAttribute直接在实现的IsValidForRequest方法中返回False。

[code]
[code] [AttributeUsage(AttributeTargets.Method, AllowMultiple=false, Inherited=true)]

public sealed class NonActionAttribute : Attribute, IActionMethodSelector

{

bool IActionMethodSelector.IsValidForRequest(HttpControllerContext controllerContext, MethodInfo methodInfo)

{

return false;

}

}

[/code]
[/code]
对于NonActionAttribute特性来说,有一点值得强调:虽然应用了该特性的方法被认为不再是一个合法的Action方法,但是ApiControllerActionSelector的GetActionMapping方法并不会使用它来判断Action方法的有效性。

在《下篇》中我们将以实例的形式讨论该主题最为核心的内容:ASP.NET Web API如何利用HttpActionSelector在目标HttpController成功激活之后如何从中选择出匹配的Action方法来处理当前的请求的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: