10

我正在尝试为使用Attribute Routing的控制器上的操作获取ActionDescriptor,但它始终为空。

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只查看AliasedMethodsNonAliasedMethods(排除所有具有直接路由的操作)。

ReflectedControllerDescriptor.GetCanonicalActions被调用时,它会在内部调用ReflectedControllerDescriptor.GetAllActionMethodsFromSelector ,它只查看AliasedMethodsNonAliasedMethods(排除所有具有直接路由的操作)。

据我所见,DirectRouteMethods仅在一个地方使用,即RouteCollection.MapMvcAttributeRoutes扩展方法。这意味着RouteTable.Routes集合有一个RouteCollectionRoute到该操作,但我不知道如何获得它。

有谁知道如何为具有RouteAttribute的操作获取ActionDescriptor

4

1 回答 1

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

于 2014-06-04T18:04:17.687 回答