使用属性路由时如何获得Mvc 5 Controller的ActionDescriptor?

时间:2014-03-03 02:41:25

标签: c# asp.net-mvc asp.net-mvc-routing asp.net-mvc-5 attributerouting

我试图在使用属性路由的控制器上获取 ActionDescriptor ,但它始终为null。

var controllerDescriptor = new ReflectedControllerDescriptor(controllerType);
var actionDescriptor = 
    controllerDescriptor.FindAction(controllerContext, actionName) ??
    controllerDescriptor.GetCanonicalActions().FirstOrDefault(a => a.ActionName == actionName);

从我的研究中,我发现在类 ActionMethodSelectorBase 中有一个名为 PopulateLookupTables 的方法,它会拆分你给它的控制器中的所有方法。在其中的方法中,它将MethodInfo的列表过滤为2组列表。

  • AliasedMethods - 所有没有使用ActionNameSelectorAttribute修饰的直接路由的操作方法。
  • NonAliasedMethods - 所有没有使用ActionNameSelectorAttribute修饰的直接路由的操作方法。

注意:如果在控制器级别设置了直接路由(RouteAttribute),则AliasedMethods和NonAliasedMethods将为空。

注意:直接路由被定义为控制器中的方法(不包括构造函数和事件),并使用继承自IRouteInfoProvider或IDirectRouteFactory的属性进行修饰(RouteAttribute继承自这两者)。

  • DirectRouteMethods - 使用某种形式的IRouteInfoProvider直接装饰它们的方法。
  • StandardRouteMethods - 没有IRouteInfoProvider直接装饰它们的方法。 (包括带有RouteAttribute的控制器中的操作方法,但该方法没有RouteAttribute)。

当调用 ReflectedControllerDescriptor.FindAction 时,它会在内部调用 ActionMethodSelectorBase.FindActionMethods ,它只会查看 AliasedMethods NonAliasedMethods (不包括直接路由的所有操作)。

当调用 ReflectedControllerDescriptor.GetCanonicalActions 时,它会在内部调用 ReflectedControllerDescriptor.GetAllActionMethodsFromSelector ,它只会查看 AliasedMethods NonAliasedMethods (排除直接路线的所有行动)。

从我所看到的, DirectRouteMethods 仅在一个地方使用,即 RouteCollection.MapMvcAttributeRoutes 扩展方法。这意味着 RouteTable.Routes 集合对操作有一个 RouteCollectionRoute ,但我不知道如何进行操作。

有没有人知道如何为 RouteAttribute

的操作获取 ActionDescriptor

1 个答案:

答案 0 :(得分:1)

您可以使用AsyncControllerActionInvoker类来执行此操作,但由于您需要的方法受到保护,因此您需要先继承该类并将该方法重新定义为public。

private class ActionSelector 
    : AsyncControllerActionInvoker
{
    // Needed because FindAction is protected, and we are changing it to be public
    public new ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
    {
        return base.FindAction(controllerContext, controllerDescriptor, actionName);
    }
}

然后,只需使用您之前使用的相同2个对象来调用新方法。

var controllerDescriptor = new ReflectedControllerDescriptor(controllerType);
var actionSelector = new ActionSelector();
var actionDescriptor = 
    actionSelector.FindAction(controllerContext, controllerDescriptor, actionName) ??
    controllerDescriptor.GetCanonicalActions().FirstOrDefault(a => a.ActionName == actionName);

请注意,这适用于StandardRouteMethods和DirectRouteMethods。

但是,有一点需要注意的是,调用此方法会将controllerContext.RouteData和controllerContext.RequestContext.RouteData属性的RouteData重置为找到的操作的属性。因此,您需要包装ControllerContext和RequestContext类并重新实现RouteData属性,这样如果重要的是不破坏上下文,则setter无效。

参考:https://aspnetwebstack.codeplex.com/workitem/1989