【问题标题】:How can I get controller type and action info from a url or from route data?如何从 url 或路由数据中获取控制器类型和操作信息?
【发布时间】:2011-02-11 01:23:01
【问题描述】:

在给定System.Web.Routing.RouteData 的情况下,如何获取将要调用的控制器操作(方法)和控制器类型?

我的场景是这样的 - 我希望能够在 OnActionExecuting 方法中执行某些操作(或不执行)。

然而,我经常想知道的不是当前动作,而是被调用的“根”动作;我的意思是我可能有一个名为“登录”的视图,这是我的登录页面。此视图可能包括 另一个局部视图“LeftNav”。当为 LeftNav 调用 OnActionExecuting 时,我希望能够确定它确实是为 Login 的“根”动作调用的。

我意识到通过调用RouteTable.Routes.GetRouteData(actionExecutingContext.HttpContext),我可以获得“root”请求的路由,但是如何将其变成 方法和类型信息?

到目前为止,我唯一的解决方案是:

 var routeData = RouteTable.Routes.GetRouteData(actionExecutingContext.HttpContext)
 var routeController = (string)routeData.Values["controller"]; 
 var routeAction = (string)routeData.Values["action"];

这样做的问题是“routeController”是去掉了“Controller”后缀的控制器名称,并且不是完全限定的;即它是“登录”,而不是“MyCode.Website.LoginController”。

如果可能的话,我宁愿得到一个实际的 TypeMethodInfo,或者至少是一个完全限定的类型名称。

有什么想法或替代方法吗?

[编辑 - 这是 ASP.Net MVC 1.0]

【问题讨论】:

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


    【解决方案1】:
      protected override void OnActionExecuting(ActionExecutingContext filterContext)
      {
         var type1 = filterContext.Controller.GetType();
         var type2 = filterContext.ActionDescriptor
                        .ControllerDescriptor.ControllerType;
      }
    

    好的,抱歉,我错过了“根”部分。

    然后,另一种方式,您可以将控制器类型保存到线程存储中。伪代码:

      protected override void OnActionExecuting(ActionExecutingContext filterContext)
      {
         if (!Thread.LocalStorage.Contains("root_controller"))
            Thread.LocalStorage["root_controller"] = 
                filterContext.ActionDescriptor
                        .ControllerDescriptor.ControllerType;
      }
    

    只是一个想法。我确定线程本地存储在 C# 中可用。这里的关键思想是您只为第一次请求保存它,因此它始终是根控制器。

    【讨论】:

    • 不幸的是,这不起作用,因为它返回 current 控制器动作的类型,而不是我给出的示例中的“根”控制器动作。我之所以调用 GetRouteData(actionExecutingContext.HttpContext) 是因为这确实成功地为我提供了“根”的路由,但我无法将其转换为控制器类型和方法。
    • 不要在 ASP.NET 应用程序中使用 ThreadLocal 存储。 ASP.NET 请求可以在线程之间来回跳转,因此 ThreadLocal 存储可能会在您意想不到的时候消失。如果您需要存储仅为当前请求保留的信息,请改用 HttpContext.Items。
    • 实际上我只是不知道有任何 ASP.NET 集合仅存储当前请求的数据。当然它比线程存储更好。感谢您的提示。
    【解决方案2】:

    这是我从各种来源编译的解决方案。 url 变量应包含操作的 URL:

            url = "YOUR URL";
            // Original path is stored and will be rewritten in the end
            var httpContext = new HttpContextWrapper(HttpContext.Current);
            string originalPath = httpContext.Request.Path;
    
            try
            {
                // Fake a request to the supplied URL into the routing system
                httpContext.RewritePath(url);
                RouteData urlRouteData = RouteTable.Routes.GetRouteData(httpContext);
    
                // If the route data was not found (e.g url leads to another site) then authorization is denied.
                // If you want to have a navigation to a different site, don't use AuthorizationMenu
                if(urlRouteData != null)
                {
                    string controllerName = urlRouteData.Values["controller"].ToString();
                    string actionName = urlRouteData.Values["action"].ToString();
    
                    // Get an instance of the controller that would handle this route
                    var requestContext = new RequestContext(httpContext, urlRouteData);
                    var controllerFactory = ControllerBuilder.Current.GetControllerFactory();
                    var controller = (ControllerBase) controllerFactory.CreateController(requestContext, controllerName);
    
                    // Find the action descriptor
                    var controllerContext = new ControllerContext(httpContext, new RouteData(), controller);
                    var controllerDescriptor = new ReflectedControllerDescriptor(controller.GetType());
                    var actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName);
                }
            }
            finally
            {
                // Reset our request path.
                httpContext.RewritePath(originalPath);
            }
    

    【讨论】:

      【解决方案3】:
      public Type ControllerType(string controllerName)
      {
         var fullName = controllerName + "Controller";
         var assemblyName = Assembly.GetExecutingAssembly().FullName;
         return Activator.CreateInstance(assemblyName, fullTypeName).GetType();
      }
      
      public MethodInfo ActionMethodInfo(string actionName, Type controllerType)
      {
         return controllerType.GetMethod(actionName);
      }
      

      您是否正在考虑类似的实现?需要一些 Try/Catches!

      【讨论】:

      • 谢谢丹 - 这有点像我要去的地方,但真的希望避免。在某个地方,MVC 确切地知道它将调用什么方法、什么类型。这种方法(和我的)的问题是我必须附加后缀“Controller”,并且还必须将命名空间添加到类型等之前。它可能会工作,但如果我可以挂钩MVC 已经在做的事情,我宁愿那样做。
      • GetMethod() 将抛出 AmbiguousMatchException 如果有多个同名方法,这是一种非常常见的模式。通常,您还必须考虑与 RouteData 中的其他值相关的匹配项,以便选择正确的操作方法。
      【解决方案4】:

      MvcSiteMapProvider 执行此操作。这是这个特殊事物的代码。

      Here is the code

      【讨论】:

      • 如何定位Action方法?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-05
      • 1970-01-01
      • 2012-11-17
      • 1970-01-01
      相关资源
      最近更新 更多