RESTful URLs en ASP.Net
March 27, 2009 | Filed Under Artículos, Novedades |Por Federico Freire y Soledad Pano.
Según el estilo REST (Representational State Transfer), la web está formada por recursos sobre los cuales se puede tomar acciones como GET, POST, PUT y DELETE. Por su parte, cada recurso se identifica unívocamente mediante una URL, como por ejemplo http://server/books/mybook/2009 . Este tipo de URL es más “linda” que una URL de la forma: http://server/books/showBook.aspx?name=myBook&edition=2009 . Los beneficios de las URLs tipo REST radican en que estas son más amigables para SEO (Search Engine Optimization) y más fácilmente “cacheables”. En este artículo se mostrará cómo implementar el uso de este tipo de URLs en una aplicación de ASP.NET utilizando la API de System.Web.Routing provista en el .Net Framework 3.5.
La API de ruteo para ASP.NET nació inicialmente como parte del framework de MVC (Model View Controller) y fue incluida luego en el Service Pack 1 del .Net Framework .NET 3.5. La misma nos permite definir patrones de ruteo para mapear una URL a un handler (una clase encargada de procesar el request) en vez de a un archivo físico del sitio. De esta forma podemos generar URLs “virtuales”. Además de los beneficios de caching y SEO mencionados, el ruteo de URLs permite reestructurar físicamente un sitio web sin romper viejas URLs que los usuarios puedan tener en sus favoritos o incluso recordar de memoria.
A diferencia de la reescritura de URL (URL Rewriting), en el caso del ruteo la dirección no cambia en ningún momento. La solicitud es capturada por el módulo de ruteo, el cual busca dentro de los patrones definidos previamente cuál es el handler al cual le corresponde procesar el request. Si la solicitud de mapea con un patrón previamente definido, el handler correspondiente se encarga de procesar la petición. De lo contrario la URL será procesada normalmente.
Para implementar el ruteo de URLs en ASP.NET se requiere entonces contar con el .Net Framework 3.5 SP1, agregar una referencia a System.Web.Routing.dll en el sitio web y realizar los pasos que se detallan a continuación.
1. Agregar el módulo de ruteo al Web.Config
El módulo de routing es el encargado de rutear cada solicitud al handler que corresponda según el patrón al cual mapee cada URL. Para agregar este módulo se debe editar el nodo httpModules del archivo Web.config, agregando el siguiente elemento:
<httpModules>
<add name=”RoutingModule” type=”System.Web.Routing.UrlRoutingModule“/>
…
</httpModules>
2. Definir las rutas
El siguiente paso es definir las rutas en la tabla de ruteo. Las rutas contienen parámetros que son delimitados entre llaves ({ }). Estos parámetros serán asignados una vez que se encuentre el match correspondiente para la URL. También podemos asignar constantes que se utilizan para comparar la URL. El carácter ‘/’ es utilizado como delimitador de los parámetros y contantes cuando se analiza la URL.
El patrón de una ruta sería por ejemplo:
books/{name}
Cuando el número de parámetros es variable se puede especificar un “parámetro comodín” a través del carácter * y debe ser incluido en el último de ellos. Por ejemplo:
books/{name}/{*edition}
El parámetro comodín puede estar o no presente en la URL. Si lo está, se le asignará como valor la cadena posterior al último parámetro. Teniendo en cuenta el ejemplo anterior, las siguientes URLs mapearían con el patrón de arriba:
books/myBook
books/myBook/2009
books/anotherBook/2008/january
En el último caso al parámetro “edition” se le asignará el valor “2008/january”, por ser éste el parámetro comodín.
Las rutas deben ser agregadas a la colección estática Routes de la clase RouteTable. En este caso lo añadimos en el global.asax desde el método que controla el application_start:
<%@ Application Language=“C#” %>
<%@ Import Namespace=“System.Web.Routing” %>
<script runat=“server”>
void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
}
private void RegisterRoutes(RouteCollection routes)
{
Route route = new Route(“books/{name}/{*edition}”, new BookURLHandler());
routes.Add(route);
}
</script>
Notar que al registrar la ruta se está especificando el patrón de la URL junto con el handler que procesará el request en caso de acierto. Los handlers son clases que implementan la interface IRouteHandler.
3. Definir un handler
La interfaz IRouteHandler tiene como único método a GetHttpHandler, el cual retorna la página que será servida al request. En el siguiente ejemplo de código se muestra cómo sería el handler que procese la solicitud de obtener un libro.
using System;
using System.Web.Routing;
using System.Web.UI;
using System.Web.Compilation;
public class BookURLHandler : IRouteHandler
{
public BookURLHandler()
{
}
#region IRouteHandler Members
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
Page bookPage = (Page) BuildManager.CreateInstanceFromVirtualPath(“~/Book.aspx”, typeof(Page));
string name = (string)requestContext.RouteData.Values["name"];
string edition = (string)requestContext.RouteData.Values["edition"];
//Pass parameters to page
bookPage.Items.Add(“name”, name);
bookPage.Items.Add(“edition”, edition);
return customerPage;
}
#endregion
}
En el ejemplo se crea una instancia de la página Book.aspx mediante el método System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath(). La página Books.aspx será en este caso la que finalmente sirva el recurso solicitado.
Los parámetros contenidos en la URL (los mismos que se encontraban entre llaves en el patrón registrado) pueden ser extraídos a través de la propiedad RouteData del RequestContext. El objeto RouteData es un diccionario que contiene los valores de los parámetros definidos en la ruta. Una vez extraídos estos valores pueden ser pasados a la instancia de la nueva página a través de la propiedad Items, la cual contiene un diccionario de objetos que son guardados en el contexto de la página que será servida.
Luego de la ejecución del handler, se continuará con el procesamiento normal de la página servida.
4. Configurar IIS
Nótese que las URLs utilizados como ejemplo hasta el momento, no contenían ninguna extensión de archivo. En el caso de IIS6, esto hace que la solicitud no sea pasada al filtro de ASP.NET. Para que esto suceda, se debe configurar el directorio virtual de la aplicación para que soporte un mapeo de tipo comodín. Esto es, indicarle que todas las extensiones sean derivadas a ASP.NET (c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll). Se debe también configurar la opción de que IIS no valide la existencia física del recurso.
En el caso de IIS 5.1 existe una limitación, ya que no está permitido el mapeo para extensiones comodín. Esta versión del IIS invoca el módulo de ASP.NET sólo cuando encuentra una extensión de archivo que mapea a ASP.Net. Por lo tanto, si dejamos la ruta sin extensión como en el ejemplo anterior lo único que obtendríamos sería un código de error 404.
La forma más fácil de solucionar este problema es agregar en alguna parte de la URL una extensión mapeada a ASP.NET, como la extensión .aspx:
Books.aspx/{name}/{edition}
O sino
dev.aspx/Books/{name}/{edition}
No importa que no exista físicamente la página aspx incluida en la ruta (por ejemplo dev.aspx), ya que al ser un recurso .aspx, este no es chequeado por el information server (a menos que se le indique lo contrario).
Conclusión
Gracias a la API de System.Web.Routing incluida recientemente en el .Net Framework la tarea de utilizar en nuestra aplicación URLs amigables se facilita notablemente. Con muy poco código se pueden crear URLs fácilmente cacheables y alineadas con las técnicas de SEO, además de agregar mayor versatilidad y escalabilidad al sitio al independizar las URLs de los recursos físicos en el servidor.