2010-04-22 3 views
10

Come posso ottenere l'azione del controller (metodo) e il tipo di controller che verrà chiamato, dato lo System.Web.Routing.RouteData?Come posso ottenere il tipo di controller e le informazioni sull'azione da un url o dai dati del percorso?

Il mio scenario è questo: voglio essere in grado di eseguire determinate azioni (o meno) nel metodo OnActionExecuting per un'azione.

Tuttavia, spesso desidero non conoscere l'azione corrente, ma l'azione "root" viene chiamata; con questo intendo che potrei avere una vista chiamata "Login", che è la mia pagina di accesso. Questa vista può includere un'altra vista parziale "LeftNav". Quando si chiama OnActionExecuting per LeftNav, voglio essere in grado di determinare che è realmente chiamato per l'aciton "root" di Login.

Mi rendo conto che chiamando il numero RouteTable.Routes.GetRouteData(actionExecutingContext.HttpContext), posso ottenere il percorso per la richiesta "root", ma come trasformarlo nel metodo e digitare informazioni?

L'unica soluzione che ho finora, è qualcosa di simile:

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

Il problema di questo è che "routeController" è il nome del controller con il suffisso "Controller" rimosso, e non è completo; cioè è "Login", piuttosto che "MyCode.Website.LoginController".

Preferisco di gran lunga ottenere un effettivo Type e MethodInfo se possibile, o almeno un nome di tipo completo.

Qualche idea o approccio alternativo?

[EDIT - questo è ASP.Net MVC 1.0]

risposta

4
protected override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
    var type1 = filterContext.Controller.GetType(); 
    var type2 = filterContext.ActionDescriptor 
        .ControllerDescriptor.ControllerType; 
    } 

OK, mi dispiace, ho perso la parte "root".

Quindi, in un altro modo, è possibile salvare il tipo di controller nella memoria thread. Pseudocodice:

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

Solo un'idea. Sono sicuro che l'archiviazione locale dei thread è disponibile in C#. L'idea chiave qui è che la salvi solo per la prima richiesta, quindi è sempre il root controller.

+0

Sfortunatamente, questo non funzionerà poiché restituisce il tipo di azione * current * del controller, non l'azione del controller "root" nell'esempio che ho dato. Il motivo per cui chiamo GetRouteData (actionExecutingContext.HttpContext) è perché questo mi fornisce correttamente il percorso per la "root", ma non posso quindi tradurlo nel tipo e nel metodo del controller. –

+0

Questo è un suggerimento molto interessante - grazie a queen3 –

+1

Non utilizzare lo storage ThreadLocal da un'applicazione ASP.NET. Le richieste ASP.NET possono passare da un thread all'altro, quindi l'archiviazione ThreadLocal può scomparire quando non lo si aspetta. Utilizzare invece HttpContext.Items se è necessario archiviare le informazioni che rimangono invariate solo per la richiesta corrente. – Levi

1
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); 
} 

Stai pensando di un'implementazione simile a questo? Alcuni Try/Catches richiesti !!!

Gentilezza,

Dan

+0

Grazie Dan - che è un pò dove sono diretto, ma in realtà sperando di evitare. Da qualche parte, MVC sa esattamente quale metodo, su quale tipo, chiamerà.Il problema con questo approccio (e il mio) è che sto aggiungendo il suffisso "Controller", e dovrò anche anteporre lo spazio dei nomi al tipo, ecc, ecc. Probabilmente funzionerebbe, ma se posso collegarmi quello che MVC sta già facendo, preferirei farlo in quel modo. –

4

Ecco la soluzione ho compilato da varie fonti. Il variabile URL deve contenere l'URL dell'azione:

 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); 
     }