ASP.NET ViewState a fondo (I)

BinaryBag

El ViewState está presente en cualquier desarrollo con ASP.NET y puede ser un aliado o un enemigo. Para intentar que sea un aliado hay que conocerlo bien, saber cómo funciona y qué se puede hacer con él.

Este es el primero de una serie de artículos en el que nos meteremos a fondo con el ViewState, desde por qué surge, qué es y cómo es su estructura (lo que veremos en este artículo), hasta qué cosas podemos hacer con él, que ya veréis que son unas cuantas y bastante interesantes.

 

ViewState: el comienzo

In the beginning, was the command line… No, no nos remontaremos tan atrás, pero sí vamos a mirar un poco al pasado del desarrollo web para ver por qué nace el ViewState.

Las páginas web son servidas a través del protocolo HTTP, el cual carece de información de estado por razones intrínsecas a su diseño. Esto significa que no guarda información sobre la conexión anterior que se ha hecho desde un cliente dado, aunque esta conexión ocurriera hace 1 segundo. Cada vez que se hace una petición a través de HTTP se regenera la página desde cero y se envía al cliente, sin guardar la información que el usuario había introducido en su navegador en la interacción anterior.

El HTTP no fue pensado para el uso que se le da hoy en día, que es ser capaz de servir complejas aplicaciones web que prácticamente nada tienen que envidiar a aplicaciones clásicas de escritorio. HTTP fue diseñado para una cosa mucho más sencilla: enviar a un cliente (user agent en la jerga HTTP) recursos identificados por una URL y situados en un servidor. Los recursos que pueden viajar a través de HTTP son de cualquier tipo y gracias a esta gran flexibilidad se pueden construir las páginas tan completas que pueblan hoy Internet, con texto, imágenes, audio, vídeo, etc.

Con estos mimbres hay que trabajar, o dicho de otra forma: había que paliar esa carencia de información de estado intrínseca a HTTP para poder simular el concepto de una “sesión de usuario”, es decir, una interacción de un usuario con una aplicación en la que, basándose en la información previa y actual, la aplicación irá ejecutando su lógica hasta que esta sesión termine.

Existen un buen número de maneras de mantener la información de estado. Sin entrar en ellas, ya que no es el objetivo de este artículo, sí que al menos vamos a mencionar los métodos con los que contamos en ASP.NET:

  • Cookies
  • Campos ocultos
  • Query string
  • ViewState
  • ControlState
  • Session State
  • Application State
  • Cache
  • Profile Properties
  • HttpContext.Items

Todas ellas tienen sus pros y sus contras, escenarios donde su uso es más o menos ventajoso y su propia ración de buenas prácticas, pero centrándonos ya en el protagonista del artículo, el ViewState es un constructo provisto por la arquitectura de ASP.NET y es uno de los métodos más importantes para mantener la información de estado de un usuario (mantener su sesión).

 

Estructura interna

El ViewState está representado por estructuras diferentes dependiendo de en qué lado del roundtrip se encuentre, el cliente o el servidor. En el lado del cliente, el ViewState es un campo oculto que guarda la información de estado codificada en base-64.

A continuación se muestra una página con sólo un GridView que contiene datos de las dos primeras filas de la tabla Production.Product de la BBDD de esos grandes vendedores de bicicletas que son la gente de AdventureWorks :

clip_image001

El código ASPX del formulario es este:

   1: <form id="form1" runat="server">

   2: <div>

   3:     <asp:SqlDataSource 

   4:         ID="sdsAWorks" 

   5:         ConnectionString="Persist Security Info = False; 

   6:         server = localhost;database = AdventureWorks;

   7:         Integrated Security = yes"

   8:         DataSourceMode="DataSet"

   9:         SelectCommand="SELECT TOP 2 Name, ProductNumber, 

  10:         DaysToManufacture, ListPrice, SellStartDate 

  11:         FROM Production.Product"

  12:         runat="server">

  13:     </asp:SqlDataSource>

  14:         

  15:     <asp:GridView 

  16:         ID="gvProducts" 

  17:         DataSourceID="sdsAWorks" 

  18:         runat="server">

  19:         <RowStyle BackColor="#CEE8E7" />

  20:         <HeaderStyle BackColor="#5BA3D7" BorderColor="#0066FF" />

  21:     </asp:GridView>

  22: </div>

  23: </form>

