Explorando ASP.NET vNext: Routing

Lentamente se van desgranando las novedades que incorporará ASP.NET vNext. Recordad que podéis ya jugar un poco con él, descargandoos la CTP de VS14 🙂

Vamos a ver en este post cuatro cosillas sobre el routing que incorpora ASP.NET vNext. Vamos a partir de una aplicación web vNext vacía y agregaremos tan solo lo justo para tener MVC y una tabla de rutas. Lo primero será editar el project.json y añadir la referencia a asp.net MVC:

  1. "dependencies": {
  2.     "Helios" : "0.1-alpha-build-0585",
  3.     "Microsoft.AspNet.Mvc": "0.1-alpha-build-1268"
  4. },

Una vez hecho esto ya podemos editar el método Configure de la clase Startup para agregar MVC y configurar una tabla de rutas:

  1.     app.UseServices(s => s.AddMvc());
  2.     app.UseMvc(r =>
  3.     {
  4.         r.MapRoute(
  5.             name: "default",
  6.             template: "{controller}/{action}/{id?}",
  7.             defaults: new { controller = "Home", action = "Index" });
  8.     });
  9. }

Con esto tenemos la “clásica” tabla de rutas por defecto. Una diferencia respecto a MVC clásico (MVC5 y anteriores) es el uso de id? para indicar un parámetro opcional (en lugar de colocar id = UrlParameter.Optional en el defaults). Esta nomenclatura es la misma que usábamos en el routing attribute de webapi 2 y es mucho más concisa.

Si añadimos un controlador HomeController para que nos devuelva una vista (Index.cshtml) veremos que todo funciona correctamente… Hasta ahí nada nuevo bajo el sol.

Pero, recordad: WebApi y MVC están ahora unificados y a nivel de routing funcionaban de forma relativamente distinta. Pero… ¿como funcionará el routing de vNext? Hagamos un experimento:

  1. public IActionResult Index()
  2.  {
  3.      return View();
  4.  }
  5.  
  6.  public IActionResult Index(int id)
  7.  {
  8.      return View();
  9.  }

En MVC clásico este código no funciona (MVC se queja de una ambigüedad) ya que dos métodos de un controlador no pueden implementar la misma acción (para el mismo verbo HTTP). Pero en WebApi esto funciona. Pues en vNext también.

Si invocamos a la acción sin pasar el parámetro id (p. ej. con / o con /Home/Index) se ejecutará el primer método. Si invocamos la acción pasando el parámetro id (p. ej. con /Home/Index/2) se ejecutará el segundo método.

Prosigamos nuestro experimento… Modifiquemos el método Startup.Configure para tener la siguiente tabla de rutas:

  1. app.UseMvc(r =>
  2. {
  3.     r.MapRoute(
  4.         name: "default",
  5.         template: "{controller}/{action}/{id?}",
  6.         defaults: new { controller = "Home", action = "Index" });
  7.  
  8.     r.MapRoute(
  9.         name: "second",
  10.         template: "Details/{id?}",
  11.         defaults: new { controller = "Product", action="Display"});
  12. });

Creamos el controlador ProductController con una acción Dispay:

  1. public IActionResult Display(int? id)
  2. {
  3.     if (id == null) ViewBag.Pid = "None";
  4.     else ViewBag.Pid = id;
  5.     return View();
  6. }

Si ahora vamos a /Details/100.. ¿qué es lo que obtenemos? En ASP.NET MVC clásico obtendremos un 404 porque las rutas se evalúan en orden y la primera que es posible aplicar se aplica. Así la url /Details/100 encaja dentro de la primera ruta con:

  • controller = Details
  • action = 100
  • id = sin valor

Y como no existe el DetailsController de ahí el 404 que obtendríamos con MVC clásico. De ahí que la tabla de rutas tenga que configurarse con las rutas en orden desde la más específica a la más general.

Pero en vNext eso ya no es así. Las rutas siguen evaluándose en orden pero si una ruta devuelve un 404 el control pasa a la siguiente ruta. Así, en este caso se intenta evaluar la primera ruta con los parámetros indicados anteriormente (al igual que en MVC clásico). Dicha ruta devuelve un 404 así que el control pasa a la siguiente ruta. La URL /Details/10 encaja con el patrón de la siguiente ruta así que se intenta aplicar dicha ruta con los parámetros:

  • controller = Product (sacado de defaults)
  • action = Display (scado de defaults)
  • id = 100

Ahora como el controlador y la acción existen se invocan y vemos la página de detalles del producto 100.

Hagamos una tercera modificación a la tabla de rutas para que quede así:

  1. app.UseMvc(r =>
  2. {
  3.     r.MapRoute(
  4.         name: "default",
  5.         template: "{controller}/{action}/{id?}",
  6.         defaults: new { controller = "Home", action = "Index" });
  7.  
  8.     r.MapRoute(
  9.         name: "second",
  10.         template: "Details/{id?}",
  11.         defaults: new { controller = "Product"});
  12. });

Es la misma tabla de rutas que teníamos con una excepción importante: la ruta “second” no define acción.

Si ahora navegamos de nuevo a /Details/100 obtenemos un 404. Era de esperar, puesto que no hay acción alguna… Pero el routing de vNext nos tiene reservada una última sorpresa: si no hay acción definida se enrutará por el verbo HTTP.

Para hacer la prueba modifica el controlador Product:

  1. public IActionResult Get(int? id)
  2. {
  3.     if (id == null) ViewBag.Pid = "None";
  4.     else ViewBag.Pid = id;
  5.     return View("Display");
  6. }

Si ahora navegas a /Details/100 deberías ver, de nuevo, los detalles del producto con id 100. Al no haber acción vNext ha enrutado por el verbo HTTP y nuestro controlador se ha enrutado como si fuese un controlador de webapi.

Bueno… hemos visto brevemente algunas de las diferencias entre el routing de vNext y el de MVC tradicional. Para más detalles os recomiendo este post del equipo de ASP.NET.

Saludos!

2 comentarios sobre “Explorando ASP.NET vNext: Routing”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *