domingo, 3 de abril de 2011

[ASP.NET MVC] Pasar parámetros a través del PathInfo

¡Muy buenas! Bueno, el título del post no queda demasiado claro, pero a ver si consigo explicar un poco la idea. ;-)

Los que habéis usado ASP.NET MVC estáis muy acostumbradas a las URLs del estilo /controlador/accion/id, es decir algo como:

  • /Home/Index/10
  • /Articles/View/Eiximenis
  • /Blog/View/10293

Sabemos que gracias a la tabla de rutas podemos pasar tantos parámetros como queramos, y así podríamos tener URLs del tipo:

  • /Articles/View/Eiximenis/MVC/2011

Que podría devolverme los articulos de “Eiximenis” con el tag “MVC” y del año 2011.

El único punto a tener presente es que el orden de los parámetros importa, es decir no es lo mismo /Articles/View/Eiximenis/MVC/2011 que /Articles/View/2011/MVC/Eiximenis. En el primer caso buscamos los artículos de Eiximenis sobre MVC en el 2011 y en el segundo caso buscaríamos los artículos del blogger 2011, sobre MVC en el año de Eiximenis. Y sin duda Fra Francesc Eiximenis, fue un gran escritor, pero que yo sepa todavía no se le ha dedicado un año (algo totalmente injusto, por supuesto :p).

En este artículo quiero enseñaros una manera para que podáis gestionar URLs del tipo:

  • /Articles/View/Author/Eiximenis/Tag/MVC/Year/2011
  • /Articles/View/Tag/MVC/Year/2011/Author/Eiximenis

Y que ambas URLs sean tratadas de forma idéntica. En este caso estaríamos pasando tres parámetros: Author, Tag y Year.

Para conseguir este efecto nos bastan dos acciones muy simples: definir un route handler nuevo y una entrada a la tabla de rutas.

El route handler lo único que debe hacer es recoger la información de la URL y parsearla en “tokens” (usando el ‘/’ como separador). Y por cada par de tokens añadir una entrada en los valores de ruta (route values). El código es muy simple:

public class UrlRouteHandler : MvcRouteHandler
{
protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
{
var path = requestContext.RouteData.Values["pathInfo"];
if (path != null)
{
var tokens = path.ToString().Split('/');
for (var idx =0; idx<tokens.Length; idx+=2)
{
if (idx+1 < tokens.Length)
{
requestContext.RouteData.Values.Add(tokens[idx], tokens[idx+1]);
}
}
}

return base.GetHttpHandler(requestContext);
}
}





Una pequeña nota es que la cadena que separamos en tokens, no es toda la URL sino “pathInfo” un parámetro de ruta que ya nos vendrá dado. Este parámetro de ruta contendrá todo aquello que no es ni el controlador ni la acción. Es decir en la URL /Articles/View/Author/Eiximenis/Tag/MVC/Year/2011 el valor de pathInfo será Author/Eiximenis/Tag/MVC/Year/2011 (que son justo los parámetros).



Ahora nos queda añadir la entrada a la tabla de rutas. En mi ejemplo yo he eliminado la entrada “Default” que genera VS2010 y la he sustituído por:




routes.Add("Default", new Route(url: "{controller}/{action}/{*pathInfo}",
routeHandler: new UrlRouteHandler(),
defaults: new RouteValueDictionary(new {controller = "Home", action = "Index"})));





La clave aquí está en el {*pathInfo}. Aquí le digo al sistema de rutas que coja todo lo que venga después de /{controller}/{action} y me lo añada a un parámetro de ruta llamado pathInfo. Además de eso, en esta ruta le indico que su routeHandler será una instancia de la clase UrlRouteHandler que hemos creado antes.



Y listos! Una vez los datos están en el route value ya puede entrar en acción el sistema de binding de ASP.NET MVC lo que quiere decir que puedo crear un controlador como el siguiente:




public class ArticlesController : Controller
{
public ActionResult View(string author, string tag, int? year)
{
dynamic data = new ExpandoObject();
data.Author = author ?? "Sin formato";
data.Tag = tag ?? "Sin confirmación";
data.Year = year.HasValue ? year.ToString() : "Sin año";
return View(data);
}
}





Que recibiría los parámetros de las URLs que hemos visto anteriormente.



Un saludo a todos!



PD: De nuevo eso es un crosspost desde mi blog en geeks.ms!