Una vez renderizado el form, en el navegador cliente encontramos el campo oculto __VIEWSTATE en el HTML recibido , que tiene este aspecto:

   1: <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" 

   2: value="/wEPDwUKLTM0NDU4NDM2NA9kFgICAw9kFgICAw88KwANAgAPFg />

   3: QeC18hRGF0YUJvdW5kZx4LXyFJdGVtQ291bnQCAmQMFCsABRYIHgROYW1

   4: lBQROYW1lHgpJc1JlYWRPbmx5aB4EVHlwZRkrAh4JRGF0YUZpZWxkBQRO

   5: YW1lFggfAgUNUHJvZHVjdE51bWJlch8DaB8EGSsCHwUFDVByb2R1Y3ROd

   6: W1iZXIWCB8CBRFEYXlzVG9NYW51ZmFjdHVyZR8DaB8EGSsBHwUFEURheX

   7: NUb01hbnVmYWN0dXJlFggfAgUJTGlzdFByaWNlHwNoHwQZKVtTeXN0ZW0

   8: uRGVjaW1hbCwgbXNjb3JsaWIsIFZlcnNpb249Mi4wLjAuMCwgQ3VsdHVy

   9: ZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5H

  10: wUFCUxpc3RQcmljZRYIHwIFDVNlbGxTdGFydERhdGUfA2gfBBkpXFN5c3

  11: RlbS5EYXRlVGltZSwgbXNjb3JsaWIsIFZlcnNpb249Mi4wLjAuMCwgQ3V

  12: sdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRl

  13: MDg5HwUFDVNlbGxTdGFydERhdGUWAmYPZBYGAgEPZBYKZg8PFgIeBFRle

  14: HQFD0FkanVzdGFibGUgUmFjZWRkAgEPDxYCHwYFB0FSLTUzODFkZAICDw

  15: 8WAh8GBQEwZGQCAw8PFgIfBgUGMCwwMDAwZGQCBA8PFgIfBgUSMDEvMDY

  16: vMTk5OCAwOjAwOjAwZGQCAg9kFgpmDw8WAh8GBQxCZWFyaW5nIEJhbGxk

  17: ZAIBDw8WAh8GBQdCQS04MzI3ZGQCAg8PFgIfBgUBMGRkAgMPDxYCHwYFB

  18: jAsMDAwMGRkAgQPDxYCHwYFEjAxLzA2LzE5OTggMDowMDowMGRkAgMPDx

  19: YCHgdWaXNpYmxlaGRkGAEFCmd2UHJvZHVjdHMPPCsACgEIAgFkaB91DdG

  20: 5dfsk09w1hbqOusnzCpM="

Aún con un sólo grid que muestra dos filas, tenemos bastante información en el ViewState.

El primer paso para empezar a desentrañar lo que hay codificado en este campo es usar algún decodificador en base-64 de los que abundan por internet. Una vez decodificado a texto plano nos encontramos con esto:

clip_image002

Aquí podemos empezar a ver ya datos relacionados con el GridView, nombres de columnas, valores, tipos de datos, etc. Sin embargo, no vemos toda la información claramente, hay mucho ruido aún. Ahora veremos qué contiene este ruido, pero ya hemos visto al ViewState en acción desde el lado del cliente, guardando la información en el campo oculto__VIEWSTATE del HTML recibido en el navegador.

 

¿Y qué pasa en el servidor?

