2011-09-27 14 views
7

Utilizando los localizadores de métodos listos para usar en ASP.NET MVC (3 o 4DP), ¿hay alguna manera de que el marco MVC diferencie entre una cadena y Guid sin necesidad para analizar el parámetro en la acción del controlador?Diferenciar Guid y parámetros de cadena en MVC 3

Ejemplos de uso serían para la dirección URL

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

para ejecutar el método

public ActionResult Details(Guid guid) 

y

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

para ejecutar el

public ActionResult Details(string id) 

método.

Sin cambios, obviamente, los métodos son ambiguos, de la siguiente manera:

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

que produce el error:

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 

he tratado de usar una limitación personalizado (basado en How can I create a route constraint of type System.Guid?) para tratar y empujarlo a través de enrutamiento:

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

Y cambió el firmas de acción a:

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

La restricción se ejecuta y se pasa, por tanto, el argumento se envía a una acción, pero al parecer todavía como una cadena, y por lo tanto ambiguo a las dos firmas de métodos. Espero que haya algo en cómo se ubican los métodos de acción que causa la ambigüedad y, por lo tanto, se puede anular al conectar un módulo personalizado para buscar métodos.

Se podría obtener el mismo resultado analizando el parámetro de cadena, pero sería muy agradable por brevedad para evitar esa lógica en la acción (sin mencionar que con suerte volverá a usarse algún día más tarde).

+0

tienes razón, el localizador método de acción es ajeno a la restricción de ruta. Si elimina el método de acción de cadena, ¿se elige el método de acción guid? – bzlm

+1

MVC no admite la sobrecarga de métodos basada exclusivamente en la firma: la solución más fácil para usted sería simplemente tener dos métodos de acción con nombre único, uno para detalles por GUID (Detalles) y otro para obtener detalles por nombre (Búsqueda o Información tal vez ?). – Tommy

+0

@bzlm - Correcto, al eliminar la acción de cadena seleccionará el Guid (suponiendo que pase la restricción O se puede analizar en un Guid). – falquan

Respuesta

11

En primer lugar, debe disambigute sus métodos, dándoles dos nombres diferentes:

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

continuación, es necesario un controlador de ruta personalizada para inspeccionar la solicitud, y cambiar el nombre del método en consecuencia:

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

por último, añadir una ruta Details en la parte superior de sus rutas, de la siguiente manera:

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

Cuando se recibe una solicitud para obtener detalles, la ruta Details utilizará su controlador de ruta personalizado para inspeccionar el token id. El controlador de ruta agrega al nombre de la acción en función de la forma del token de ID, de modo que la solicitud se dirigirá a la acción adecuada.

8

Mi opinión es que el uso del selector de métodos de acción es más útil y menos codificado.

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

Este selector inspecciona la solicitud del parámetro de ID. Si es guid, vuelve verdadero. Por lo tanto, se usa:

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

Puede usar la clase incorporada GuidRouteConstraint en lugar de tener que agregar un código personalizado. Aprecio que este no haya sido el caso al momento de escribir. –

5

Si todavía está registrando rutas de esta manera a continuación se añadió el() de clase GuidRouteConstraint en una versión más reciente de MVC y se debe utilizar en lugar de una implementación personalizada:

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

a continuación, puede crear su acción como resultado:

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

Funcionó como un regalo, solo asegúrese de usar la declaración 'using' correcta - probablemente quiera' usar System.Web.Mvc.Routing.Constraints; 'y no System.Web.Http.Routing.Constraints –