domingo, junio 14, 2009

Problema de expiración de la sesión en ASP.net

Los códigos de este post los podéis descargar desde aquí

Finalmente, por fin he encontrado un rato para escribir un post en el blog. Tenía en mente varios temas pero al fin me he decidido por éste que me parece bastante interesante y útil para aquellos que desarollamos aplicaciones en asp.net. Pongámonos en situación, cuando escribimos aplicaciones, una de las herramientas más utilizadas para mantener la persistencia de los datos es la sesión, almacenamos en sesión tablas con las que trabaja el usuario por pantalla, ids de filas, usuarios, variables, etc. todo ello para que cuando el usuario pulse algún botón y se realice un postback, podamos realizar las acciones correspondientes. Es decir, la sesión nos mantiene conectado con el usuario en un entorno desconectado. El problema viene cuando el tiempo de sesión expira, si el usuario no ha realizado ninguna acción pasados x minutos (5 por defecto), el servidor (IIS) elimina su sesión. En el momento en que el usuario pulse un botón y se vaya al servidor, nos encontraremos con que aquellos datos que habíamos guardado en la sesión ya no existen y (dependiendo de cómo controlemos esta situación) saltará una excepción en algún punto de nuestro código, echándonos fuera de la aplicación, o saltando una fantástica pantalla amarilla o algo parecido.

En aplicaciones en las que las operaciones se realicen en el servidor no vamos a tener problemas con la sesión porque cada vez que el usuario realice un postback, la cuenta de expiración de la sesión se reinicirá. En el caso que hagamos aplicaciones que hagan uso de la lógica del cliente, tipo blogs, editores wysiwyg, o que el usuario realice operaciones largas en el explorador sin tener que ir al servidor, el tiempo de expiración de la sesión se convierte en un gran problema para el programador y en una gran molestia para el cliente. Imaginaos escribiendo un post en un blog y que cuando lleváis 30 minutos escribiendo, habéis acabado el post le dais a guardar y salta una excepción, perdiendo todo el trabajo...

Posibles soluciones a este problema
Existen varias soluciones a este problema, que dependiendo de dónde esté alojada nuestra aplicación nos serán factibles o no.

Solución 1: Web.config

La solución más fácil es incorporar en nuestro web.config la etiqueta "sessionState" con el atributo "timeout" indicándole el número de minutos que deseamos que se mantenga la sesión (dentro de la sección system.web):



En este caso, hemos aumentado el tiempo de sesión a 60 minutos, antes de que el servidor la elimine. Sin embargo, me he encontrado alojamientos en los que, aunque en mi web.config tenga incluido este tag, el tiempo de sesión viene marcado por el servidor sin posibilidad de cambiarlo desde aquí.

Solución 2: Pool de aplicaciones
La segunda solución con la que contamos es la de crear un pool de aplicaciones en el IIS para nuestra aplicación. En dicho pool podemos especificarle el tiempo de sesión que queremos. Además, al crear un pool propio, se lanza un proceso en el sistema para nosotros solos. Esto es bueno en el caso de alojamientos compartidos en los que en el mismo proceso puede haber muchas aplicaciones ejecutándose. Siempre es aconsejable tener nuestro propio pool de aplicaciones, el cual se crea de la siguiente manera:

1. Desde el administrador de IIS, pulsamos con el botón derecho sobre "Grupos de aplicaciones" y seleccionamos un nuevo grupo:


2. Le damos un nombre al nuevo grupo de aplicaciones:


3. Lo siguiente es configurar las opciones de rendimiento para este grupo de aplicaciones, como la cantidad de memoria a asignar, el tiempo de reciclado del proceso o el tiempo de sesión para este grupo, entre otras opciones:


4. Por último, a un sitio o directorio web, le asignaremos este grupo de aplicaciones desde sus propiedades:


Solución 3: Servicio web de Echo
Para implementar las dos soluciones anteriores necesitamos tener control sobre el servidor (cosa que no siempre es así). En el caso que tengamos alojamientos compartidos, podemos utilizar esta opción (es la que utilizo ahora). Normalmente, un portal web dispone de dos partes, la pública (en la que normalmente se puede funcionar con cookies) y la privada en la que trabajaremos con sesiones. Es en la parte privada donde utilizaremos esta opción. La idea es implementar un servicio web que simplemente realice un eco, reciba un parámetro y lo devuelva. Habilitamos la sesión para el servicio web con lo que cada vez que se llame a este servicio, la cuenta de expiración de la sesión se reinicirá. Desde las página de gestión se llama, vía AJAX, periódicamente (yo lo tengo cada 50 segundos) a dicho servicio web para que no reinicie la cuenta. De forma esquemática, la solución es la siguiente:


El código del servicio web es el siguiente:

Tenemos que introducir el using System.Web.Script.Services para poder utilizar este servicio web vía AJAX. Además, la definición del servicio web incluye la sentencia [WebMethod(true)] indicando que se utilizará la sesión en ese método del servicio web. Para llamar a dicho método desde la página maestra (lo normal) o desde cualquier página de nuestra aplicación utilizaremos el siguiente código:

a. Incluir una referencia web al servicio web creado.
b. Dar de alta el servicio web mediante el ScriptManager de la página
En la propiedad Path pondremos la ubicación del fichero .asmx (servicio web). Si, al crear el servicio web, separados el código del fichero, se creará un fichero .asmx con la definición del servicio web donde le hayamos dicho y un fichero .cs con los códigos dentro de la carpeta App_Code.


c. Crear la función Javascript que llama al servicio web


d. Crear el temporizador para que se repita la llamada cada x segundos

Ponemos la función en el onload de la página para que se ejecute al iniciar la página. En este caso esta puesto que se repita la llamada cada 50 segundos (50.000 milisegundos)


Conclusión
Esta última es una solución sencilla que nos puede servir en cualquier entorno en el que trabajemos con aplicaciones asp.net. Utilizando el servicio de echo, nos aseguramos que la sesión del usuario no va a expirar independientemente del tiempo que esté sin hacer ningún postback. En el caso que el usuario cierre la ventana del explorador, se deja de llamar al servicio con lo que la sesión expirará en los 5 minutos correspondientes. Por supuesto, siempre podemos colocar el botón de cerrar que matará la sesión. Como siempre, espero que os sea de utilidad este post y gracias por leer este blog (ya sois un montón).

8 comentarios:

Víctor dijo...

Muy interesante el artículo Manuel!

El tema de la sesión es algo que nos trae de cabeza a los proveedores de hosting (ya que no podemos estar dando pool's a todo el mundo ya que la memoria y cpu es finita) pero la solución de echo que has implementado me parece muy útil y funcional.

Si me lo permites la probaré en una aplicación que estamos desarrollando para ver el rendimiento y el consumo del proceso.

Un saludo!!!

Manuel Cardenas Thorlund dijo...

Gracias por comentar el artículo Victor,
puedes utilizar cualquier código puesto en el blog sin ningún tipo de problema.

Anónimo dijo...

Hola, como puedo bajar tu código? porque lo pusiste como imagen.

Manuel Cardenas Thorlund dijo...

Buenas, al principio del artículo tienes un enlace a los códigos.

Anónimo dijo...

Muchas gracias.

Anónimo dijo...

Excelente la linea

pagaloca dijo...

no pude acceder al código de que otra forma lo puedo conseguir ya que me interesa el tema

Jose Roberto Chavez Rodriguez dijo...

Esto se puede poner en la Master Page utilizo .net 2.0