2012-03-04 10 views
7

Sto lavorando a una soluzione ASP.NET MVC con un numero di menu diversi. Il menu da visualizzare dipende dal ruolo dell'utente attualmente connesso.Come reindirizzare l'azione MVC senza restituire 301? (utilizzando MVC 4 beta)

In MVC 3 avevo un codice personalizzato per supportare questo scenario, avendo un unico metodo di controllo che restituirebbe il menu corretto. Lo farebbe differendo la richiesta al controller appropriato e l'azione in base all'utente corrente.

Questo codice sembra essere danneggiato in MVC 4 e sto cercando aiuto per risolverlo.

In primo luogo, ho aggiunto una classe TransferResult di supporto per eseguire il reindirizzamento:

public class TransferResult : RedirectResult 
{ 
    #region Transfer to URL 
    public TransferResult(string url) : base(url) 
    { 
    } 
    #endregion 

    #region Transfer using RouteValues 
    public TransferResult(object routeValues) : base(GetRouteUrl(routeValues)) 
    { 
    } 

    private static string GetRouteUrl(object routeValues) 
    { 
     var url = new UrlHelper(new RequestContext(new HttpContextWrapper(HttpContext.Current), new RouteData()), RouteTable.Routes); 
     return url.RouteUrl(routeValues); 
    } 
    #endregion 

    #region Transfer using ActionResult (T4MVC only) 
    public TransferResult(ActionResult result) : base(GetRouteUrl(result.GetT4MVCResult())) 
    { 
    } 

    private static string GetRouteUrl(IT4MVCActionResult result) 
    { 
     var url = new UrlHelper(new RequestContext(new HttpContextWrapper(HttpContext.Current), new RouteData()), RouteTable.Routes); 
     return url.RouteUrl(result.RouteValueDictionary); 
    } 
    #endregion 

    public override void ExecuteResult(ControllerContext context) 
    { 
     HttpContext httpContext = HttpContext.Current; 
     httpContext.RewritePath(Url, false); 
     IHttpHandler httpHandler = new MvcHttpHandler(); 
     httpHandler.ProcessRequest(HttpContext.Current); 
    } 
} 

In secondo luogo, ho modificato T4MVC per emettere alcuni metodi di controllo di supporto, con conseguente ogni controller di avere questo metodo:

protected TransferResult Transfer(ActionResult result) 
{ 
    return new TransferResult(result); 
} 

Questo mi ha permesso di avere un'azione del controller condiviso per restituire un menu, senza dover confondere le viste con qualsiasi logica condizionale:

public virtual ActionResult Menu() 
{ 
    if(Principal.IsInRole(Roles.Administrator)) 
     return Transfer(MVC.Admin.Actions.Menu()); 
    return View(MVC.Home.Views.Partials.Menu); 
} 

Tuttavia, il codice in ExecuteResult nella classe TransferResult non sembra funzionare con l'attuale versione di anteprima di MVC 4. Mi dà il seguente errore (indicando la linea "httpHandler.ProcessRequest"):

'HttpContext.SetSessionStateBehavior' can only be invoked before 
'HttpApplication.AcquireRequestState' event is raised. 

Qualche idea su come risolvere questo problema?

PS: Mi rendo conto che potrei ottenere lo stesso utilizzando una semplice estensione HtmlHelper, che è quello che sto attualmente utilizzando come soluzione alternativa. Tuttavia, ho molti altri scenari in cui questo metodo mi ha permesso di mescolare e riutilizzare le azioni, e odio rinunciare a questa flessibilità quando mi trasferisco su MVC 4.

+0

Si dovrebbe mettere questo metodo in una base di classe' Controller', non in tutti i controller di – SLaks

+0

@SLaks Questo sembra essere un micro-ottimizzazione, dato che il codice è generato da T4MVC ed è solo un one-liner. Inoltre non fa nulla per aiutare a risolvere il problema;) –

+0

Deve essere un giorno lento, non può nemmeno ottenere un upvote: o) –

risposta

5

A volte penso che "MVC" debba essere chiamato "RCMV" per "Router Controller Model View" poiché è davvero l'ordine in cui le cose accadono. Inoltre, poiché è solo "MVC", le persone tendono sempre a dimenticare il routing. Il bello di MVC è che il routing è configurabile ed estensibile. Credo che quello che stai cercando di fare possa essere risolto con un gestore di percorsi personalizzato.

non ho ancora testato questo, ma si dovrebbe essere in grado di fare qualcosa del genere:

routes.Add(
    new Route(
     "{controller}/{action}/{id}", 
     new RouteValueDictionary(new { controller = "Home", action = "Menu" }), 
     new MyRouteHandler(Roles.Administrator, new { controller = "Admin" }))); 

Allora il vostro gestore di percorso sarà simile a questa:

public class MyRouteHandler : IRouteHandler 
{ 
    public string Role { get; set; } 

    public object RouteValues { get; set; } 

    public MyRouteHandler(string role, object routeValues) 
    { 
     Role = role; 
     RouteValues = routeValues; 
    } 

    public IHttpHandler GetHttpHandler(RequestContext requestContext) 
    { 
     return new MyHttpHandler(Role, RouteValues); 
    } 
} 

E infine gestire la ri -routing nel vostro HttpHandler:

public class MyHttpHandler : IHttpHandler 
{ 
    public string Role { get; set; } 

    public object RouteValues { get; set; } 

    public MyHttpHandler(string role, object routeValues) 
    { 
     Role = role; 
     RouteValues = routeValues; 
    } 

    public void ProcessRequest(HttpContext httpContext) 
    { 
     if (httpContext.User.IsInRole(Role)) 
     { 
      RouteValueDictionary routeValues = new RouteValueDictionary(RouteValues); 

      // put logic here to create path similar to what you were doing 
      // before but you will need to replace any keys in your route 
      // with the values from the dictionary created above. 

      httpContext.RewritePath(path); 
     } 

     IHttpHandler handler = new MvcHttpHandler(); 
     handler.ProcessRequest(httpContext); 
    } 
} 

che non può essere corretta al 100%, ma dovrebbe farti in d destra irection in un modo che non dovrebbe imbattersi in nulla deprecato in MVC4.

+0

+1 per venire con una soluzione Tuttavia, il vecchio codice forniva un'unica soluzione condivisa che potevo usare nei miei metodi di controller secondo le necessità, mentre ciò richiede un codice personalizzato per ogni uso. Preferirei una soluzione più generica se ne esiste una –

+0

Risposta aggiornata essere più generico Un altro vantaggio di questa soluzione è che può essere inserito in un re libreria utilizzabile (supponendo che i nomi dei ruoli possano essere inseriti in tale libreria) e quindi è possibile utilizzarli su più app MVC. Il tuo metodo statico era interno alla tua app. –

+0

Con la vecchia soluzione avevo una libreria di classi con i bit condivisi e un modello T4MVC.tt modificato per generare i metodi del controller, ma lo includerei sempre comunque. Per quanto posso dire, questo funziona solo per i percorsi che configuro manualmente per abilitare questa sostituzione, specificando il gestore personalizzato. O mi sta sfuggendo qualcosa? –

1

Penso che un TransferResult debba essere incluso nel framework senza che ogni sviluppatore debba lottare con il reimplementarlo per versioni diverse quando si rompe. (come in questo thread e ad esempio anche il seguente thread: Implementing TransferResult in MVC 3 RC - does not work).

Se siete d'accordo con me, vorrei solo incoraggiarvi a votare per "Server.Trasferimento" per diventare inclusi nel framework MVC per sé:. http://aspnetwebstack.codeplex.com/workitem/798