2011-12-03 19 views
8

Ho uno sfondo nello sviluppo di software desktop e sto iniziando con l'apprendimento di ASP.NET MVC.String magiche in ASP.NET MVC

Nel mio HomeController predefinita ho l'azione Index, che ha il codice che assomiglia a questo:

if (!Request.IsAuthenticated) 
    return RedirectToAction("Login", "Account"); 

In altre parole, reindirizzare l'utente a "/ account/login". L'azione AccountController.Login gestirà quindi l'utente e lo rimanderà a HomeController una volta che avrà effettuato l'accesso con successo.

Questo codice mi odora, forse solo perché sono abituato a fare le cose in modo diverso nei software desktop. Cosa succede se cambio il nome dell'azione di login su "LogOn"? Cosa succede se rimuovo completamente AccountController e lo sostituisco con qualcos'altro? Introdurrò un nuovo bug ma non otterrò errori del compilatore, e probabilmente anche i miei test di unità non lo prenderanno. Poiché ho usato le stringhe per specificare i nomi di controller e di azione, il refactoring e la riprogettazione hanno più potenziale per violare il codice dappertutto.

Quello che vorrei è qualcosa di simile:

if (!Request.IsAuthenticated) 
    return RedirectToAction(() => AccountController.Login); 

Comunque io non sono sicuro se questo è ancora possibile o se è il modo migliore per farlo.

Sono stupido o altre persone hanno avuto lo stesso problema? Cosa fai per aggirarlo?

risposta

20

Penso che quello che stai cercando è il motivo per cui T4MVC esiste - rimuove tutte le "corde magiche" relative ai controllori e alle azioni e li sostituisce con le classi e le proprietà.

Con T4MVC, questo

if (!Request.IsAuthenticated) 
    return RedirectToAction("Login", "Account"); 

diventa questo

if (!Request.IsAuthenticated) 
    return RedirectToAction(MVC.Account.Login()); 

c'è una bandiera che può essere impostato nelle impostazioni T4MVC per costringerlo a eseguire il modello in ogni generazione, dando inizio avvertimento quando qualcosa potrebbe essere cambiato.

Sebbene non sia quello che hai chiesto, puoi prendere in considerazione l'utilizzo di AuthorizeAttribute per rimuovere la necessità di controllare se la richiesta è autenticata all'interno dell'azione del controller.

questo

public class HomeController : Controller 
{ 
    public ActionResult Index() 
    { 
     if (!Request.IsAuthenticated) 
      return RedirectToAction("Login", "Account"); 

     // .... carry on 
    } 
} 

diventa

public class HomeController : Controller 
{ 
    [Authorize] 
    public ActionResult Index() 
    { 
     // .... carry on 
    } 
} 

poi nel web.config, impostare l'URL per puntare alla URL account di accesso

<authentication mode="Forms"> 
    <forms loginUrl="account/login" timeout="30" /> 
</authentication> 

Certo, questo non ti dà alcun sicurezza se i tuoi controllori e le tue azioni cambiano (simile al tuo reclamo originale), ma puoi sempre s impostare un percorso per indirizzare l'URL scelto al controller e all'azione corretti e utilizzare le classi generate da T4MVC nel percorso, fornendo un avviso di tempo di compilazione se le cose sono cambiate.

+1

Wow che è così utile. Spero/spero che lo integrino direttamente in MVC in futuro. Non vedo come usare così tante stringhe magiche possa essere accettabile in questi giorni. – DLeh

3

So che non è davvero una risposta, ma l'utilizzo di uno strumento come Resharper può aiutare a risolvere anche questo.Resharper tiene traccia dei controller e delle azioni ed emette un avviso se una stringa magica che dovrebbe essere un controller non lo è. Funziona solo su metodi standard come RedirectToAction o ActionLink.

Modifica: A quanto pare è possibile aggiungere annotazioni per farlo funzionare con i metodi di estensione personalizzati. Vedi here.

1

La risposta di Russ è giusta, ma in realtà non risponde alla tua preoccupazione .. che è davvero "Perché ci sono stringhe magiche in MVC?".

Nelle prime versioni di MVC, era in realtà piuttosto diverso. Non usavano le stringhe magiche e avevano più metodi basati sui tipi. Tuttavia, questo è stato modificato per una serie di motivi. Ho dimenticato i dettagli, ma c'erano ragioni logiche per questo altrimenti strano ritorno ai metodi non tipografici.

Mi sembra di ricordare che potrebbe aver avuto qualcosa a che fare con il modo in cui MVC cerca più aree per le convenzioni di corrispondenza, e questo è stato molto più facile da ottenere con le stringhe rispetto agli oggetti digitati. Questo è particolarmente vero per Views e partial. .

Forse qualcuno che ricorda i dettagli possono carillon

0

In alternativa al T4MVC, ho scritto un piccolo supporto che consente di utilizzare più o meno esattamente la sintassi suggerita e senza la necessità di codice generato automaticamente:

<a href="@(Url.To<MyController>().MyAction("foo", 42))">link</a> 

E nel controller:

return Redirect(Url.To<MyController>().MyAction("foo", 42).ToString()); 

la mia soluzione funziona facendo in fase di esecuzione quanto T4MVC fa al momento della compilazione.

C'è un NuGet package e un GitHub project.

4

È possibile scrivere i propri metodi di estensione personalizzati per evitare stringhe magiche. Ad esempio è possibile vedere la mia implementazione qui: https://github.com/ivaylokenov/ASP.NET-MVC-Lambda-Expression-Helpers Ricordare che questo aggiunge un po 'di tempo di esecuzione delle prestazioni generali che è possibile risolvere memorizzando nella cache tutti i collegamenti. Se si desidera controllare il tempo di compilazione T4MVC è la soluzione: http://t4mvc.codeplex.com/

3

In C# 6 è possibile utilizzare nameof e facilmente refactoring molte di queste stringhe magiche.

... = new SelectList(context.Set<User>(), nameof(User.UserId), nameof(User.UserName)); 

... 
return RedirectToAction(nameof(Index)); 
0

Se sei veramente solo preoccupato per il reindirizzamento ad azioni e controller, il C# 6 nameof operatore è veramente utile - fino a quando è necessario reindirizzare tra i controller. Ho finito per scrivere un metodo di utilità per ottenere il nome del controller:

public class Utilities 
{ 
    public static string ControllerName<T>() where T : Controller 
    { 
     var name = typeof(T).Name; 
     return name.Substring(0, Math.Max(name.LastIndexOf(nameof(Controller), 
              StringComparison.CurrentCultureIgnoreCase), 0)); 
    } 
} 

e si può utilizzare in questo modo:

public ActionResult Foo() 
{ 
    return RedirectToAction(nameof(HomeController.Index), 
          Utilities.ControllerName<HomeController>()); 
}