Buenoooo… sigo esta serie que se inició a raíz de las preguntas que recibí en el webcast que realicé sobre ASP.NET MVC para la gente del DotNetClub de la UOC. Si queréis ver que otros posts de esta serie hay (o habrá) echad un vistazo al post que sirve de índice.
En este caso la pregunta fue “Cómo y desde donde acceder a las cookies en una aplicación MVC”. Este post está muy relacionado con el mismo que hablaba sobre el acceso a la sesión. En particular las normas sobre desde donde acceder serían las mismas:
- No acceder a las cookies desde las vistas. Las vistas deben ser agnósticas sobre la procedencia de los datos que muestran.
- (Intentar) no acceder desde el modelo: Esto “ata” el modelo a “objetos http”. Intenta mantener el modelo libre de esas dependencias, y si por alguna razón no puedes encapsula todo este acceso en clases helper y preferiblemente inyecta esas clases usando un contenedor IoC.
- Acceder desde los controladores. Los controladores ya están atados a todos los objetos http, así que es uno de los mejores lugares para acceder a las cookies.
Acceso a las cookies de forma manual
Para establecer una cookie basta con usar la clase HttpCookie y la colección Cookies que está en la Response:
string valor = "valor de cookie";
HttpCookie cookie1 = new HttpCookie("foo_one", valor);
ControllerContext.HttpContext.Response.SetCookie(cookie1);
Aquí estamos estableciendo la cookie llamada foo_one con el valor contenido en la variable valor. La colección Cookies que está en la Response contiene las cookies que se envían del servidor al cliente.
Para leer una cookie que el cliente nos envía, debemos acceder a la colección cookies, pero ahora debemos usar la que está en la Request:
var c1 = ControllerContext.HttpContext.Request.Cookies["foo_one"];
if (c1 != null)
{
string valor = c1.Value;
}
En el código recuperamos la cookie foo_one que nos ha enviado el cliente.
Acceso a las cookies via Value Provider
Bien… que os parece este código?
public ActionResult GeCookieManual()
{
var c1 = ControllerContext.HttpContext.Request.Cookies["foo_one"];
var c2 = ControllerContext.HttpContext.Request.Cookies["foo_two"];
FooData fd = null;
if (c1 != null && c2 != null)
{
fd = new FooData()
{
OneValue = c1.Value,
AnotherValue = c2.Value
};
}
return View("Details", fd);
}
Fijaos en lo que hace… obtiene dos cookies y con ellas crea un objeto de la clase FooModel.
¿Y bien? ¿No os suena mucho al binding todo eso? Si los datos estuviesen en la querystring (GET) o bien en POST ASP.NET MVC seria capaz de crear automáticamente el objeto FooModel… y con las cookies no puede?
Pues no. Al menos no “de serie”… pero por suerte ASP.NET MVC es tan extensible que introducirle esta capacidad es muy, muy fácil…
La clave está en crearnos un IValueProvider propio, que trabaje con las cookies. Ya he comentado en anteriores posts que los value providers son los encargados de “parsear” la request del cliente, recoger los datos y dejarlos todos en un mismo sitio para que el model binder pueda realizar el binding. Por defecto hay value providers para querystring y para POST (además de alguno más rarito que no nos importa ahora), pero no lo hay para cookies. Veamos como podríamos crearlo.
No voy a entrar mucho en detalle, solo comentaré que es necesario crear la clase que implementa IValueProvider y luego crear la factoría (la clase que creará nuestro value provider). En mi post sobre Value Providers tenéis más detalles.
La clase que implementa IValueProvider es muy simple:
public class CookieValueProvider : IValueProvider
{
private readonly HttpCookieCollection cookies;
public CookieValueProvider(HttpCookieCollection cookies)
{
this.cookies = cookies;
}
public bool ContainsPrefix(string prefix)
{
return this.cookies[prefix] != null;
}
public ValueProviderResult GetValue(string key)
{
var cookie = this.cookies[key];
return cookie != null ?
new ValueProviderResult(cookie.Value, cookie.Value, CultureInfo.CurrentUICulture) : null;
}
}
Básicamente ContainsPrefix debe devolver si la clave existe dentro de este value provider y GetValue debe devolver el valor asociado a la clave. En nuestro caso simplemente debemos mirar dentro de la colección de cookies que recibimos del constructor.
Ahora toca la factoria, que lo único que debe hacer es crear un CookieValueProvider y pasarle la colección Cookies de la Request:
public override IValueProvider GetValueProvider(ControllerContext controllerContext)
{
return new CookieValueProvider(controllerContext.HttpContext.Request.Cookies);
}
Y finalmente, en el Application_Start (en Global.asax) registramos esa factoría de value providers:
ValueProviderFactories.Factories.Add(new CookieValueProviderFactory());
Ahora si tenemos la clase FooData deifnida como:
public class FooData
{
public string OneValue { get; set; }
public string AnotherValue { get; set; }
}
Y si hemos establecido cookies que se llaman igual que las propiedades (OneValue y AnotherValue), podemos obtener los valores dejando que actúe el binding de ASP.NET MVC:
public ActionResult GetCookieAuto(FooData data)
{
// data está rellenado con el valor de las cookies
}
Y listos! Nuestro value provider obtiene el valor de las cookies y el model binder se encarga del resto! 🙂
Os dejo el link de un post de donde vi hace algún tiempo el value provider para cookies en ASP.NET MVC.
Un saludo a todos!!!
Hola,
magnífico post, como de costumbre, Eduard! Qué gran serie nos estás regalando, amigo!
Saludos.
@José M.
Muchas gracias por tu comentario!!! 😀 😀
Gracias por tu post, me has dado el material necesario para seguir avanzando con mi aplicacion.
Saludos!!
EEhhhmm… bueno, no se me ocurre un título mejor. Este post nace gracias a un tweet de Lluis Franco .
Excelente! pero ahora como establezco la duración de la cookie?
@Mario
Para establecer la duración de una cookie puedes usar la propiedad «Expires» que tiene la clase HttpCookie. Esta propiedad guarda la fecha hasta la cual la cookie es válida.
P.ej. si quieres que tu cookie expire en una hora:
cookie.Expires = DateTime.Now.AddMinutes(60.0);
Saludos!
7 recursos para entender las Cookies en desarrollo Web
7 recursos para entender las Cookies en desarrollo Web