ASP.NET MVC: Redirecciones permanentes

Buenas! Los que estéis al tanto de las novedades de ASP.NET 4, sabreis que una de ella es Response.RedirectPermanent (de la cual Ibon habla un poco en este post). La diferencia con respecto a Response.Redirect es que esta emite un código HTTP 302 (Found) mientras que RedirectPermanent emite un código HTTP 301 (Moved Permanently).

A efectos del usuario final el resultado es exactamente el mismo: cuando el navegador recibe un HTTP 301 o bien un HTTP 302, realiza otra petición a la URL especificada en el header “Location”, con lo cual se consigue el objetivo final: que el usuario sea redirigido a otra página.

¿Entonces? Bueno, como vivimos en un mundo dominado por las máquinas y por el software, ahora resulta que no nos basta con contentar al usuario: también debemos contentar a… Google y similares. Aunque para el usuario final un HTTP 302 y un HTTP 301 sean lo mismo (una redirección) los buscadores los tratan de forma muy distinta. Cuando Google realiza una petición a una URL digamos A, y recibe un código HTTP 302 que le redirecciona a B, básicamente Google sigue manteniendo en su índice la página A, puesto que un código HTTP 302 significa, en el fondo, que la redirección es temporal. Por otro lado, cuando Google recibe un HTTP 301, elimina la página A de su índice y en su lugar guarda el resultado de la redirección…

… en el fondo lo importante de este rollo es que a efectos de SEO a Google no le gustan los HTTP 302. En este post del blog de Matt Cutts hay más información sobre 302 vs 301.

En ASP.NET MVC tenemos varias maneras de realizar redirecciones, la más común de la cual es llamar al método RedirectToAction que devuelve un objeto RedirectToRouteResult configurado para redirigir al usuario a la acción especificada del controlador indicado… El tema está en que RedirectToRouteResult utiliza el código HTTP 302 para realizar la redirección (en el fondo termina llamando a Response.Redirect).

Imaginad que tengo en el controlador Home la acción Index que me redirecciona a la acción Destination, que es la que me muestra una vista:

public ActionResult Index()
{
return RedirectToAction("Destination");
}

public ActionResult Destination()
{
return View();
}

Si abro la url /Home/Index, evidentemento soy redirigido a Home/Destination, pero si observo las peticiones del navegador (en mi caso uso firefox + firebug):

image

Observad como hay dos peticiones: la primera devuelve el código HTTP 302 y el campo Location con la URL a la que el navegador debe redirigirse. La segunda petición devuelve un HTTP 200 (código para indicar que todo ha ido OK y que mandamos la respuesta al navegador).

Ahora el quid de la cuestión: como podemos realizar redirecciones permanentes en ASP.NET MVC? Pues hasta donde yo sé, no es posible directamente, pero por suerte, dado que el framework es bastante extensible no nos va a costar nada añadir dicha posibilidad… vamos a ello!

1. Creación de un ActionResult propio

El primer paso es crearnos un ActionResult propio… dado que el RedirectToRouteResult no nos sirve, ya que termina usando Response.Redirect, vamos a crearnos uno de propio, que he llamado (en un alarde de originalidad) PermanentRedirectToRouteResult.

Esta clase tendrá un constructor con cuatro parámetros: acción, controlador, el nombre de la ruta a usar y los valores de la ruta. De hecho sólo el primero (acción) es imprescindible. El resto son “avanzados” y sirven para redirigirnos a acciones de otros controladores o bien para pasar parámetros. La definición inicial de la clase es simple:

public class PermanentRedirectToRouteResult : ActionResult
{
private RouteCollection _routes;

public PermanentRedirectToRouteResult(string action, string controller, string routeName, RouteValueDictionary routeValues)
{
Action = action;
Controller = controller;
RouteName = routeName ?? String.Empty;
RouteValues = routeValues ?? new RouteValueDictionary();
}
public string RouteName {
get;
private set;
}
public RouteValueDictionary RouteValues {
get;
private set;
}
public string Action { get; private set; }
public string Controller { get; private set; }
public override void ExecuteResult(ControllerContext context)
{
}
}

Guay! Solo nos falta rellenar el método ExecuteResult para hacer lo que queramos (en este caso una redirecciónmediante HTTP 301). Para ello, primero calculamos la URL de redirección (usando la clase UrlHelper) y luego establecemos el código HTTP en 301 y el campo Location de la Response:

public override void ExecuteResult(ControllerContext context)
{
string url = UrlHelper.GenerateUrl(RouteName, Action, Controller,
new RouteValueDictionary(), RouteTable.Routes, context.RequestContext, false);
context.HttpContext.Response.StatusCode = 301;
context.HttpContext.Response.RedirectLocation = url;
}

No uso Response.RedirectPermanent porque si no mi código sólo seria compatible con ASP.NET 4 (es decir con VS2010)… de esta manera mi código sigue siendo compatible con VS2008 🙂

2. Un par de métodos de extensión…

Este punto simplemente es para que podamos hacer algo parecido a lo que hacemos con RedirectToAction: llamar a a un método que es quien crea el objeto RedirectToRouteResult. En mi caso, he creado métodos de extensión sobre la clase Controller:

public static class ControllerExtensions
{
public static PermanentRedirectToRouteResult PermanentRedirectToAction(this Controller @this,
string action)
{
return PermanentRedirectToAction(@this, action, null);
}

public static PermanentRedirectToRouteResult PermanentRedirectToAction(this Controller @this,
string action, string controller)
{
return PermanentRedirectToAction(@this, action, controller, null, null);
}

public static PermanentRedirectToRouteResult PermanentRedirectToAction(this Controller @this,
string action, string controller, string routeName, RouteValueDictionary values)
{
return new PermanentRedirectToRouteResult(action, controller, routeName, values);
}
}

Mola! Ahora ya podemos hacer nuestras redirecciones permanentes de una forma muy similar a como hacemos las normales. P.ej. puedo añadir la siguiente acción a mi controlador:

public ActionResult IndexPermanent()
{
return this.PermanentRedirectToAction("Destination");
}

Y si ahora abro la URL /Home/IndexPermanent, observo que he sido redirigido a Home/Destination, pero ahora con el código HTTP 301:

image

Y listos! Ya lo tenemos… ¿veis que fácil? 😀

PD: He dejado un zip con todo el código (en mi skydrive).

3 comentarios en “ASP.NET MVC: Redirecciones permanentes”

Deja un comentario

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