2011-09-27 3 views
7

Utilizzando i locator di metodo predefiniti in ASP.NET MVC (3 o 4DP), esiste un modo per differenziare la struttura MVC da una stringa a Guid senza richiedere analizzare il parametro nell'azione del controller?Differenzia guida e parametri stringa in MVC 3

Esempi di utilizzo sarebbero per l'URL

http://[domain]/customer/details/F325A917-04F4-4562-B104-AF193C41FA78

per eseguire il metodo

public ActionResult Details(Guid guid) 

e

http://[domain]/customer/details/bill-gates

per eseguire il

public ActionResult Details(string id) 
metodo

.

senza modifiche, ovviamente, i metodi sono ambigue, come segue:

public ActionResult Details(Guid id) 
{ 
    var model = Context.GetData(id); 
    return View(model); 
} 

public ActionResult Details(string id) 
{ 
    var model = Context.GetData(id); 
    return View(model); 
} 

causando l'errore:

The current request for action 'Details' on controller type 'DataController' is ambiguous between the following action methods: 
System.Web.Mvc.ActionResult Details(System.Guid) on type Example.Web.Controllers.DataController 
System.Web.Mvc.ActionResult Details(System.String) on type Example.Web.Controllers.DataController 

ho cercato di utilizzare un vincolo personalizzato (in base a How can I create a route constraint of type System.Guid?) per provare e inviarlo tramite instradamento:

routes.MapRoute(
    "Guid", 
    "{controller}/{action}/{guid}", 
    new { controller = "Home", action = "Index" }, 
    new { guid = new GuidConstraint() } 
); 

routes.MapRoute(
    "Default", // Route name 
    "{controller}/{action}/{id}", 
    new { controller = "Home", action = "Index", id = UrlParameter.Optional } 
); 

E acceso firme azione al:

public ActionResult Details(Guid guid) 
{ 
    var model = Context.GetData(guid); 
    return View(model); 
} 

public ActionResult Details(string id) 
{ 
    var model = Context.GetData(id); 
    return View(model); 
} 

Il vincolo esegue e passa, quindi l'argomento viene inviato ad un'azione, ma apparentemente ancora come una stringa, e quindi ambiguo ai due firme dei metodi. Mi aspetto che ci sia qualcosa nel modo in cui sono posizionati i metodi di azione che causa l'ambiguità, e quindi potrebbe essere sovrascritto collegando un modulo personalizzato per individuare i metodi.

Lo stesso risultato potrebbe essere ottenuto analizzando il parametro stringa, ma sarebbe davvero bello per brevità evitare quella logica nell'azione (per non parlare di riutilizzare, eventualmente, un giorno più tardi).

+0

Hai ragione, il metodo di azione Locator è ignaro del vincolo percorso. Se rimuovi il metodo di azione della stringa, viene scelto il metodo di guida? – bzlm

+1

MVC non supporta l'overloading dei metodi basato esclusivamente sulla firma: la soluzione più semplice per voi sarebbe probabilmente quella di avere due metodi di azione denominati in modo univoco, uno per i dettagli tramite GUID (Dettagli) e un altro per ottenere i dettagli per nome (Ricerca o Informazioni forse ?). – Tommy

+0

@bzlm - Correggere, rimuovendo l'azione della stringa si seleziona il Guid (supponendo che passi il vincolo OR possa essere analizzato in un Guid). – falquan

risposta

11

In primo luogo, è necessario disambigute i metodi dando loro due nomi diversi:

public ActionResult DetailsGuid(Guid guid) 
{ 
    var model = Context.GetData(guid); 
    return View(model); 
} 

public ActionResult DetailsString(string id) 
{ 
    var model = Context.GetData(id); 
    return View(model); 
} 

Successivamente, è necessario un gestore percorso personalizzato per ispezionare la richiesta, e cambiare il nome del metodo di conseguenza:

using System.Web.Mvc; 
using System.Web.Routing; 

public class MyRouteHandler : IRouteHandler 
{ 
    public IHttpHandler GetHttpHandler(RequestContext requestContext) 
    { 
     var routeData = requestContext.RouteData; 
     var stringValue = routeData.Values["id"].ToString(); 
     Guid guidValue; 
     var action = routeData.Values["action"]; 
     if (Guid.TryParse(stringValue, out guidValue) && (guidValue != Guid.Empty); 
      routeData.Values["action"] = action + "Guid"; 

     else 
      routeData.Values["action"] = action + "String"; 

     var handler = new MvcHandler(requestContext); 
     return handler; 
    } 
} 

Infine, aggiungere un percorso Details in cima tuoi percorsi, come segue:

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

Quando una richiesta arriva per i dettagli, la rotta Details utilizzerà il gestore di route personalizzato per ispezionare il token id. Il gestore di route aggiunge al nome dell'azione in base al modulo del token ID, in modo che la richiesta venga indirizzata all'azione appropriata.

8

La mia opinione è che l'utilizzo del selettore del metodo di azione sia più utilizzabile e meno codificante.

public class GuidMethodSelectorAttribute : ActionMethodSelectorAttribute 
{ 
    public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo) 
    { 
     var idStr = controllerContext.RouteData.Values["id"]; 
     if (idStr == null) 
      return false; 
     Guid a; 
     var result = Guid.TryParse(idStr.ToString(), out a); 
     return result; 
    } 
} 

Questo selettore controlla la richiesta del parametro ID. Se è guid, restituisce true. Quindi, per utilizzarlo:

public class HomeController : Controller 
{ 
    [GuidMethodSelector] 
    public ActionResult Index(Guid id) 
    { 
     return View(); 
    } 
    public ActionResult Index(string id) 
    { 
     return View(); 
    } 
} 
+0

È possibile utilizzare la classe GuidRouteConstraint incorporata anziché dover aggiungere un codice personalizzato. Apprezzo che questo potrebbe non essere stato il caso al momento della scrittura. –

5

Se si sta ancora registrando percorsi in questo modo, allora la() della classe GuidRouteConstraint è stata aggiunta in una versione più recente di MVC e dovrebbe essere utilizzato al posto di un'implementazione personalizzata:

public override void RegisterArea(AreaRegistrationContext context) 
{ 
    context.MapRoute(
     "Guid", 
     "{controller}/{action}/{guid}", 
     new { controller = "Home", action = "Index" }, 
     new { guid = new GuidRouteConstraint() } 
    ); 
} 

allora si può semplicemente creare il risultato azione come:

public class HomeController : Controller { 
    public ActionResult Index(Guid guid) { 
    } 
} 
+2

Ha funzionato a meraviglia, assicurati solo di usare la giusta istruzione 'using' - probabilmente vorrai' usando System.Web.Mvc.Routing.Constraints; 'e non System.Web.Http.Routing.Constraints –