2. Una breve introducción a ASP.NET
Aquí nos proponemos presentar, con la ayuda de algunos ejemplos, los conceptos de ASP.NET que nos serán útiles en el resto del documento. Esta introducción no permite comprender las sutilezas de las interacciones cliente-servidor de una aplicación web. Para ello, se puede consultar:
- Programación ASP.NET [Desarrollo web con ASP.NET 1.1 (2004)]
Esta introducción está dirigida a quienes deseen avanzar rápidamente, aceptando, en un primer momento, dejar de lado algunos puntos que pueden ser importantes. El resto del documento permite profundizar en ellos. Quienes ya conocen ASP.NET pueden pasar directamente al párrafo 3.
2.1. Un proyecto de ejemplo
2.1.1. Creación del proyecto
![]() |
- En [1], se crea un nuevo proyecto con Visual Web Developer
- en [2], se elige un proyecto web en Visual C#
- en [3], se indica que se desea crear una aplicación web ASP.NET
- en [4], le damos un nombre a la aplicación. Se creará una carpeta para el proyecto con ese nombre.
- En [5], se indica la carpeta principal de la carpeta [4] del proyecto
![]() |
- En [6], el proyecto creado
- [Default.aspx] es una página web creada por defecto. Contiene etiquetas HTML y etiquetas ASP.NET
- [Default.aspx.cs] contiene el código para gestionar los eventos provocados por el usuario en la página [Defaul.aspx] que se muestra en su navegador
- [Default.aspx.designer.cs] contiene la lista de componentes ASP.NET de la página [Default.aspx]. Cada componente ASP.NET incluido en la página [Default.aspx] da lugar a la declaración de dicho componente en [Default.aspx.designer.cs].
- [Web.config] es el archivo de configuración del proyecto ASP.NET.
- [References] es la lista de los DLL utilizados por el proyecto web. Estos DLL son bibliotecas de clases que el proyecto utilizará. En [7] se encuentra la lista de DLL que se incluyen por defecto en las referencias del proyecto. La mayoría son innecesarias. Si el proyecto necesita utilizar una DLL que no aparece en [7], esta se puede agregar mediante [8].
2.1.2. La página [Default.aspx]
Si se ejecuta el proyecto mediante [Ctrl-F5], se muestra la página [Default.aspx] en un navegador:
![]() |
- en [1], la página URL del proyecto web. Visual Web Developer cuenta con un servidor web integrado que se inicia al solicitar la ejecución de un proyecto. Este servidor escucha en un puerto aleatorio, en este caso el 1490. El puerto de escucha suele ser el 80. En [1], no se solicita ninguna página. En este caso, se muestra la página [Default.aspx], de ahí su nombre de página por defecto.
- En [2], la página [Default.aspx] está vacía.
- En Visual Web Developer, la página [Default.aspx] [3] se puede crear visualmente (pestaña [Design]) o mediante etiquetas (pestaña [Source])
- En [4], la página [Defaul.aspx] en modo [Design]. Se crea colocando en ella los componentes que se encuentran en la caja de herramientas [5].
![]() |
El modo [Source] [6] permite acceder al código fuente de la página:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Intro._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
</div>
</form>
</body>
</html>
- la línea 1 es una directiva ASP.NET que enumera ciertas propiedades de la página
- la directiva Page se aplica a una página web. Existen otras directivas, como Application, WebService, ... que se aplican a otros objetos ASP.NET
- el atributo CodeBehind indica el archivo que maneja los eventos de la página
- el atributo Language indica el lenguaje .NET utilizado por el archivo CodeBehind
- el atributo Inherits indica el nombre de la clase definida dentro del archivo CodeBehind
- El atributo AutoEventWireUp="true" indica que la vinculación entre un evento en [Default.aspx] y su controlador en [Defaul.aspx.cs] se realiza mediante el nombre del evento. Por lo tanto, elevento Load en la página [Default.aspx] será procesado por el método Page_Load de la clase Intro._Default, definida por el atributo Inherits.
- Las líneas 4 a 14 describen la página [Defaul.aspx] mediante etiquetas:
- HTML clásicas, como la etiqueta <body> o <div>
- ASP.NET. Estas son las etiquetas que tienen el atributo runat="server". Las etiquetas ASP.NET son procesadas por el servidor web antes de enviar la página al cliente. Se transforman en etiquetas HTML. Por lo tanto, el navegador del cliente recibe una página HTML estándar en la que ya no existen etiquetas ASP.NET.
La página [Default.aspx] se puede modificar directamente a partir de su código fuente. A veces es más sencillo que pasar por el modo [Design]. Modificamos el código fuente de la siguiente manera:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Intro._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Introduction ASP.NET</title>
</head>
<body>
<h3>Introduction à ASP.NET</h3>
<form id="form1" runat="server">
<div>
</div>
</form>
</body>
</html>
En la línea 6, le damos un título a la página mediante la etiqueta HTML <title>. En la línea 9, introducimos un texto en el cuerpo (<body>) de la página. Si ejecutamos el proyecto (Ctrl-F5), obtenemos el siguiente resultado en el navegador:
![]() |
2.1.3. Los archivos [Default.aspx.designer.cs] y [Default.aspx.cs]
El archivo [Default.aspx.designer.cs] declara los componentes de la página [Defaul.aspx]:
//------------------------------------------------------------------------------
// <generado automáticamente>
// Este código fue generado por una herramienta.
// Versión del tiempo de ejecución: 2.0.50727.3603
//
// Los cambios realizados en este archivo pueden provocar un comportamiento incorrecto y se perderán si
// se vuelve a generar el código.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Intro {
public partial class _Default {
/// <summary>
/// Control form1.
/// </summary>
/// <remarks>
/// Campo generado automáticamente.
/// Para modificarlo, mueva la declaración del campo del archivo de diseño al archivo de código subyacente.
/// </remarks>
protected global::System.Web.UI.HtmlControls.HtmlForm form1;
}
}
En este archivo se encuentra la lista de los componentes ASP.NET de la página [Default.aspx] que cuentan con un identificador. Estos corresponden a las etiquetas de [Default.aspx] que tienen el atributo runat="server" y el atributo id. Así, el componente de la línea 23 anterior corresponde a la etiqueta
<form id="form1" runat="server">
de [Default.aspx].
El desarrollador interactúa poco con el archivo [Default.aspx.designer.cs]. Sin embargo, este archivo es útil para conocer la clase de un componente en particular. Así, se observa a continuación que el componente form1 es de tipo HtmlForm. El desarrollador puede entonces explorar esta clase para conocer sus propiedades y métodos. Los componentes de la página [Default.aspx] son utilizados por la clase del archivo [Default.aspx.cs]:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace Intro
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
}
Cabe señalar que la clase definida en los archivos [Default.aspx.cs] y [Default.aspx.designer.cs] es la misma (línea 10): Intro._Default. Es la palabra clave partial la que permite extender la declaración de una clase a varios archivos, en este caso dos.
En la línea 10, arriba, se ve que la clase [_Default] extiende la clase [Page] y hereda sus eventos. Uno de ellos es el evento Load, que se produce cuando el servidor web carga la página. En la línea 12, el método Page_Load gestiona el evento Load de la página. Por lo general, es aquí donde se inicializa la página antes de mostrarla en el navegador del cliente. En este caso, el método Page_Load no hace nada.
La clase asociada a una página web, en este caso la clase Intro._Default, se crea al inicio de la solicitud del cliente y se destruye una vez que se ha enviado la respuesta al cliente. Por lo tanto, no puede utilizarse para almacenar información entre dos solicitudes. Para ello, es necesario utilizar el concepto de sesión de usuario.
2.2. Los eventos de una página web ASP.NET
Creamos la siguiente página [Default.aspx]:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Intro._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Introduction ASP.NET</title>
</head>
<body>
<h3>Introduction à ASP.NET</h3>
<form id="form1" runat="server">
<div>
<table>
<tr>
<td>
Nom</td>
<td>
<asp:TextBox ID="TextBoxNom" runat="server"></asp:TextBox>
</td>
<td>
</td>
</tr>
<tr>
<td>
Age</td>
<td>
<asp:TextBox ID="TextBoxAge" runat="server"></asp:TextBox>
</td>
<td>
</td>
</tr>
</table>
</div>
<asp:Button ID="ButtonValider" runat="server" Text="Valider" />
<hr />
<p>
Evénements traités par le serveur</p>
<p>
<asp:ListBox ID="ListBoxEvts" runat="server"></asp:ListBox>
</p>
</form>
</body>
</html>
El modo de la página [Design] es el siguiente:
![]() |
El archivo [Default.aspx.designer.cs] es el siguiente:
namespace Intro {
public partial class _Default {
protected global::System.Web.UI.HtmlControls.HtmlForm form1;
protected global::System.Web.UI.WebControls.TextBox TextBoxNom;
protected global::System.Web.UI.WebControls.TextBox TextBoxAge;
protected global::System.Web.UI.WebControls.Button ButtonValider;
protected global::System.Web.UI.WebControls.ListBox ListBoxEvts;
}
}
En él se encuentran todos los componentes ASP.NET de la página [Default.aspx] que cuentan con un identificador.
Actualizamos el archivo [Default.aspx.cs] de la siguiente manera:
using System;
namespace Intro
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Init(object sender, EventArgs e)
{
// se registra el evento
ListBoxEvts.Items.Insert(0, string.Format("{0}: Page_Init", DateTime.Now.ToString("hh:mm:ss")));
}
protected void Page_Load(object sender, EventArgs e)
{
// se registra el evento
ListBoxEvts.Items.Insert(0, string.Format("{0}: Page_Load", DateTime.Now.ToString("hh:mm:ss")));
}
protected void ButtonValider_Click(object sender, EventArgs e)
{
// se registra el evento
ListBoxEvts.Items.Insert(0, string.Format("{0}: ButtonValider_Click", DateTime.Now.ToString("hh:mm:ss")));
}
}
}
La clase [_Default] (línea 5) maneja tres eventos:
- el evento Init (línea 7), que ocurre cuando se ha inicializado la página
- el evento Load (línea 13), que ocurre cuando el servidor web ha cargado la página. El evento Init ocurre antes que el evento Load.
- el evento Click en el botón ButtonValider (línea 19), que ocurre cuando el usuario hace clic en el botón [Valider]
El manejo de cada uno de estos tres eventos consiste en agregar un mensaje al componente Listbox denominado ListBoxEvts. Este mensaje muestra la hora del evento y su nombre. Cada mensaje se coloca al inicio de la lista. Por lo tanto, los mensajes que aparecen en la parte superior de la lista son los más recientes.
Al ejecutar el proyecto, se obtiene la siguiente página:
![]() |
En [1] se puede ver que los eventos Page_Init y Page_Load ocurrieron en ese orden. Cabe recordar que el evento más reciente aparece al principio de la lista. Cuando el navegador solicita la página [Default.aspx] directamente mediante su URL [2], lo hace mediante un comando HTTP (Protocolo de transferencia HyperText) denominado GET. Una vez que la página se ha cargado en el navegador, el usuario provocará eventos en la página. Por ejemplo, hará clic en el botón [Valider] [3]. Los eventos provocados por el usuario, una vez que la página se ha cargado en el navegador, activan una solicitud a la página [Default.aspx], pero esta vez con un comando HTTP llamado POST. En resumen:
- la carga inicial de una página P en un navegador se realiza mediante una operación HTTP GET
- los eventos que ocurren posteriormente en la página generan cada vez una nueva solicitud hacia la misma página P, pero esta vez con un comando HTTP POST. Una página P puede saber si fue solicitada con un comando GET o con un comando POST, lo que le permite comportarse de manera diferente si es necesario, lo cual suele ser el caso la mayoría de las veces.
Solicitud inicial de una página ASPX: GET
![]() |
- a [1], el navegador solicita la página ASPX mediante un comando HTTP GET sin parámetros.
- En [2], el servidor web le envía como respuesta el flujo HTML, que es la traducción de la página ASPX solicitada.
Procesamiento de un evento generado en la página mostrada por el navegador: POST
![]() |
- en [1], durante un evento en la página HTML, el navegador solicita la página ASPX, que ya se había obtenido mediante una operación GET, esta vez con un comando HTTP POST acompañado de parámetros. Estos parámetros son los valores de los componentes que se encuentran dentro de la etiqueta <form> de la página HTML que muestra el navegador. A estos valores se les denomina «valores enviados por el cliente». La página ASPX los utilizará para procesar la solicitud del cliente.
- En [2], el servidor web le envía como respuesta el flujo HTML, que es la traducción de la página ASPX solicitada inicialmente por POST, o bien de otra página si se ha producido una transferencia o redirección de página.
Volvamos a nuestra página de ejemplo:
![]() |
- en [2], la página se obtuvo mediante un GET.
- en [1], vemos los dos eventos que ocurrieron durante este GET
Si, en la imagen anterior, el usuario hace clic en el botón [Valider] [3], se solicitará la página [Default.aspx] mediante un POST. Este POST irá acompañado de parámetros que serán los valores de todos los componentes incluidos en la etiqueta <form> de la página [Default.aspx]: los dos TextBox y [TextBoxNom, TextBoxAge], el botón [ButtonValider] y la lista [ListBoxEvts]. Los valores enviados para los componentes son los siguientes:
- TextBox: el valor ingresado
- Button: el texto del botón, en este caso el texto «Validar»
- Listbox: el texto del mensaje seleccionado en el ListBox
En respuesta al POST, se obtiene la página [4]. De nuevo, es la página [Default.aspx]. Este es el comportamiento normal, a menos que haya una transferencia o redirección de página por parte de los administradores de eventos de la página. Se puede observar que se han producido dos nuevos eventos:
- el evento Page_Load, que ocurrió al cargar la página
- el evento ButtonValider_Click, que ocurrió al hacer clic en el botón [Valider]
Se puede observar que:
- el evento Page_Init no se produjo en la operación HTTP POST, mientras quesí se había producido en el evento HTTP GET
- el evento Page_Load ocurre siempre, ya sea en un GET o en un POST. Es en este método donde, por lo general, necesitamos saber si se trata de un GET o de un POST.
- Al finalizar el POST, la página [Default.aspx] se devolvió al cliente con las modificaciones realizadas por los controladores de eventos. Siempre es así. Una vez procesados los eventos de una página P, esa misma página P se devuelve al cliente. Hay dos formas de salirse de esta regla. El último manejador de eventos ejecutado puede
- transferir el flujo de ejecución a otra página P2.
- redirigir el navegador del cliente a otra página P2.
En ambos casos, es la página P2 la que se devuelve al navegador. Ambos métodos presentan diferencias sobre las que volveremos más adelante.
- El evento ButtonValider_Click ocurrió después del evento Page_Load. Por lo tanto, es este controlador el que puede tomar la decisión de transferir o redirigir a una página P2.
- La lista de eventos [4] conservó los dos eventos que se mostraron durante la carga inicial GET de la página [Default.aspx]. Esto resulta sorprendente si se tiene en cuenta que la página [Default.aspx] se recreó durante el POST. Deberíamos encontrar la página [Default.aspx] con sus valores de diseño y, por lo tanto, un ListBox vacío. La ejecución de los procesadores Page_Load y ButtonValider_Click debería colocar allí dos mensajes. Sin embargo, se encuentran cuatro. Esto se explica por el mecanismo del VIEWSTATE. Durante el GET inicial, el servidor web envía la página [Default.aspx] con una etiqueta HTML <input type="hidden" ...> denominada campo oculto (línea 10 a continuación).
En el campo de identificación «__VIEWSTATE», el servidor web codifica el valor de todos los componentes de la página. Lo hace tanto con el GET inicial como con los POST que le siguen. Cuando aparece un POST en una página P:
- el navegador solicita la página P enviando en su solicitud los valores de todos los componentes que se encuentran dentro de la etiqueta <form>. En el ejemplo anterior, se puede observar que el componente «__VIEWSTATE» se encuentra dentro de la etiqueta <form>. Por lo tanto, su valor se envía al servidor durante un POST.
- La página P se instancia e inicializa con sus valores de construcción
- el componente «__VIEWSTATE» se utiliza para devolver a los componentes los valores que tenían cuando la página P se envió anteriormente. Así, por ejemplo, la lista de eventos [4] recupera los dos primeros mensajes que tenía cuando se envió en respuesta al GET inicial del navegador.
- Los componentes de la página P toman entonces como valores los valores enviados por el navegador. En ese momento, el formulario de la página P se encuentra en el estado en el que el usuario lo envió.
- Se procesa el evento Page_Load. Aquí se agrega un mensaje a la lista de eventos [4].
- Se procesa el evento que provocó el POST. Aquí, ButtonValider_Click agrega un mensaje a la lista de eventos [4].
- Se devuelve la página P. Los componentes tienen como valor:
- el valor enviado, c.a.d; el valor que el componente tenía en el formulario cuando este se envió al servidor;
- o bien un valor proporcionado por uno de los controladores de eventos.
En nuestro ejemplo,
- los dos componentes TextBox recuperarán su valor enviado, ya que los controladores de eventos no los modifican
- la lista de eventos [4] recupera su valor enviado, c.a.d. Todos los eventos ya registrados en la lista, más dos nuevos eventos creados por los métodos Page_Load y ButtonValider_Click.
El mecanismo de VIEWSTATE se puede activar o desactivar a nivel de cada componente. Desactivémoslo para el componente [ListBoxEvts]:
![]() |
- En [1], el VIEWSTATE del componente [ListBoxEvts] está desactivado. El de los TextBox y [2] está activado por defecto.
- En [3], los dos eventos devueltos después del GET inicial
![]() |
- en [4], se ha llenado el formulario y se hace clic en el botón [Valider]. Se realizará un POST hacia la página [Default.aspx].
- En [6], el resultado que se muestra después de hacer clic en el botón [Valider]
- El mecanismo de VIEWSTATE activado explica que TextBox y [7] hayan conservado su valor registrado en [4]
- el mecanismo desactivado del VIEWSTATE explica que el componente [ListBoxEvts] y [8] no hayan conservado su contenido en [5].
2.3. Manejo de los valores enviados
Aquí nos centraremos en los valores enviados por los dos TextBox cuando el usuario hace clic en el botón [Valider]. La página [Default.aspx] en modo [Design] cambia de la siguiente manera:
![]() |
El código fuente del elemento agregado en [1] es el siguiente:
<p>
Eléments postés au serveur :
<asp:Label ID="LabelPost" runat="server"></asp:Label>
</p>
Utilizaremos el componente [LabelPost] para mostrar los valores ingresados en los dos TextBox y [2]. El código del controlador de eventos [Default.aspx.cs] cambia de la siguiente manera:
using System;
namespace Intro
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Init(object sender, EventArgs e)
{
// se registra el evento
ListBoxEvts.Items.Insert(0, string.Format("{0}: Page_Init", DateTime.Now.ToString("hh:mm:ss")));
}
protected void Page_Load(object sender, EventArgs e)
{
// se registra el evento
ListBoxEvts.Items.Insert(0, string.Format("{0}: Page_Load", DateTime.Now.ToString("hh:mm:ss")));
}
protected void ButtonValider_Click(object sender, EventArgs e)
{
// se registra el evento
ListBoxEvts.Items.Insert(0, string.Format("{0}: ButtonValider_Click", DateTime.Now.ToString("hh:mm:ss")));
// se muestra el nombre y la edad
LabelPost.Text = string.Format("nom={0}, age={1}", TextBoxNom.Text.Trim(), TextBoxAge.Text.Trim());
}
}
}
En la línea 24, se actualiza el componente LabelPost:
- LabelPost es de tipo [System.Web.UI.WebControls.Label] (véase Default.aspx.designer.cs). Su propiedad Text representa el texto que muestra el componente.
- TextBoxNom y TextBoxAge son del tipo [System.Web.UI.WebControls.TextBox]. La propiedad Text de un componente TextBox es el texto que se muestra en el campo de entrada.
- El método Trim() elimina los espacios que puedan preceder o seguir a una cadena de caracteres
Como se explicó anteriormente, cuando se ejecuta el método ButtonValider_Click, los componentes de la página tienen el valor que tenían cuando el usuario envió la página. Por lo tanto, las propiedades Text de ambos TextBox tienen como valor los textos ingresados por el usuario en el navegador.
A continuación se muestra un ejemplo:
![]() |
- en [1], los valores enviados
- en [2], la respuesta del servidor.
- en [3], los TextBox han recuperado el valor enviado mediante el mecanismo del VIEWSTATE activado
- en [4], los mensajes del componente ListBoxEvts provienen de los métodos Page_Init, Page_Load, ButtonValider_Click y de un VIEWSTATE desactivado
- En [5], el componente LabelPost obtuvo su valor mediante el método ButtonValider_Click. Se han recuperado correctamente los dos valores ingresados por el usuario en los componentes TextBox y [1].
Como se ve arriba, el valor enviado para la edad es la cadena «yy», un valor no válido. Vamos a agregar a la página unos componentes llamados validadores. Sirven para verificar la validez de los datos enviados. Esta validez se puede verificar en dos lugares:
- en el lado del cliente. Una opción de configuración del validador permite elegir si las pruebas se realizan en el navegador o no. En ese caso, las pruebas se llevan a cabo mediante código JavaScript integrado en la página HTML. Cuando el usuario envía los valores ingresados en el formulario, estos son verificados primero por el código JavaScript. Si alguna de las pruebas falla, no se realiza el envío. De esta manera se evita un intercambio de datos con el servidor, lo que hace que la página sea más receptiva.
- en el servidor. Si bien las validaciones del lado del cliente pueden ser opcionales, del lado del servidor son obligatorias, independientemente de si se han realizado o no en el lado del cliente. De hecho, cuando una página recibe valores enviados, no tiene forma de saber si el cliente los ha validado antes de enviarlos. Por lo tanto, del lado del servidor, el desarrollador siempre debe verificar la validez de los datos enviados.
La página [Default.aspx] evoluciona de la siguiente manera:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Intro._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Introduction ASP.NET</title>
</head>
<body>
<h3>Introduction à ASP.NET</h3>
<form id="form1" runat="server">
<div>
<table>
<tr>
<td>
Nom</td>
<td>
<asp:TextBox ID="TextBoxNom" runat="server"></asp:TextBox>
</td>
<td>
<asp:RequiredFieldValidator ID="RequiredFieldValidatorNom" runat="server"
ControlToValidate="TextBoxNom" Display="Dynamic"
ErrorMessage="Donnée obligatoire !"></asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<td>
Age</td>
<td>
<asp:TextBox ID="TextBoxAge" runat="server"></asp:TextBox>
</td>
<td>
<asp:RequiredFieldValidator ID="RequiredFieldValidatorAge" runat="server"
ControlToValidate="TextBoxAge" Display="Dynamic"
ErrorMessage="Donnée obligatoire !"></asp:RequiredFieldValidator>
<asp:RangeValidator ID="RangeValidatorAge" runat="server"
ControlToValidate="TextBoxAge" Display="Dynamic"
ErrorMessage="Tapez un nombre entre 1 et 150 !" MaximumValue="150"
MinimumValue="1" Type="Integer"></asp:RangeValidator>
</td>
</tr>
</table>
</div>
<asp:Button ID="ButtonValider" runat="server" onclick="ButtonValider_Click"
Text="Valider" CausesValidation="False"/>
<hr />
<p>
Evénements traités par le serveur</p>
<p>
<asp:ListBox ID="ListBoxEvts" runat="server" EnableViewState="False">
</asp:ListBox>
</p>
<p>
Eléments postés au serveur :
<asp:Label ID="LabelPost" runat="server"></asp:Label>
</p>
<p>
Eléments validés par le serveur :
<asp:Label ID="LabelValidation" runat="server"></asp:Label>
</p>
<asp:Label ID="LabelErreursSaisie" runat="server" ForeColor="Red"></asp:Label>
</form>
</body>
</html>
Se han agregado validadores en las líneas 20, 32 y 35. En la línea 58, se utiliza un componente Label para mostrar los valores enviados que son válidos. En la línea 60, se utiliza un componente Label para mostrar un mensaje de error si hay errores de entrada.
La página [Default.aspx] en modo [Design] es la siguiente:
![]() |
- Los componentes [1] y [2] son del tipo RequiredFieldValidator. Este validador verifica que un campo de entrada no esté vacío.
- El componente [3] es del tipo RangeValidator. Este validador verifica que un campo de entrada contenga un valor entre dos límites.
- En [4], las propiedades del validador [1].
Presentaremos los dos tipos de validadores a través de sus etiquetas en el código de la página [Default.aspx]:
<asp:RequiredFieldValidator ID="RequiredFieldValidatorNom" runat="server"
ControlToValidate="TextBoxNom" Display="Dynamic"
ErrorMessage="Donnée obligatoire !"></asp:RequiredFieldValidator>
- ID: el identificador del componente
- ControlToValidate: el nombre del componente cuyo valor se verifica. En este caso, queremos que el componente TextBoxNom no tenga un valor vacío (cadena vacía o secuencia de espacios)
- ErrorMessage: mensaje de error que se mostrará en el validador en caso de datos inválidos.
- EnableClientScript: valor booleano que indica si el validador también debe ejecutarse del lado del cliente. Este atributo tiene el valor True por defecto cuando no se establece explícitamente como se indica arriba.
- Display: modo de visualización del validador. Hay dos modos:
- static (predeterminado): el validador ocupa espacio en la página aunque no muestre ningún mensaje de error
- dynamic: el validador no ocupa espacio en la página si no muestra ningún mensaje de error.
<asp:RangeValidator ID="RangeValidatorAge" runat="server"
ControlToValidate="TextBoxAge" Display="Dynamic"
ErrorMessage="Tapez un nombre entre 1 et 150 !" MaximumValue="150"
MinimumValue="1" Type="Integer"></asp:RangeValidator>
- Tipo: el tipo de los datos verificados. En este caso, la edad es un número entero.
- MinimumValue, MaximumValue: los límites dentro de los cuales debe estar el valor verificado
La configuración del componente que genera el POST influye en el modo de validación. En este caso, ese componente es el botón [Valider]:
<asp:Button ID="ButtonValider" runat="server" onclick="ButtonValider_Click" Text="Valider" CausesValidation="True" />
- CausesValidation: establece el modo automático o el nombre de las validaciones del lado del servidor. Este atributo tiene el valor predeterminado «True» si no se menciona explícitamente. En este caso,
- del lado del cliente, se ejecutan los validadores con valores desde EnableClientScript hasta True. El POST solo se ejecuta si todos los validadores del lado del cliente tienen éxito.
- Del lado del servidor, todos los validadores presentes en la página se ejecutan automáticamente antes del procesamiento del evento que provocó el POST. En este caso, se ejecutarían antes de la ejecución del método ButtonValider_Click. En este método, es posible saber si todas las validaciones se han realizado con éxito o no. Page.IsValid es «True» si todas han tenido éxito, «False» en caso contrario. En este último caso, se puede detener el procesamiento del evento que provocó el POST. La página enviada se devuelve tal como se ingresó. Los validadores que no hayan pasado la validación muestran entonces su mensaje de error (atributo ErrorMessage).
Si CausesValidation tiene el valor False, entonces
- del lado del cliente, no se ejecuta ningún validador
- En el lado del servidor, es el desarrollador quien debe solicitar la ejecución de los validadores de la página. Lo hace mediante el método Page.Validate(). Según el resultado de las validaciones, este método establece la propiedad Page.IsValid en «True» o «False».
En [Default.aspx.cs], el código de procesamiento de ButtonValider_Click cambia de la siguiente manera:
protected void ButtonValider_Click(object sender, EventArgs e)
{
// se registra el evento
ListBoxEvts.Items.Insert(0, string.Format("{0}: ButtonValider_Click", DateTime.Now.ToString("hh:mm:ss")));
// se muestra el nombre y la edad
LabelPost.Text = string.Format("nom={0}, age={1}", TextBoxNom.Text.Trim(), TextBoxAge.Text.Trim());
// ¿Es válida la página?
Page.Validate();
if (!Page.IsValid)
{
// mensaje de error general
LabelErreursSaisie.Text = "Veuillez corriger les erreurs de saisie...";
LabelErreursSaisie.Visible = true;
return;
}
// se oculta el mensaje de error
LabelErreursSaisie.Visible = false;
// se muestran el nombre y la edad validados
LabelValidation.Text = string.Format("nom={0}, age={1}", TextBoxNom.Text.Trim(), TextBoxAge.Text.Trim());
}
En el caso de que el botón [Valider] tenga su atributo CausesValidation en True y los validadores tengan su atributo EnableClientScript en True, el método ButtonValider_Click solo se ejecuta cuando los valores enviados son válidos. Entonces, cabe preguntarse cuál es el sentido del código que aparece a partir de la línea 8. Hay que recordar que siempre es posible escribir un cliente programado que envíe valores no verificados a la página [Default.aspx]. Por lo tanto, esta debe volver a realizar siempre las pruebas de validez.
- Línea 8: inicia la ejecución de todos los validadores de la página. En el caso de que el botón [Valider] tenga su atributo CausesValidation establecido en True, esto se realiza automáticamente y no es necesario volver a hacerlo. Aquí hay redundancia.
- líneas 9-15: caso en el que uno de los validadores ha fallado
- líneas 16-19: caso en el que todos los validadores han pasado la prueba
A continuación se muestran dos ejemplos de ejecución:
![]() |
- en [1], un ejemplo de ejecución en el caso en que:
- el botón [Valider] tiene su propiedad CausesValidation en True
- los validadores tienen su propiedad EnableClientScript cambiada a True
Los mensajes de error [2] fueron mostrados por los validadores ejecutados del lado del cliente mediante el código JavaScript de la página. No se envió ningún POST al servidor, como lo muestra la etiqueta de los elementos enviados [3].
- En [4], un ejemplo de ejecución en el caso en que:
- el botón [Valider] tiene su propiedad CausesValidation establecida en False
- los validadores tienen su propiedad EnableClientScript establecida en False
Los validadores ejecutados del lado del servidor mostraron los mensajes de error [5]. Como lo muestra [6], efectivamente se envió un POST al servidor. En [7], el mensaje de error mostrado por el método [ButtonValider_Click] en caso de errores de entrada.
![]() |
- En [8], un ejemplo obtenido con datos válidos. [9,10] muestra que los elementos enviados han sido validados. Al realizar pruebas repetidas, es necesario establecer en False la propiedad EnableViewState de la etiqueta [LabelValidation] para que el mensaje de validación no permanezca visible a lo largo de las ejecuciones.
2.4. Gestión de datos de ámbito de la aplicación
Volvamos a la arquitectura de ejecución de una página ASPX:
![]() |
La clase de la página ASPX se instancia al inicio de la solicitud del cliente y se destruye al final de la misma. Por lo tanto, no puede utilizarse para almacenar datos entre dos solicitudes. Es posible que se desee almacenar dos tipos de datos:
- datos compartidos por todos los usuarios de la aplicación web. Por lo general, se trata de datos de solo lectura. Se utilizan tres archivos para implementar este intercambio de datos:
- [Web.Config]: el archivo de configuración de la aplicación
- [Global.asax, Global.asax.cs]: permite definir una clase, llamada clase global de la aplicación, cuya vida útil es la misma que la de la aplicación, así como controladores para ciertos eventos de esa misma aplicación.
La clase global de la aplicación permite definir datos que estarán disponibles para todas las solicitudes de todos los usuarios.
- datos compartidos por las solicitudes de un mismo cliente. Estos datos se almacenan en un objeto llamado «Sesión». Se habla entonces de «sesión de cliente» para referirse a la memoria del cliente. Todas las solicitudes de un cliente tienen acceso a esta sesión. Pueden almacenar y leer información en ella
![]() |
Arriba, mostramos los tipos de memoria a los que tiene acceso una página ASPX:
- la memoria de la aplicación, que en la mayoría de los casos contiene datos de solo lectura y a la que pueden acceder todos los usuarios.
- la memoria de un usuario específico, o sesión, que contiene datos de lectura y escritura y a la que pueden acceder las consultas sucesivas de un mismo usuario.
- Aunque no se muestra arriba, existe una memoria de solicitud, o contexto de solicitud. La solicitud de un usuario puede ser procesada por varias páginas ASPX sucesivas. El contexto de la solicitud permite que una página 1 transmita información a una página 2.
Aquí nos interesan los datos de ámbito Application, aquellos que comparten todos los usuarios. La clase global de la aplicación se puede crear de la siguiente manera:
![]() |
- en [1], se agrega un nuevo elemento al proyecto
- en [2], se agrega la clase global de la aplicación
- en [3], se mantiene el nombre predeterminado [Global.asax] para el nuevo elemento
![]() |
- en [4], se agregaron dos archivos nuevos al proyecto
- en [5], se muestra el código de [Global.asax]
<%@ Application Codebehind="Global.asax.cs" Inherits="Intro.Global" Language="C#" %>
- La etiqueta Application reemplaza a la etiqueta Page que teníamos para [Default.aspx]. Identifica la clase de aplicación global
- Codebehind: define el archivo en el que se define la clase de aplicación global
- Inherits: define el nombre de esta clase
La clase Intro.Global generada es la siguiente:
using System;
namespace Intro
{
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
}
protected void Session_Start(object sender, EventArgs e)
{
}
protected void Application_BeginRequest(object sender, EventArgs e)
{
}
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
}
protected void Application_Error(object sender, EventArgs e)
{
}
protected void Session_End(object sender, EventArgs e)
{
}
protected void Application_End(object sender, EventArgs e)
{
}
}
}
- línea 5: la clase global de la aplicación deriva de la clase HttpApplication
La clase se genera con plantillas de manejadores de eventos de la aplicación:
- líneas 8 y 38: gestionan los eventos Application_Start (inicio de la aplicación) y Application_End (fin de la aplicación cuando el servidor web se detiene o cuando el administrador cierra la aplicación)
- líneas 13, 33: gestionan los eventos Session_Start (inicio de una nueva sesión de cliente al llegar un nuevo cliente o al vencer una sesión existente) y Session_End (fin de una sesión de cliente, ya sea explícitamente por programación o implícitamente al excederse el tiempo permitido para una sesión).
- línea 28: gestiona el evento Application_Error (aparición de una excepción no manejada por el código de la aplicación y que se remite al servidor)
- línea 18: maneja el evento Application_BeginRequest (llegada de una nueva solicitud).
- línea 23: gestiona el evento Application_AuhenticateRequest (se produce cuando un usuario se ha autenticado).
El método [Application_Start] se utiliza a menudo para inicializar la aplicación a partir de la información contenida en [Web.Config]. El que se genera al crear un proyecto por primera vez tiene el siguiente aspecto:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
...
</configSections>
<appSettings/>
<connectionStrings/>
<system.web>
...
</system.web>
<system.codedom>
....
</system.codedom>
<!--
La section system.webServer est requise pour exécuter ASP.NET AJAX sur Internet
Information Services 7.0. Elle n'est pas nécessaire pour les versions précédentes d'IIS.
-->
<system.webServer>
...
</system.webServer>
<runtime>
....
</runtime>
</configuration>
Para nuestra aplicación actual, este archivo no es necesario. Si lo eliminamos o le cambiamos el nombre, la aplicación seguirá funcionando con normalidad. Nos enfocaremos en las etiquetas de las líneas 8 y 9:
- <appsettings> permite definir un diccionario de información
- <connectionStrings> permite definir cadenas de conexión a bases de datos
Consideremos el siguiente archivo [Web.config]:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
...
</configSections>
<appSettings>
<add key="cle1" value="valeur1"/>
<add key="cle2" value="valeur2"/>
</appSettings>
<connectionStrings>
<add connectionString="connectionString1" name="conn1"/>
</connectionStrings>
<system.web>
...
Este archivo puede ser utilizado por la siguiente clase global de la aplicación:
using System;
using System.Configuration;
namespace Intro
{
public class Global : System.Web.HttpApplication
{
public static string Param1 { get; set; }
public static string Param2 { get; set; }
public static string ConnString1 { get; set; }
public static string Erreur { get; set; }
protected void Application_Start(object sender, EventArgs e)
{
try
{
Param1 = ConfigurationManager.AppSettings["cle1"];
Param2 = ConfigurationManager.AppSettings["cle2"];
ConnString1 = ConfigurationManager.ConnectionStrings["conn1"].ConnectionString;
}
catch (Exception ex)
{
Erreur = string.Format("Erreur de configuration : {0}", ex.Message);
}
}
protected void Session_Start(object sender, EventArgs e)
{
}
}
}
- líneas 8-11: cuatro propiedades estáticas P. Dado que el ciclo de vida de la clase Global es el mismo que el de la aplicación, cualquier consulta realizada a la aplicación tendrá acceso a estas propiedades P mediante la sintaxis Global.P.
- líneas 17-19: se puede acceder al archivo [Web.config] a través de la clase [System.Configuration.ConfigurationManager]
- líneas 17-18: recupera los elementos de la etiqueta <appSettings> del archivo [Web.config] mediante el atributo key.
- línea 19: recupera los elementos de la etiqueta <connectionStrings> del archivo [Web.config] a través del atributo name.
Se puede acceder a los atributos estáticos de las líneas 8 a 11 desde cualquier manejador de eventos de las páginas ASPX cargadas. Los utilizamos en el manejador [Page_Load] de la página [Default.aspx]:
protected void Page_Load(object sender, EventArgs e)
{
// se registra el evento
ListBoxEvts.Items.Insert(0, string.Format("{0}: Page_Load", DateTime.Now.ToString("hh:mm:ss")));
// se recupera la información de la clase global de la aplicación
LabelGlobal.Text = string.Format("Param1={0},Param2={1},ConnString1={2},Erreur={3}", Global.Param1, Global.Param2, Global.ConnString1, Global.Erreur);
}
- línea 6: los cuatro atributos estáticos de la clase global de la aplicación se utilizan para alimentar una nueva etiqueta de la página [Default.aspx]
![]() |
Al ejecutarlo, obtenemos el siguiente resultado:
![]() |
Como se puede observar arriba, los parámetros de [web.config] se han recuperado correctamente. La clase global de la aplicación es el lugar adecuado para almacenar información compartida por todos los usuarios.
2.5. Manejo de datos de ámbito de sesión
Aquí nos interesa cómo almacenar información a lo largo de las solicitudes de un usuario específico:
![]() |
Cada usuario tiene su propia memoria, que se conoce como su sesión.
Hemos visto que la clase de aplicación global cuenta con dos controladores para gestionar los eventos:
- Session_Start: inicio de una sesión
- Session_end: fin de una sesión
El mecanismo de la sesión funciona de la siguiente manera:
- al recibir la primera solicitud de un usuario, el servidor web crea un token de sesión y se lo asigna al usuario. Este token es una secuencia de caracteres única para cada usuario. El servidor lo envía en la respuesta a la primera solicitud del usuario.
- En las solicitudes siguientes, el usuario (el navegador web) incluye en su solicitud el token de sesión que se le ha asignado. De esta manera, el servidor web puede reconocerlo.
- Una sesión tiene una duración determinada. Cuando el servidor web recibe una solicitud de un usuario, calcula el tiempo transcurrido desde la solicitud anterior. Si ese tiempo excede la duración de la sesión, se crea una nueva sesión para el usuario. Los datos de la sesión anterior se pierden. Con el servidor web IIS (Internet Information Server) de Microsoft, las sesiones tienen, por defecto, una duración de 20 minutos. El administrador del servidor web puede modificar este valor.
- El servidor web sabe que se trata de la primera solicitud de un usuario porque dicha solicitud no incluye un token de sesión. Es la única.
Cualquier página ASP.NET tiene acceso a la sesión del usuario a través de la propiedad Session de la página, de tipo [System.Web.SessionState.HttpSessionState]. Utilizaremos las siguientes propiedades P y métodos M de la clase HttpSessionState:
Nombre | Tipo | Función |
Item[String clé] | P | La sesión se puede estructurar como un diccionario. Item[clé] es el elemento de la sesión identificado por clé. En lugar de escribir [HttpSessionState].Item[clé], también se puede escribir [HttpSessionState].[clé]. |
Borrar | M | vacía el diccionario de la sesión |
Cancelar | M | termina la sesión. La sesión ya no es válida. Se iniciará una nueva sesión con la próxima solicitud del usuario. |
Como ejemplo de memoria de usuario, vamos a contar el número de veces que un usuario hace clic en el botón [Valider]. Para obtener este resultado, es necesario mantener un contador en la sesión del usuario.
La página [Default.aspx] evoluciona de la siguiente manera:
![]() |
La clase global de la aplicación [Global.asax.cs] evoluciona de la siguiente manera:
using System;
using System.Configuration;
namespace Intro
{
public class Global : System.Web.HttpApplication
{
public static string Param1 { get; set; }
...
protected void Application_Start(object sender, EventArgs e)
{
...
}
protected void Session_Start(object sender, EventArgs e)
{
// contador de consultas
Session["nbRequêtes"] = 0;
}
}
}
En la línea 19, se utiliza la sesión del usuario para almacenar un contador de solicitudes identificado por la clave «nbRequêtes». Este contador es actualizado por el controlador [ButtonValider_Click] de la página [Default.aspx]:
using System;
namespace Intro
{
public partial class _Default : System.Web.UI.Page
{
....
protected void ButtonValider_Click(object sender, EventArgs e)
{
// se registra el evento
ListBoxEvts.Items.Insert(0, string.Format("{0}: ButtonValider_Click", DateTime.Now.ToString("hh:mm:ss")));
// se muestra el nombre y la edad publicados
LabelPost.Text = string.Format("nom={0}, age={1}", TextBoxNom.Text.Trim(), TextBoxAge.Text.Trim());
// número de consultas
Session["nbRequêtes"] = (int)Session["nbRequêtes"] + 1;
LabelNbRequetes.Text = Session["nbRequêtes"].ToString();
// ¿Es válida la página?
Page.Validate();
if (!Page.IsValid)
{
...
}
...
}
}
}
- línea 16: se incrementa el contador de consultas
- línea 17: el contador se muestra en la página
A continuación se muestra un ejemplo de ejecución:
![]() |
2.6. Manejo de GET / POST al cargar una página
Ya mencionamos que existen dos tipos de solicitudes a una página ASPX:
- la solicitud inicial del navegador realizada con un comando HTTP GET. El servidor responde enviando la página solicitada. Supondremos que esta página es un formulario, c.a.d, y que en la página ASPX enviada hay una etiqueta <form runat="server"...>.
- Las siguientes solicitudes las realiza el navegador en respuesta a ciertas acciones del usuario en el formulario. El navegador entonces realiza una solicitud HTTP POST.
Ya sea en una solicitud GET o en una solicitud POST, se ejecuta el método [Page_Load]. En el caso de GET, este método se utiliza habitualmente para inicializar la página enviada al navegador del cliente. Posteriormente, mediante el mecanismo de VIEWSTATE, la página permanece inicializada y solo es modificada por los controladores de eventos que provocan los POST. No es necesario reiniciar la página en Page_Load. De ahí la necesidad de que este método sepa si la solicitud del cliente es un GET o un POST.
Veamos el siguiente ejemplo. Se agrega una lista desplegable a la página [Default.aspx]. El contenido de esta lista se definirá en el administrador Page_Load de la solicitud GET:
![]() |
La lista desplegable se declara en [Default.aspx.designer.cs] de la siguiente manera:
protected global::System.Web.UI.WebControls.DropDownList DropDownListNoms;
Utilizaremos los siguientes métodos M y propiedades P de la clase [DropDownList]:
Nombre | Tipo | Función |
Elementos | P | la colección de tipo ListItemCollection de los elementos de tipo ListItem de la lista desplegable |
SelectedIndex | P | el índice, que comienza en 0, del elemento seleccionado en la lista desplegable cuando se envía el formulario |
SelectedItem | P | el elemento de tipo ListItem seleccionado en la lista desplegable cuando se envía el formulario |
SelectedValue | P | el valor de tipo string del elemento de tipo ListItem seleccionado en la lista desplegable al enviar el formulario. Definiremos este concepto de valor más adelante. |
La clase ListItem de los elementos de una lista desplegable sirve para generar las etiquetas <option> de la etiqueta HTML <select>:
En la etiqueta <option>
- textei es el texto que se muestra en la lista desplegable
- vali es el valor enviado por el navegador si textei es el texto seleccionado en la lista desplegable
Cada opción puede generarse mediante un objeto LisItem creado con el constructor ListItem(string texto, string valor).
En [Default.aspx.cs], el código del controlador [Page_Load] cambia de la siguiente manera:
protected void Page_Load(object sender, EventArgs e)
{
// se registra el evento
...
// se recupera la información de la clase global de la aplicación
...
// Inicialización del combo de nombres únicamente durante el GET inicial
if (!IsPostBack)
{
for (int i = 0; i < 3; i++)
{
DropDownListNoms.Items.Add(new ListItem("nom"+i,i.ToString()));
}
}
}
- línea 8: la clase Page tiene un atributo IsPostBack de tipo booleano. En realidad, esto significa que la solicitud del usuario es un POST. Por lo tanto, las líneas 10 a 13 solo se ejecutan sobre el GET inicial del cliente.
- Línea 12: se agrega a la lista [DropDownListNoms] un elemento de tipo ListItem (cadena de texto, cadena de valor). El texto que se mostrará para el elemento (i+1) será nomi y el valor enviado para este elemento, si se selecciona, será i.
Se modifica el controlador [ButtonValider_Click] para mostrar el valor enviado por la lista desplegable:
protected void ButtonValider_Click(object sender, EventArgs e)
{
// se registra el evento
...
// se muestran los valores enviados
LabelPost.Text = string.Format("nom={0}, age={1}, combo={2}", TextBoxNom.Text.Trim(), TextBoxAge.Text.Trim(), DropDownListNoms.SelectedValue);
// número de consultas
...
}
En la línea 6, el valor publicado para la lista [DropDownListNoms] se obtiene mediante la propiedad SelectedValue de la lista. A continuación se muestra un ejemplo de ejecución:
![]() |
- en [1], el contenido de la lista desplegable después del GET inicial y justo antes del primer POST
- en [2], la página después del primer POST.
- en [3], el valor enviado para la lista desplegable. Corresponde al atributo value del ListItem seleccionado en la lista.
- En [4], la lista desplegable. Contiene los mismos elementos que después del GET inicial. Esto se debe al mecanismo del VIEWSTATE.
Para comprender la interacción entre el VIEWSTATE de la lista DropDownListNoms y la prueba if (! IsPostBack) del administrador Page_Load de [Default.aspx], se invita al lector a repetir la prueba anterior con las siguientes configuraciones:
Caso | DropDownListNoms.EnableViewState | prueba if(! IsPostBack) en Page_Load de [Default.aspx] |
Las diferentes pruebas arrojan los siguientes resultados:
- este es el caso presentado anteriormente
- la lista se llena durante el GET inicial, pero no durante los POST siguientes. Como EnableViewState es falso, la lista queda vacía después de cada POST
- la lista se completa tanto después del GET inicial como en los POST siguientes. Como EnableViewState corresponde a vrai, tenemos 3 nombres después del GET inicial, 6 nombres después del primer POST, 9 nombres después del segundo POST, ...
- La lista se completa tanto después del GET inicial como durante los POST siguientes. Como EnableViewState es igual a faux, la lista se completa con solo 3 nombres en cada consulta, ya sea la consulta inicial GET o las consultas siguientes POST. Se observa el mismo comportamiento que en el caso 1. Por lo tanto, hay dos formas de obtener el mismo resultado.
2.7. Manejo de VIEWSTATE de los elementos de una página ASPX
Por defecto, todos los elementos de una página ASPX tienen su propiedad EnableViewState establecida en True. Cada vez que la página ASPX se envía al navegador del cliente, contiene el campo oculto __VIEWSTATE, cuyo valor es una cadena de caracteres que codifica el conjunto de valores de los componentes que tienen su propiedad entre EnableViewState y True. Para minimizar el tamaño de esta cadena, se puede intentar reducir el número de componentes cuyas propiedades van de EnableViewState a True.
Recordemos cómo los componentes de una página ASPX obtienen sus valores al finalizar un POST:
- se instancia la página ASPX. Los componentes se inicializan con sus valores de diseño.
- el valor __VIEWSTATE enviado por el navegador se utiliza para asignar a los componentes el valor que tenían cuando la página ASPX se envió al navegador la vez anterior.
- Los valores enviados por el navegador se asignan a los componentes
- Se ejecutan los controladores de eventos. Estos pueden modificar el valor de ciertos componentes.
De esta secuencia se deduce que los componentes que:
- tienen su valor enviado
- tienen su valor modificado por un manejador de eventos
pueden tener su propiedad de EnableViewState a Faux, ya que su valor de VIEWSTATE (paso 2) será modificado en uno de los pasos 3 o 4.
La lista de componentes de nuestra página está disponible en [Default.aspx.designer.cs]:
namespace Intro {
public partial class _Default {
protected global::System.Web.UI.HtmlControls.HtmlForm form1;
protected global::System.Web.UI.WebControls.TextBox TextBoxNom;
protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidatorNom;
protected global::System.Web.UI.WebControls.TextBox TextBoxAge;
protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidatorAge;
protected global::System.Web.UI.WebControls.RangeValidator RangeValidatorAge;
protected global::System.Web.UI.WebControls.DropDownList DropDownListNoms;
protected global::System.Web.UI.WebControls.Button ButtonValider;
protected global::System.Web.UI.WebControls.ListBox ListBoxEvts;
protected global::System.Web.UI.WebControls.Label LabelPost;
protected global::System.Web.UI.WebControls.Label LabelValidation;
protected global::System.Web.UI.WebControls.Label LabelErreursSaisie;
protected global::System.Web.UI.WebControls.Label LabelGlobal;
protected global::System.Web.UI.WebControls.Label LabelNbRequetes;
}
}
El valor de la propiedad EnableViewState de estos componentes podría ser el siguiente:
Composant | Valor publicado | EnableViewState | Pourquoi |
TextBoxNom | valor ingresado en el TextBox | Falso | el valor del componente se contabiliza |
TextBoxAge | lo mismo | ||
RequiredFieldValidatorNom | ninguno | Falso | no se ha definido el valor del componente |
RequiredFieldValidatorAge | lo mismo | ||
RangeValidatorAge | lo mismo | ||
LabelPost | ninguna | Falso | obtiene su valor mediante un controlador de eventos |
LabelValidation | lo mismo | ||
LabelErreursSaisie | lo mismo | ||
LabelGlobal | ídem | ||
LabelNbRequetes | ídem | ||
DropDownListNoms | «valor» del elemento seleccionado | True | queremos conservar el contenido de la lista a lo largo de las consultas sin tener que regenerarla |
ListBoxEvts | "valor" del elemento seleccionado | Falso | El contenido de la lista es generado por un controlador de eventos |
ButtonValider | Texto del botón | Falso | El componente conserva su valor de diseño |
2.8. Redirección de una página a otra
Hasta ahora, las operaciones GET y POST siempre devolvían la misma página [Default.aspx]. Consideraremos el caso en el que una solicitud es procesada por dos páginas ASPX sucesivas, [Default.aspx] y [Page1.aspx], y en el que es esta última la que se devuelve al cliente. Además, veremos cómo la página [Default.aspx] puede transmitir información a la página [Page1.aspx] a través de una memoria que llamaremos «memoria de la solicitud».
![]() |
Creamos la página [Page1.aspx]:
![]() |
- en [1], agregamos un nuevo elemento al proyecto
- en [2], se agrega un elemento [Web Form] llamado [Page1.aspx] [3]
![]() |
- en [4], la página agregada
- en [5], la página una vez generada
El código fuente de [Page1.aspx] es el siguiente:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Page1.aspx.cs" Inherits="Intro.Page1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Page1</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<h1>
Page 1</h1>
<asp:Label ID="Label1" runat="server"></asp:Label>
<br />
<asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="~/Default.aspx">Retour
vers page [Default]</asp:HyperLink>
</div>
</form>
</body>
</html>
- línea 13: una etiqueta que servirá para mostrar información transmitida por la página [Default.aspx]
- línea 15: un enlace HTML a la página [Default.aspx]. Cuando el usuario hace clic en este enlace, el navegador solicita la página [Default.aspx] mediante una operación GET. La página [Default.aspx] se carga entonces como si el usuario hubiera escrito directamente su URL en el navegador.
La página [Default.aspx] se enriquece con un nuevo componente de tipo LinkButton:
![]() |
El código fuente de este nuevo componente es el siguiente:
<asp:LinkButton ID="LinkButtonToPage1" runat="server" CausesValidation="False"
EnableViewState="False" onclick="LinkButtonToPage1_Click">Forward vers Page1</asp:LinkButton>
- CausesValidation="False": al hacer clic en el enlace se activará un POST hacia [Defaul.aspx]. El componente [LinkButton] se comporta igual que el componente [Button]. En este caso, no queremos que al hacer clic en el enlace se activen los validadores.
- EnableViewState="False": no es necesario conservar el estado del enlace a lo largo de las solicitudes. Mantiene sus valores de diseño.
- onclick="LinkButtonToPage1_Click": nombre del método que, en [Defaul.aspx.cs], maneja el evento Click en el componente LinkButtonToPage1.
El código del controlador LinkButtonToPage1_Click es el siguiente:
// hacia la Página 1
protected void LinkButtonToPage1_Click(object sender, EventArgs e)
{
// se agrega información al contexto
Context.Items["msg1"] = "Message de Default.aspx pour Page1";
// se pasa la solicitud a la Página 1
Server.Transfer("Page1.aspx",true);
}
En la línea 7, la solicitud se transfiere a la página [Page1.aspx] mediante el método [Server.Transfer]. El segundo parámetro del método, que es true, indica que se debe pasar a [Page1.aspx] toda la información que se envió a [Default.aspx] durante el POST. Esto permite, por ejemplo, que [Page1.aspx] tenga acceso a los valores enviados a través de una colección llamada Request.Form. La línea 5 utiliza lo que se conoce como el contexto de la solicitud. Se accede a él a través de la propiedad Context de la clase Page. Este contexto puede servir como memoria entre las diferentes páginas que procesan la misma solicitud, en este caso [Default.aspx] y [Page1.aspx]. Para ello se utiliza el diccionario Items.
Cuando [Page1.aspx] se carga mediante la operación Server.Transfer("Page1.aspx",true), todo ocurre como si [Page1.aspx] hubiera sido llamada por un GET desde un navegador. El controlador Page_Load de [Page1.aspx] se ejecuta normalmente. Lo utilizaremos para mostrar el mensaje generado por [Default.aspx] en el contexto de la solicitud:
using System;
namespace Intro
{
public partial class Page1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Label1.Text = Context.Items["msg1"] as string;
}
}
}
En la línea 9, el mensaje generado por [Default.aspx] en el contexto de la consulta se muestra en Label1.
A continuación se muestra un ejemplo de ejecución:
![]() |
- en la página [Default.aspx] [1], hacemos clic en el enlace [2], que nos lleva a la página Page1
- en [3], se muestra la página Page1
- en [4], el mensaje creado en [Default.aspx] y mostrado por [Page1.aspx]
- en [5], la página URL que se muestra en el navegador es la de la página [Default.aspx]
2.9. Redirección de una página a otra
Aquí presentamos otra técnica funcionalmente similar a la anterior: cuando el usuario solicita la página [Default.aspx] a través de una POST, recibe como respuesta otra página, la [Page2.aspx]. En el método anterior, la solicitud del usuario era procesada sucesivamente por dos páginas: [Default.aspx] y [Page1.aspx]. En el método de redirección de página que presentamos ahora, hay dos solicitudes distintas del navegador:
![]() |
- en [1], el navegador realiza una solicitud POST a la página [Default.aspx]. Esta procesa la solicitud y envía una respuesta denominada de redirección al navegador. Esta respuesta es un simple flujo HTTP (líneas de texto) que le pide al navegador que se redirija a otra URL: [Page2.aspx]. [Default.aspx] no envía un flujo HTML en esta primera respuesta.
- En [2], el navegador realiza una solicitud GET a la página [Page2.aspx]. Esta se envía entonces como respuesta al navegador.
- Si la página [Default.aspx] desea transmitir información a la página [Page2.aspx], puede hacerlo a través de la sesión del usuario. A diferencia del método anterior, el contexto de la solicitud no se puede utilizar aquí, ya que hay dos solicitudes distintas y, por lo tanto, dos contextos distintos. Por lo tanto, hay que utilizar la sesión del usuario para que las páginas se comuniquen entre sí.
Al igual que se hizo con [Page1.aspx], agregamos al proyecto la página [Page2.aspx]:
![]() |
- en [1], se agregó [Page2.aspx] al proyecto
- en [2], se modificó el aspecto visual de [Page2.aspx]
- en [3], agregamos a la página [Default.aspx] un componente LinkButton [4] que redirigirá al usuario a [Page2.aspx].
El código fuente de [Page2.aspx] es similar al de [Page1.aspx]:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Page2.aspx.cs" Inherits="Intro.Page2" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Page2</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<h1>
Page 2</h1>
<asp:Label ID="Label1" runat="server"></asp:Label>
<br />
<asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="~/Default.aspx">Retour
vers page [Default]</asp:HyperLink>
</div>
</form>
</body>
</html>
En [Default.aspx], la incorporación del componente LinkButton generó el siguiente código fuente:
<asp:LinkButton ID="LinkButtonToPage2" runat="server"
onclick="LinkButtonToPage2_Click">Redirection vers Page2</asp:LinkButton>
El controlador [LinkButtonToPage2_Click] es el que se encarga de la redirección a [Page2.aspx]. Su código en [Defaul.aspx.cs] es el siguiente:
protected void LinkButtonToPage2_Click(object sender, EventArgs e)
{
// se agrega un mensaje a la sesión
Session["msg2"] = "Message de [Default.aspx] pour [Page2.aspx]";
// se redirige al cliente a [Page2.aspx]
Response.Redirect("Page2.aspx");
}
- línea 4: se agrega un mensaje a la sesión del usuario
- línea 5: el objeto Response es una propiedad de cualquier página ASPX. Representa la respuesta enviada al cliente. Cuenta con un método Redirect que hace que la respuesta enviada al cliente sea una orden de redirección HTTP.
Cuando el navegador reciba la orden de redirección hacia [Page2.aspx], ejecutará un GET en esta página. En esta, se ejecutará el método [Page_Load]. Lo utilizaremos para recuperar el mensaje que [Default.aspx] colocó en la sesión y mostrarlo. El código [Page2.aspx.cs] es el siguiente:
using System;
namespace Intro
{
public partial class Page2 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// se muestra el mensaje guardado en la sesión por [Default.aspx]
Label1.Text = Session["msg2"] as string;
}
}
}
Al ejecutarlo, se obtienen los siguientes resultados:
![]() |
- En [1], se hace clic en el enlace de redirección de [Default.aspx]. Se realiza una redirección de POST a la página [Default.aspx]
- en [2], el navegador fue redirigido a [Page2.aspx]. Esto se observa en la URL URL que muestra el navegador. En el método anterior, esta URL era la de [Default.aspx], ya que la única solicitud realizada por el navegador fue hacia esa URL. Aquí hay una primera solicitud de POST a [Default.aspx], y luego, sin que el usuario se dé cuenta, una segunda solicitud de GET a [Page2.aspx].
- En [3], se observa que [Page2.aspx] recuperó correctamente el mensaje que [Default.aspx] había colocado en la sesión.
2.10. Conclusion
Hemos presentado, con la ayuda de algunos ejemplos, los conceptos de ASP.NET que nos serán útiles en el resto del documento. Esta introducción no permite comprender las sutilezas de los intercambios entre el cliente y el servidor de una aplicación web. Para ello, se puede consultar:
- Programación ASP.NET [Développement WEB avec ASP.NET 1.1 ]



