En el lado del servidor, el ViewState está implementado como un diccionario de pares clave-valor que contiene la información de estado (también contiene un hash para detectar si alguien ha manipulado el ViewState entre postbacks, de esto hablaremos en un próximo artículo). Cuando ASP.NET renderiza el HTML de una página, incluye el campo oculto __VIEWSTATE, serializa la información de estado que va a almacenar en él, la codifica en base-64 y la envía en ese campo oculto.

El hecho de que se serialice la información no es un detalle al que se le deba prestar poca atención, sobre todo desde el rol del arquitecto. Dado que en el ViewState se pueden almacenar otros datos aparte de la información de estado que incluye automáticamente ASP.NET, en algunos proyectos podría surgir el problema de que se ha decidido almacenar en él elementos que no son serializables, no se prepararon para que lo fueran, no pueden serlo por restricciones funcionales o técnicas, etc. Por tanto, no hay que pasar por alto ese punto al diseñar la arquitectura de una aplicación si se van a hacer usos específicos del ViewState que no sean el uso por defecto del runtime ASP.NET.

En este proceso interno del runtime de ASP.NET nos encontramos con interioridades poco documentadas de la arquitectura de ASP.NET, como la clase que se usa para serializar los datos, LosFormatter (Limited Object Serialization Formatter). Esta clase, de la que apenas existe documentación y que se encuentra en el espacio de nombres System.Web.UI, es capaz de serializar cualquier tipo de objeto (los mismos que el BinaryFormatter) en una cadena de texto ASCII compacta y codificada en base-64. Sin embargo, el LosFormatter está optimizado para serializar tipos sencillos como String, Integer, Array, Hashtable, ArrayList, Boolean, Pair y Triplet. LosFormatter, aunque creada para uso interno de ASP.NET, puede ser utilizada en cualquier desarrollo, con lo que si alguno estaba necesitando este tipo de serialización puede aprovecharse de ella 🙂

Hemos nombrado Pair y Triplet, dos clases de uso interno de ASP.NET y también escondidas en System.Web.UI, cuya finalidad es simplemente almacenar en una clase 2 ó 3 valores, exponiendo las propiedades First, Second (Pair y Triplet) y Third (Triplet).

Hablar de Pairs y Triplets es hablar ya de la estructura definitiva en la que se almacenan los datos en el ViewState. Pero primero que eso, para que los Pairs y Triplets tengan sentido, hace falta que revisemos en este contexto qué es lo que se guarda exactamente en el ViewState. Al principio hemos dicho que se guarda la información de estado, lo que permite que un usuario pueda mantener una “sesión” en una aplicación web. Esto es así a alto nivel, a nivel conceptual, pero ahora ya estamos al nivel de los átomos y en este nivel, la información de estado no es otra que la información de todos los controles existentes en la jerarquía de controles de una página. Es decir, en el ViewState se guarda toda la información de estado de toda la jerarquía de controles de la página y esta información se almacena en Pairs y Triplets. Recordemos que todos los controles de una página ASP.NET cuelgan del objeto Page, creando una jerarquía más o menos intrincada según la página.

La información de cada control se almacena en un Triplet, cuyo First contiene listas de clave-valor para almacenar valores de propiedades, por ejemplo: width-150px, height-50px. El Second contiene una lista con los índices de las posiciones que ocupan sus controles hijo en la jerarquía de controles de la página y Third almacena los Triplet de estos controles hijo. Casi nada. Para comprender mejor esta estructura podéis leer este post de Paul Wilson donde plantea un ejemplo que clarifica un poco las cosas.

Finalmente, para decodificar el ViewState y comprender mejor su estructura interna os recomiendo que os descarguéis el ViewState Decoder de Fritz Onion, una herramientas muy útil para trabajar con el ViewState.

Hasta aquí llega este primer artículo, en el próximo seguiremos ahondando en lo que se puede hacer con el ViewState, que aún queda mucha tela por cortar.