ASP.NET MVC Pipeline: Routing

Hace ya un tiempo que no escribo sobre el “tradicional” ASP.NET MVC y justamente la otra vez me cruce con esta excelente entrada en DotNet Tricks que me motivo a escribir sobre un tema que siempre posponía: el pipeline de ASP.NET MVC y sus puntos de extensión.

En esta primer entrega vamos hablar sobre la etapa de routing del pipeline, con una breve descripción de la función que cumple y las diferentes formas en que podemos extenderlo. En próximas entregas vamos a ir hablando de las siguientes faces: “Controller Initialization”, “Action Execution”, “Result Execution” y “View Initialization & Rendering”.

Routing

Routing

Routing

Esta es la primer etapa del pipeline y su función se centra en determinar quien será el encargado de manejar la petición.

Como ya todos saben el framework ASP.NET MVC nos provee de un motor de enrutamiento – routing engine – que se encarga de parsear la URL del request entrante y luego de machearla/mapearla contra alguna de las rutas – routes – almacenadas en la tabla de enrutamiento – route table –. A partir de la ruta coincidente podremos determinar que handler será el encargo de manejar la petición – el comportamiento por defecto es determinar el controlador y acción que deben ser invocados – pero como veremos más adelante podemos extendernos en este punto.

La tabla de enrutamiento esta definida en el archivo global.asax y es única por aplicación. Cuando la aplicación se inicia se puebla dicha tabla con las rutas quedando disponibles para el motor de enrutamiento.

Entre la información que podemos encontrar en una ruta tenemos el URL pattern – utilizado para hacer el “matching” con el request entrante -, el route handler – encargado de proporcionarnos el http handler – y el “route data” – que encapsula información sobre la ruta -.

El proceso de enrutamiento en detalle

El motor de enrutamiento no conoce nada acerca de ASP.NET MVC, su trabajo simplemente consiste en comparar la URL del request entrante con las rutas almacenadas en la route table y ante la primer coincidencia establecer, por medio de la ruta seleccionada, cual sera el handler HTTP que manejará la petición.

Routing - Camino feliz

Routing – Camino feliz

Si seguimos el diagrama de arriba vemos que el primer paso es comparar la URL del request con todas las rutas definidas en la tabla de enrutamiento de la aplicación. Este trabajo esta a cargo del objeto UrlRoutingModule que debe evaluar la URL con cada una de las definiciones de rutas y ante la primer coincidencia – matching – seleccionar la ruta y detener la comparación. En caso de que no haya ninguna coincidencia se le retorna un error HTTP 404 – “Page Not Found” – al cliente.

Suponiendo que haya coincidencia, de la ruta seleccionada extraemos el RouteData que nos permite un fácil acceso al route handler. Sobre este último se realizan un par de validaciones (como por ejemplo que no sea nulo o que no sea de un tipo especial).

Un objeto route handler debe implementar el contrato IRouteHandler y por default el framework trabaja con una instancia de MvcRouteHandler.

Si las verificaciones pasan se agrega el route data al http context, caso contrario se detiene la ejecución del módulo de ruteo.

El paso final consiste en extraer el http handler del route handler y sobre el mismo realizar un par de validaciones similares a las anteriores.

Un objeto http handler debe implementar el contrato IHttpHandler y por default se trata del objeto MvcHandler.

Si las validaciones son superadas se define al handler http como el encargado de manejar la solicitud (si bien este último paso no esta reflejado en el diagrama, podemos verlo en la porción de código que recorte de la clase UrlRoutingModule):

El objeto RouteData y por ende el HttpHandler serán utilizados en la siguiente etapa del pipeline “Controller Initialization“. Para quienes no lo tengan muy presente, es en la siguiente etapa donde generalmente se selecciona el controlador y acción que van a manejar la petición (ojo, esa es la acción por default, veremos en un ejemplo posterior que podemos modificar este comportamiento).

Extendiendo el Routing

Existen diferentes puntos donde podemos extender y/o customizar esta fase del pipeline. Vamos a ver algunos de ellos, yendo de los más sencillos a cuestiones un poco más particulares:

Estableciendo las rutas de la aplicación

En este etapa es donde le proveemos información a la tabla de enrutamiento. Ademas de permitirnos trabajar por convención de URLs, el framework nos permite crear custom routes, definir valores por default a los parámetros de entrada, manejar diferentes segmentos de una URL, agregar restricciones a las rutas, etc.

Existen diferentes formar de agregar una Route a la RouteTable, puede ser por medio de los métodos Add(…) y MapPageRoute(…) que ofrece RouteCollection o el método MapRoute(…) que trae la RouteCollectionExtension.

Podes encontrar información detalla en el sitio de MSDN.

Utilizando atributos de ruteo

Antes de la versión de ASP.NET MVC 5 el mecanismo de enrutamiento del framework  establecía que una URI coincidía con una acción – enrutamiento basado en convención-. Una de las novedades que trajo esta nueva versión era la del mecanismo de routing basado en atributos – Attribute Routing – el cual utiliza atributos para definir el ruteo, y nos permite tener mucho mas control sobre las URI de nuestra web.

Ya hablemos de este tema aquí ASP.NET MVC 5 – Attribute Routing y aquí ASP.NET MVC 5 – Attribute Routing II.

Custom Route Handler

Uno de los puntos de extensión que tenemos es el route handler, componente responsable de crear el http handler que procesará la request.

Hay dos motivos posibles para extender el route handler, el primero manipular el RouteData previo a ser tomado por el MvcHandler (default handler del framework) y el segundo es salir del pipeline de ASP.NET MVC para manejar nosotros mismos la request por medio de un custom HttpHandler.

Para crear un custom route handler debemos implementar la interfaz IRouteHandler:

Como podemos ver solo hay que implementar un único método GetHttpHandler(…) cuya función es devolvernos el http handler que va a manejar la request. Como parámetro recibe el request context que contiene información acerca del current request (información del contexto http como la propia request, server, application) y del route data (valores de los parámetros y el objeto Route en sí mismo).

Veamos un ejemplo, donde vamos a crear un custom http handler – que corta con el pipeline de ASP.NET MVC y se encargua de redireccionarnos al blog -, y un custom route handler para que nos proporcione dicho http handler. Por último vamos asignarle el custom route handler a una ruta especifica.

El primer paso, crear un http handler cuya tranajo sea re-dirigirnos al blog:

El segundo paso, crear el route handler que nos retorne una instancia del http handler anterior:

Por último, a una route en particular le especificamos el route handler:

Con este ejemplo cuando accedamos a la URL http://localhost:8080/sebys obtendremos una re-direccion automatica a http://sebys.com.ar 😉

Custom Route Constrains

Como dijimos anteriormente podemos definir restricciones en una ruta, que pueden ser expresiones regulares o cualquier clase que implemente IRouteConstraint – este último para los casos en que la restricción no pueda ser representada una expresión regular -.

Implementando el contrato IRouteConstraint podemos crear nuestras propias “route constraint”.

Veamos un ejemplo sencillo, en donde necesitamos validar que el parámetro de una ruta sea par. El primer paso es crear una clase que implemente el contrato mencionado anteriormente:

Lo que debemos implementar en ese caso es el método Match(…) y devolver true o false dependiendo si se pasa o no la restricción.

Para que esta restricción se aplique en una determinada regla previamente debemos especificarlo en la composición de la ruta:

Ahora solo serán admitidas URLs como http://localhost:5050/numerospares/2 y siendo rechazadas URLs como http://localhost:5050/numerospares/1.

Con esto finalizamos la primer entrada, espero que le sea de utilidad.

Enlaces recomendados:

 

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s