2. Una breve introduzione a ASP.NET
In questa sede ci proponiamo di introdurre, con l’ausilio di alcuni esempi, i concetti di ASP.NET che ci saranno utili nel prosieguo del documento. Questa introduzione non consente di comprendere le sottigliezze delle interazioni client/server di un’applicazione web. A tal fine, si può consultare:
- Programmazione ASP.NET [Sviluppo Web con ASP.NET 1.1 (2004)]
Questa introduzione è pensata per chi desidera procedere rapidamente, accettando, in una prima fase, di tralasciare alcuni aspetti che potrebbero rivelarsi importanti. Il seguito del documento consente di approfondire tali aspetti. Chi ha già familiarità con ASP.NET può passare direttamente al paragrafo 3.
2.1. Un progetto di esempio
2.1.1. Creazione del progetto
![]() |
- in [1], si crea un nuovo progetto con Visual Web Developer
- in [2], si seleziona un progetto web in Visual C#
- in [3], si specifica che si desidera creare un'applicazione web ASP.NET
- in [4], si assegna un nome all'applicazione. Verrà creata una cartella per il progetto con questo nome.
- in [5], si indica la cartella principale della cartella [4] del progetto
![]() |
- in [6], il progetto creato
- [Default.aspx] è una pagina web creata per impostazione predefinita. Contiene i tag HTML e i tag ASP.NET
- [Default.aspx.cs] contiene il codice per la gestione degli eventi generati dall’utente sulla pagina [Defaul.aspx] visualizzata nel suo browser
- [Default.aspx.designer.cs] contiene l’elenco dei componenti ASP.NET della pagina [Default.aspx]. Ogni componente ASP.NET inserito nella pagina [Default.aspx] genera la dichiarazione di tale componente in [Default.aspx.designer.cs].
- [Web.config] è il file di configurazione del progetto ASP.NET.
- [References] è l'elenco dei file DLL utilizzati dal progetto web. Questi file DLL sono librerie di classi che il progetto è destinato a utilizzare. In [7] è riportato l'elenco dei DLL inseriti di default nei riferimenti del progetto. La maggior parte di esse è superflua. Se il progetto deve utilizzare una DLL non elencata in [7], questa può essere aggiunta tramite [8].
2.1.2. La pagina [Default.aspx]
Se si esegue il progetto tramite [Ctrl-F5], la pagina [Default.aspx] viene visualizzata in un browser:
![]() |
- in [1], la pagina URL del progetto web. Visual Web Developer dispone di un server web integrato che viene avviato quando si richiede l'esecuzione di un progetto. Esso ascolta su una porta casuale, in questo caso la 1490. La porta di ascolto è solitamente la porta 80. In [1] non viene richiesta alcuna pagina. In questo caso, viene visualizzata la pagina [Default.aspx], da cui il nome di pagina predefinita.
- In [2], la pagina [Default.aspx] è vuota.
- In Visual Web Developer, la pagina [Default.aspx] [3] può essere creata visivamente (scheda [Design]) o tramite tag (scheda [Source])
- in [4], la pagina [Defaul.aspx] in modalità [Design]. Si crea trascinandovi i componenti presenti nella casella degli strumenti [5].
![]() |
La modalità [Source] [6] consente di accedere al codice sorgente della pagina:
<%@ 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 riga 1 è una direttiva ASP.NET che elenca alcune proprietà della pagina
- la direttiva Page si applica a una pagina web. Esistono altre direttive, quali Application, WebService, ... che si applicano ad altri oggetti ASP.NET
- l'attributo CodeBehind indica il file che gestisce gli eventi della pagina
- l'attributo Language indica il linguaggio .NET utilizzato dal file CodeBehind
- l'attributo Inherits indica il nome della classe definita all'interno del file CodeBehind
- L'attributo AutoEventWireUp="true" indica che il collegamento tra un evento in [Default.aspx] e il relativo gestore in [Defaul.aspx.cs] avviene tramite il nome dell'evento. Pertanto, l'evento Load nella pagina [Default.aspx] verrà gestito dal metodo Page_Load della classe Intro._Default definita dall'attributo Inherits.
- le righe 4-14 descrivono la pagina [Defaul.aspx] utilizzando i tag:
- classici HTML, come il tag <body> o <div>
- ASP.NET. Si tratta dei tag che presentano l’attributo runat="server". I tag ASP.NET vengono elaborati dal server web prima dell’invio della pagina al client. Vengono trasformati in tag HTML. Il browser del cliente riceve quindi una pagina standard HTML in cui non sono più presenti tag ASP.NET.
La pagina [Default.aspx] può essere modificata direttamente dal suo codice sorgente. A volte è più semplice che passare attraverso la modalità [Design]. Modifichiamo il codice sorgente nel modo seguente:
<%@ 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>
Alla riga 6, assegniamo un titolo alla pagina tramite il tag HTML <title>. Alla riga 9, inseriamo un testo nel corpo (<body>) della pagina. Se eseguiamo il progetto (Ctrl-F5), otteniamo il seguente risultato nel browser:
![]() |
2.1.3. I file [Default.aspx.designer.cs] e [Default.aspx.cs]
Il file [Default.aspx.designer.cs] dichiara i componenti della pagina [Defaul.aspx]:
//------------------------------------------------------------------------------
// <generato automaticamente>
// Questo codice è stato generato da uno strumento.
// Versione del runtime: 2.0.50727.3603
//
// Le modifiche apportate a questo file potrebbero causare un comportamento errato e andranno perse se
// il codice viene rigenerato.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Intro {
public partial class _Default {
/// <summary>
/// Controllo form1.
/// </summary>
/// <remarks>
/// Campo generato automaticamente.
/// Per modificarlo, spostare la dichiarazione del campo dal file di progettazione al file code-behind.
/// </remarks>
protected global::System.Web.UI.HtmlControls.HtmlForm form1;
}
}
In questo file è presente l’elenco dei componenti ASP.NET della pagina [Default.aspx] dotati di un identificatore. Essi corrispondono ai tag di [Default.aspx] che presentano l’attributo runat="server" e l’attributo id. Pertanto, il componente della riga 23 sopra riportata corrisponde al tag
<form id="form1" runat="server">
di [Default.aspx].
Lo sviluppatore interagisce poco con il file [Default.aspx.designer.cs]. Tuttavia, questo file è utile per conoscere la classe di un particolare componente. Si vede quindi di seguito che il componente form1 è di tipo HtmlForm. Lo sviluppatore può quindi esplorare questa classe per conoscerne le proprietà e i metodi. I componenti della pagina [Default.aspx] sono utilizzati dalla classe del file [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)
{
}
}
}
Si noti che la classe definita nei file [Default.aspx.cs] e [Default.aspx.designer.cs] è la stessa (riga 10): Intro._Default. È la parola chiave partial che consente di estendere la dichiarazione di una classe su più file, in questo caso due.
Alla riga 10, sopra, si vede che la classe [_Default] estende la classe [Page] ed eredita i suoi eventi. Uno di questi è l’evento Load, che si verifica quando la pagina viene caricata dal server web. Riga 12: il metodo Page_Load gestisce l’evento Load della pagina. È generalmente qui che si inizializza la pagina prima della sua visualizzazione nel browser del cliente. In questo caso, il metodo Page_Load non esegue alcuna operazione.
La classe associata a una pagina web, in questo caso la classe Intro._Default, viene creata all’inizio della richiesta del cliente e distrutta una volta inviata la risposta al cliente. Non può quindi essere utilizzata per memorizzare informazioni tra una richiesta e l’altra. A tal fine è necessario ricorrere al concetto di sessione utente.
2.2. Gli eventi di una pagina web ASP.NET
Costruiamo la seguente pagina [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>
La modalità [Design] della pagina è la seguente:
![]() |
Il file [Default.aspx.designer.cs] è il seguente:
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;
}
}
Qui si trovano tutti i componenti ASP.NET della pagina [Default.aspx] dotati di un identificativo.
Modifichiamo il file [Default.aspx.cs] come segue:
using System;
namespace Intro
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Init(object sender, EventArgs e)
{
// si registra l'evento
ListBoxEvts.Items.Insert(0, string.Format("{0}: Page_Init", DateTime.Now.ToString("hh:mm:ss")));
}
protected void Page_Load(object sender, EventArgs e)
{
// si registra l'evento
ListBoxEvts.Items.Insert(0, string.Format("{0}: Page_Load", DateTime.Now.ToString("hh:mm:ss")));
}
protected void ButtonValider_Click(object sender, EventArgs e)
{
// si registra l'evento
ListBoxEvts.Items.Insert(0, string.Format("{0}: ButtonValider_Click", DateTime.Now.ToString("hh:mm:ss")));
}
}
}
La classe [_Default] (riga 5) gestisce tre eventi:
- l’evento Init (riga 7), che si verifica quando la pagina è stata inizializzata
- l’evento Load (riga 13) che si verifica quando la pagina è stata caricata dal server web. L’evento Init si verifica prima dell’evento Load.
- l'evento Click sul pulsante ButtonValider (riga 19), che si verifica quando l'utente fa clic sul pulsante [Valider]
La gestione di ciascuno di questi tre eventi consiste nell’aggiungere un messaggio al componente Listbox denominato ListBoxEvts. Questo messaggio visualizza l’ora e il nome dell’evento. Ogni messaggio viene inserito all’inizio dell’elenco; pertanto, i messaggi in cima all’elenco sono quelli più recenti.
Quando si esegue il progetto, si ottiene la seguente pagina:
![]() |
Da [1] si evince che gli eventi Page_Init e Page_Load si sono verificati in quest'ordine. Si ricorda che l’evento più recente si trova in cima all’elenco. Quando il browser richiede la pagina [Default.aspx] direttamente tramite il suo URL [2], lo fa tramite un comando HTTP (HyperText Transfer Protocol) denominato GET. Una volta caricata la pagina nel browser, l’utente genererà degli eventi sulla pagina. Ad esempio, cliccherà sul pulsante [Valider] [3]. Gli eventi generati dall’utente una volta che la pagina è stata caricata nel browser innescano una richiesta alla pagina [Default.aspx], ma questa volta con un comando HTTP denominato POST. Riassumendo:
- il caricamento iniziale di una pagina P in un browser avviene tramite un’operazione HTTP GET
- gli eventi che si verificano successivamente sulla pagina generano ogni volta una nuova richiesta verso la stessa pagina P, ma questa volta con un comando HTTP POST. Una pagina P è in grado di rilevare se è stata richiesta con un comando GET o con un comando POST, il che le consente di comportarsi in modo diverso se necessario, cosa che avviene nella maggior parte dei casi.
Richiesta iniziale di una pagina ASPX: GET
![]() |
- in [1], il browser richiede la pagina ASPX tramite un comando HTTP GET senza parametri.
- In [2], il server web gli invia in risposta il flusso HTML, traduzione della pagina ASPX richiesta.
Elaborazione di un evento generato sulla pagina visualizzata dal browser: POST
![]() |
- in [1], in seguito a un evento sulla pagina HTML, il browser richiede la pagina ASPX già acquisita con un'operazione GET, questa volta con un comando HTTP POST accompagnato da parametri. Questi parametri sono i valori dei componenti che si trovano all’interno del tag <form> della pagina HTML visualizzata dal browser. Questi valori sono detti «valori inviati dal client». Verranno utilizzati dalla pagina ASPX per elaborare la richiesta del client.
- In [2], il server web invia in risposta il flusso HTML, ovvero la traduzione della pagina ASPX richiesta inizialmente da POST o di un’altra pagina, qualora si sia verificato un trasferimento o un reindirizzamento.
Torniamo alla nostra pagina di esempio:
![]() |
- in [2], la pagina è stata ottenuta tramite un GET.
- in [1], si vedono i due eventi che si sono verificati durante questo GET
Se, come sopra, l’utente clicca sul pulsante [Valider] [3], la pagina [Default.aspx] verrà richiesta tramite un POST. Questo POST sarà accompagnato da parametri che corrisponderanno ai valori di tutti i componenti inclusi nel tag <form> della pagina [Default.aspx]: i due TextBox e [TextBoxNom, TextBoxAge], il pulsante [ButtonValider], l’elenco [ListBoxEvts]. I valori inviati per i componenti sono i seguenti:
- TextBox: il valore inserito
- Button: il testo del pulsante, in questo caso il testo «Conferma»
- Listbox: il testo del messaggio selezionato in ListBox
In risposta a POST, si ottiene la pagina [4]. Si tratta nuovamente della pagina [Default.aspx]. Questo è il comportamento normale, a meno che non vi sia un trasferimento o un reindirizzamento della pagina da parte dei gestori degli eventi della pagina. Si può notare che si sono verificati due nuovi eventi:
- l’evento Page_Load, verificatosi durante il caricamento della pagina
- l’evento ButtonValider_Click, verificatosi a seguito del clic sul pulsante [Valider]
Si può notare che:
- l’evento Page_Init non si è verificato durante l’operazione HTTP POST, mentresi era verificato durante l’evento HTTP GET
- l'evento Page_Load si verifica sempre, sia su un GET che su un POST. È in questo metodo che in genere è necessario sapere se si ha a che fare con un GET o con un POST.
- Al termine del POST, la pagina [Default.aspx] è stata rinviata al client con le modifiche apportate dai gestori di eventi. È sempre così. Una volta elaborati gli eventi di una pagina P, quella stessa pagina P viene rinviata al client. Esistono due modi per eludere questa regola. L’ultimo gestore di eventi eseguito può
- trasferire il flusso di esecuzione a un’altra pagina P2.
- reindirizzare il browser del cliente verso un’altra pagina P2.
In entrambi i casi, è la pagina P2 che viene restituita al browser. I due metodi presentano delle differenze su cui torneremo.
- L'evento ButtonValider_Click si è verificato dopo l'evento Page_Load. È quindi questo gestore che può decidere se effettuare il trasferimento o il reindirizzamento verso una pagina P2.
- L’elenco degli eventi [4] ha mantenuto i due eventi visualizzati durante il caricamento iniziale GET della pagina [Default.aspx]. Ciò è sorprendente se si considera che la pagina [Default.aspx] è stata ricreata durante il POST. Ci si aspetterebbe di ritrovare la pagina [Default.aspx] con i suoi valori di progettazione e, di conseguenza, un ListBox vuoto. L’esecuzione dei gestori Page_Load e ButtonValider_Click dovrebbe quindi inserirvi due messaggi. E invece se ne trovano quattro. È il meccanismo del VIEWSTATE a spiegare questo fenomeno. Durante l’esecuzione iniziale di GET, il server web invia la pagina [Default.aspx] con un tag HTML <input type="hidden" ...> denominato campo nascosto (riga 10 qui sotto).
Nel campo ID "__VIEWSTATE" il server web codifica il valore di tutti i componenti della pagina. Lo fa sia sul GET iniziale che sui POST successivi. Quando si verifica un POST su una pagina P:
- il browser richiede la pagina P inviando nella sua richiesta i valori di tutti i componenti che si trovano all’interno del tag <form>. Nell’esempio sopra riportato, si può notare che il componente "__VIEWSTATE" si trova all’interno del tag <form>. Il suo valore viene quindi inviato al server al momento di un POST.
- La pagina P viene istanziata e inizializzata con i suoi valori di costruzione
- il componente "__VIEWSTATE" viene utilizzato per restituire ai componenti i valori che avevano quando la pagina P era stata inviata in precedenza. È così, ad esempio, che l’elenco degli eventi [4] recupera i primi due messaggi che conteneva quando era stato inviato in risposta al GET iniziale del browser.
- I componenti della pagina P assumono quindi come valori quelli inviati dal browser. A questo punto, il modulo della pagina P si trova nello stato in cui l’utente lo ha inviato.
- Viene elaborato l’evento Page_Load. In questa fase, aggiunge un messaggio all’elenco degli eventi [4].
- Viene elaborato l'evento che ha generato POST. Qui ButtonValider_Click aggiunge un messaggio all'elenco degli eventi [4].
- Viene restituita la pagina P. I componenti hanno come valore:
- o il valore inviato, c.a.d; oppure il valore che il componente aveva nel modulo quando questo è stato inviato al server
- oppure un valore fornito da uno dei gestori di eventi.
Nel nostro esempio,
- i due componenti TextBox riprenderanno il valore inviato poiché i gestori di eventi non li modificano
- l'elenco degli eventi [4] recupera il proprio valore inviato, c.a.d. tutti gli eventi già presenti nell'elenco, più due nuovi eventi creati dai metodi Page_Load e ButtonValider_Click.
Il meccanismo di VIEWSTATE può essere attivato o disattivato a livello di ciascun componente. Disattiviamolo per il componente [ListBoxEvts]:
![]() |
- in [1], il VIEWSTATE del componente [ListBoxEvts] è disattivato. Quello dei componenti TextBox e [2] è attivato per impostazione predefinita.
- In [3], i due eventi restituiti dopo il GET iniziale
![]() |
- in [4], il modulo è stato compilato e si fa clic sul pulsante [Valider]. Verrà effettuato un POST verso la pagina [Default.aspx].
- in [6], il risultato restituito dopo aver cliccato sul pulsante [Valider]
- il meccanismo del VIEWSTATE attivato spiega perché i TextBox e [7] abbiano mantenuto il valore inserito in [4]
- il meccanismo inibito del VIEWSTATE spiega perché il componente [ListBoxEvts] e [8] non abbiano mantenuto il proprio contenuto [5].
2.3. Gestione dei valori inviati
Ci occuperemo qui dei valori inviati dai due TextBox quando l’utente clicca sul pulsante [Valider]. La pagina [Default.aspx] in modalità [Design] si evolve come segue:
![]() |
Il codice sorgente dell’elemento aggiunto in [1] è il seguente:
<p>
Eléments postés au serveur :
<asp:Label ID="LabelPost" runat="server"></asp:Label>
</p>
Utilizzeremo il componente [LabelPost] per visualizzare i valori inseriti nei due TextBox e [2]. Il codice del gestore di eventi [Default.aspx.cs] si evolve come segue:
using System;
namespace Intro
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Init(object sender, EventArgs e)
{
// si registra l'evento
ListBoxEvts.Items.Insert(0, string.Format("{0}: Page_Init", DateTime.Now.ToString("hh:mm:ss")));
}
protected void Page_Load(object sender, EventArgs e)
{
// si registra l'evento
ListBoxEvts.Items.Insert(0, string.Format("{0}: Page_Load", DateTime.Now.ToString("hh:mm:ss")));
}
protected void ButtonValider_Click(object sender, EventArgs e)
{
// si registra l'evento
ListBoxEvts.Items.Insert(0, string.Format("{0}: ButtonValider_Click", DateTime.Now.ToString("hh:mm:ss")));
// vengono visualizzati il nome e l'età
LabelPost.Text = string.Format("nom={0}, age={1}", TextBoxNom.Text.Trim(), TextBoxAge.Text.Trim());
}
}
}
Riga 24, si aggiorna il componente LabelPost:
- LabelPost è di tipo [System.Web.UI.WebControls.Label] (cfr. Default.aspx.designer.cs). La sua proprietà Text rappresenta il testo visualizzato dal componente.
- TextBoxNom e TextBoxAge sono di tipo [System.Web.UI.WebControls.TextBox]. La proprietà Text di un componente TextBox è il testo visualizzato nell'area di immissione.
- Il metodo Trim() rimuove gli spazi che possono precedere o seguire una stringa di caratteri
Come spiegato in precedenza, quando viene eseguito il metodo ButtonValider_Click, i componenti della pagina assumono il valore che avevano al momento dell’invio della pagina da parte dell’utente. Le proprietà Text di entrambi i TextBox assumono quindi come valore i testi inseriti dall’utente nel browser.
Ecco un esempio:
![]() |
- in [1], i valori inviati
- in [2], la risposta del server.
- in [3], i TextBox hanno recuperato il valore inviato tramite il meccanismo del VIEWSTATE attivato
- in [4], i messaggi del componente ListBoxEvts provengono dai metodi Page_Init, Page_Load, ButtonValider_Click e da un VIEWSTATE disabilitato
- In [5], il componente LabelPost ha ottenuto il proprio valore tramite il metodo ButtonValider_Click. Sono stati correttamente recuperati entrambi i valori inseriti dall'utente nei due TextBox e [1].
Come si vede sopra, il valore inviato per l’età è la stringa "yy", un valore non valido. Aggiungeremo alla pagina dei componenti chiamati validatori. Servono a verificare la validità dei dati inviati. Tale validità può essere verificata in due punti:
- sul client. Un’opzione di configurazione del validatore consente di scegliere se eseguire o meno i controlli sul browser. In tal caso, vengono effettuati tramite codice JavaScript incorporato nella pagina HTML. Quando l’utente invia i valori inseriti nel modulo, questi vengono innanzitutto verificati dal codice JavaScript. Se uno dei test fallisce, l’invio non viene effettuato. In questo modo si evita un’andata e ritorno con il server, rendendo così la pagina più reattiva.
- sul server. Se i controlli lato client possono essere facoltativi, lato server sono obbligatori indipendentemente dal fatto che sia stata effettuata o meno una verifica lato client. Infatti, quando una pagina riceve dei valori inviati tramite POST, non ha modo di sapere se questi siano stati verificati dal client prima dell’invio. Lato server, lo sviluppatore deve quindi verificare sempre la validità dei dati inviati tramite POST.
La pagina [Default.aspx] si evolve come segue:
<%@ 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>
I validatori sono stati aggiunti alle righe 20, 32 e 35. Alla riga 58, viene utilizzato un componente Label per visualizzare i valori inviati validi. Alla riga 60, viene utilizzato un componente Label per visualizzare un messaggio di errore in caso di errori di immissione.
La pagina [Default.aspx] in modalità [Design] è la seguente:
![]() |
- I componenti [1] e [2] sono di tipo RequiredFieldValidator. Questo validatore verifica che un campo di immissione non sia vuoto.
- Il componente [3] è di tipo RangeValidator. Questo validatore verifica che un campo di immissione contenga un valore compreso tra due limiti.
- In [4], le proprietà del validatore [1].
Presenteremo i due tipi di validatori attraverso i relativi tag nel codice della pagina [Default.aspx]:
<asp:RequiredFieldValidator ID="RequiredFieldValidatorNom" runat="server"
ControlToValidate="TextBoxNom" Display="Dynamic"
ErrorMessage="Donnée obligatoire !"></asp:RequiredFieldValidator>
- ID: l’identificativo del componente
- ControlToValidate: il nome del componente il cui valore viene verificato. In questo caso si vuole che il componente TextBoxNom non abbia un valore vuoto (stringa vuota o sequenza di spazi)
- ErrorMessage: messaggio di errore da visualizzare nel validatore in caso di dati non validi.
- EnableClientScript: valore booleano che indica se il validatore deve essere eseguito anche sul lato client. Questo attributo ha il valore predefinito True quando non è esplicitamente impostato come sopra.
- Display: modalità di visualizzazione del validatore. Sono disponibili due modalità:
- static (predefinito): il validatore occupa spazio sulla pagina anche se non visualizza alcun messaggio di errore
- dynamic: il validatore non occupa spazio sulla pagina se non visualizza messaggi di errore.
<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: il tipo di dato verificato. In questo caso l'età è un numero intero.
- MinimumValue, MaximumValue: gli intervalli entro i quali deve rientrare il valore verificato
La configurazione del componente che genera il codice POST influisce sulla modalità di convalida. In questo caso, tale componente è il pulsante [Valider]:
<asp:Button ID="ButtonValider" runat="server" onclick="ButtonValider_Click" Text="Valider" CausesValidation="True" />
- CausesValidation: imposta la modalità automatica o il nome delle convalide lato server. Questo attributo ha il valore predefinito "True" se non viene esplicitamente specificato. In questo caso,
- lato client, vengono eseguiti i validatori con valori compresi tra EnableClientScript e True. Il POST viene eseguito solo se tutti i validatori lato client hanno esito positivo.
- Sul lato server, tutti i validatori presenti nella pagina vengono eseguiti automaticamente prima dell’elaborazione dell’evento che ha generato il POST. In questo caso, verrebbero eseguiti prima dell’esecuzione del metodo ButtonValider_Click. In questo metodo è possibile verificare se tutte le validazioni hanno avuto esito positivo o meno. Page.IsValid è "True" se tutte hanno avuto esito positivo, "False" in caso contrario. In quest’ultimo caso, è possibile interrompere l’elaborazione dell’evento che ha generato il POST. La pagina inviata viene restituita così come è stata inserita. I validatori che non hanno superato la verifica visualizzano quindi il proprio messaggio di errore (attributo ErrorMessage).
Se CausesValidation ha il valore False, allora
- sul lato client non viene eseguito alcun validatore
- Sul lato server, spetta allo sviluppatore stesso richiedere l'esecuzione dei validatori della pagina. Lo fa tramite il metodo Page.Validate(). A seconda del risultato delle validazioni, questo metodo imposta la proprietà Page.IsValid su "True" o "False".
In [Default.aspx.cs] il codice di elaborazione di ButtonValider_Click viene modificato come segue:
protected void ButtonValider_Click(object sender, EventArgs e)
{
// si registra l'evento
ListBoxEvts.Items.Insert(0, string.Format("{0}: ButtonValider_Click", DateTime.Now.ToString("hh:mm:ss")));
// si visualizzano il nome e l'età
LabelPost.Text = string.Format("nom={0}, age={1}", TextBoxNom.Text.Trim(), TextBoxAge.Text.Trim());
// La pagina è valida?
Page.Validate();
if (!Page.IsValid)
{
// messaggio di errore generale
LabelErreursSaisie.Text = "Veuillez corriger les erreurs de saisie...";
LabelErreursSaisie.Visible = true;
return;
}
// si nasconde il messaggio di errore
LabelErreursSaisie.Visible = false;
// vengono visualizzati il nome e l'età convalidati
LabelValidation.Text = string.Format("nom={0}, age={1}", TextBoxNom.Text.Trim(), TextBoxAge.Text.Trim());
}
Nel caso in cui il pulsante [Valider] abbia il proprio attributo CausesValidation impostato su True e i validatori abbiano il proprio attributo EnableClientScript impostato su True, il metodo ButtonValider_Click viene eseguito solo quando i valori inviati sono validi. Ci si può quindi chiedere quale sia il significato del codice che si trova a partire dalla riga 8. È necessario ricordare che è sempre possibile scrivere un client programmato che invii valori non verificati alla pagina [Default.aspx]. Pertanto, quest’ultima deve sempre ripetere i test di validità.
- riga 8: avvia l’esecuzione di tutti i validatori della pagina. Nel caso in cui il pulsante [Valider] abbia l’attributo impostato da CausesValidation a True, ciò avviene automaticamente e non è necessario ripeterlo. Qui c'è una ridondanza.
- righe 9-15: caso in cui uno dei validatori abbia fallito
- righe 16-19: caso in cui tutti i validatori abbiano superato il controllo
Ecco due esempi di esecuzione:
![]() |
- in [1], un esempio di esecuzione nel caso in cui:
- il pulsante [Valider] abbia la proprietà CausesValidation impostata su True
- i validatori hanno la proprietà EnableClientScript impostata su True
I messaggi di errore [2] sono stati visualizzati dai validatori eseguiti lato client dal codice JavaScript della pagina. Non è stato inviato alcun POST al server, come mostra l'etichetta degli elementi inviati [3].
- In [4], un esempio di esecuzione nel caso in cui:
- il pulsante [Valider] abbia la proprietà CausesValidation impostata su False
- i validatori hanno la proprietà EnableClientScript impostata su False
I messaggi di errore [5] sono stati visualizzati dai validatori eseguiti sul lato server. Come mostra [6], è stato effettivamente inviato un POST al server. In [7], il messaggio di errore visualizzato dal metodo [ButtonValider_Click] in caso di errori di immissione dati.
![]() |
- in [8] un esempio ottenuto con dati validi. [9,10] mostra che gli elementi inviati sono stati convalidati. Quando si eseguono test ripetuti, è necessario impostare su False la proprietà EnableViewState dell’etichetta [LabelValidation] affinché il messaggio di convalida non rimanga visualizzato durante le successive esecuzioni.
2.4. Gestione dei dati di ambito dell’applicazione
Torniamo all’architettura di esecuzione di una pagina ASPX:
![]() |
La classe della pagina ASPX viene istanziata all’inizio della richiesta del client e distrutta al termine della stessa. Pertanto, non può essere utilizzata per memorizzare dati tra una richiesta e l’altra. Si potrebbero voler memorizzare due tipi di dati:
- dati condivisi da tutti gli utenti dell'applicazione web. Si tratta in genere di dati in sola lettura. Per implementare questa condivisione dei dati vengono utilizzati tre file:
- [Web.Config]: il file di configurazione dell’applicazione
- [Global.asax, Global.asax.cs]: consente di definire una classe, denominata classe globale dell’applicazione, la cui durata corrisponde a quella dell’applicazione, nonché i gestori per alcuni eventi della stessa applicazione.
La classe globale dell’applicazione consente di definire dati che saranno disponibili per tutte le richieste di tutti gli utenti.
- dati condivisi dalle richieste dello stesso cliente. Questi dati vengono memorizzati in un oggetto denominato «Sessione». Si parla quindi di «sessione cliente» per indicare la memoria del cliente. Tutte le richieste di un cliente hanno accesso a questa sessione. Possono memorizzarvi e leggerne le informazioni
![]() |
Di seguito illustriamo i tipi di memoria a cui ha accesso una pagina ASPX:
- la memoria dell’applicazione, che contiene per lo più dati in sola lettura ed è accessibile a tutti gli utenti;
- la memoria di un utente specifico, o sessione, che contiene dati in lettura/scrittura ed è accessibile alle richieste successive dello stesso utente.
- non rappresentata sopra, esiste una memoria di richiesta, o contesto di richiesta. La richiesta di un utente può essere elaborata da più pagine ASPX successive. Il contesto della richiesta consente a una pagina 1 di trasmettere informazioni a una pagina 2.
Qui ci interessano i dati di ambito Application, ovvero quelli condivisi da tutti gli utenti. La classe globale dell’applicazione può essere creata come segue:
![]() |
- in [1], si aggiunge un nuovo elemento al progetto
- in [2], si aggiunge la classe globale dell’applicazione
- in [3], si mantiene il nome predefinito [Global.asax] per il nuovo elemento
![]() |
- in [4], sono stati aggiunti due nuovi file al progetto
- in [5], viene visualizzato il codice di [Global.asax]
<%@ Application Codebehind="Global.asax.cs" Inherits="Intro.Global" Language="C#" %>
- Il tag Application sostituisce il tag Page che avevamo per [Default.aspx]. Identifica la classe di applicazione globale
- Codebehind: definisce il file in cui è definita la classe di applicazione globale
- Inherits: definisce il nome di questa classe
La classe Intro.Global generata è la seguente:
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)
{
}
}
}
- riga 5: la classe globale dell'applicazione deriva dalla classe HttpApplication
La classe viene generata con gli scheletri dei gestori degli eventi dell’applicazione:
- righe 8, 38: gestiscono gli eventi Application_Start (avvio dell'applicazione) e Application_End (terminazione dell'applicazione quando il server web si arresta o quando l'amministratore disconnette l'applicazione)
- righe 13, 33: gestiscono gli eventi Session_Start (avvio di una nuova sessione client all’arrivo di un nuovo cliente o alla scadenza di una sessione esistente) e Session_End (fine di una sessione client, sia esplicitamente tramite programmazione sia implicitamente per superamento della durata consentita per una sessione).
- riga 28: gestisce l'evento Application_Error (verifica di un'eccezione non gestita dal codice dell'applicazione e segnalata al server)
- riga 18: gestisce l'evento Application_BeginRequest (arrivo di una nuova richiesta).
- riga 23: gestisce l'evento Application_AuhenticateRequest (si verifica quando un utente ha effettuato l'autenticazione).
Il metodo [Application_Start] viene spesso utilizzato per inizializzare l’applicazione a partire dalle informazioni contenute in [Web.Config]. Quello generato al momento della creazione iniziale di un progetto ha il seguente aspetto:
<?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>
Per la nostra applicazione attuale, questo file non è necessario. Se lo eliminiamo o lo rinominiamo, l'applicazione continua a funzionare normalmente. Ci concentreremo sui tag delle righe 8 e 9:
- <appsettings> consente di definire un dizionario di informazioni
- <connectionStrings> consente di definire stringhe di connessione ai database
Consideriamo il seguente file [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>
...
Questo file può essere utilizzato dalla seguente classe globale dell’applicazione:
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)
{
}
}
}
- righe 8-11: quattro proprietà statiche P. Poiché il ciclo di vita della classe Global coincide con quello dell’applicazione, qualsiasi richiesta effettuata all’applicazione avrà accesso a queste proprietà P tramite la sintassi Global.P.
- righe 17-19: il file [Web.config] è accessibile tramite la classe [System.Configuration.ConfigurationManager]
- righe 17-18: recupera gli elementi del tag <appSettings> dal file [Web.config] tramite l’attributo key.
- riga 19: recupera gli elementi del tag <connectionStrings> dal file [Web.config] tramite l'attributo name.
Gli attributi statici delle righe 8-11 sono accessibili da qualsiasi gestore di eventi delle pagine ASPX caricate. Li utilizziamo nel gestore [Page_Load] della pagina [Default.aspx]:
protected void Page_Load(object sender, EventArgs e)
{
// si registra l'evento
ListBoxEvts.Items.Insert(0, string.Format("{0}: Page_Load", DateTime.Now.ToString("hh:mm:ss")));
// si recuperano le informazioni dalla classe globale dell'applicazione
LabelGlobal.Text = string.Format("Param1={0},Param2={1},ConnString1={2},Erreur={3}", Global.Param1, Global.Param2, Global.ConnString1, Global.Erreur);
}
- riga 6: i quattro attributi statici della classe globale dell’applicazione vengono utilizzati per alimentare una nuova etichetta della pagina [Default.aspx]
![]() |
All'esecuzione, otteniamo il seguente risultato:
![]() |
Come si può vedere sopra, i parametri di [web.config] sono stati recuperati correttamente. La classe globale dell'applicazione è il luogo ideale per memorizzare le informazioni condivise da tutti gli utenti.
2.5. Gestione dei dati nell'ambito della sessione
In questa sede ci interessa capire come memorizzare le informazioni nel corso delle richieste di un determinato utente:
![]() |
Ogni utente dispone di una propria memoria, denominata sessione.
Abbiamo visto che la classe di applicazione globale dispone di due gestori per la gestione degli eventi:
- Session_Start: inizio di una sessione
- Session_end: fine di una sessione
Il meccanismo della sessione viene implementato nel modo seguente:
- alla prima richiesta di un utente, il server web crea un token di sessione che assegna all’utente. Questo token è una sequenza di caratteri unica per ogni utente. Viene inviato dal server nella risposta alla prima richiesta dell’utente.
- nelle richieste successive, l’utente (il browser web) include nella propria richiesta il token di sessione che gli è stato assegnato. In questo modo il server web è in grado di riconoscerlo.
- Una sessione ha una durata prestabilita. Quando il server web riceve una richiesta da un utente, calcola il tempo trascorso dalla richiesta precedente. Se tale tempo supera la durata della sessione, viene creata una nuova sessione per l’utente. I dati della sessione precedente vanno persi. Con il server web IIS (Internet Information Server) di Microsoft, le sessioni hanno per impostazione predefinita una durata di 20 minuti. Questo valore può essere modificato dall’amministratore del server web.
- Il server web riconosce che si tratta della prima richiesta di un utente poiché tale richiesta non contiene un token di sessione. È l’unica.
Ogni pagina ASP.NET ha accesso alla sessione dell’utente tramite la proprietà Session della pagina, di tipo [System.Web.SessionState.HttpSessionState]. Utilizzeremo le seguenti proprietà P e metodi M della classe HttpSessionState:
Nome | Tipo | Ruolo |
Item[String clé] | P | La sessione può essere strutturata come un dizionario. Item[clé] è l'elemento della sessione identificato da clé. Invece di scrivere [HttpSessionState].Item[clé], è possibile scrivere anche [HttpSessionState].[clé]. |
Cancella | M | svuota il dizionario della sessione |
Abbandona | M | termina la sessione. La sessione non è quindi più valida. Una nuova sessione avrà inizio con la prossima richiesta dell'utente. |
Come esempio di memoria utente, conteremo il numero di volte in cui un utente fa clic sul pulsante [Valider]. Per ottenere questo risultato, è necessario mantenere un contatore nella sessione dell'utente.
La pagina [Default.aspx] si evolve come segue:
![]() |
La classe globale dell’applicazione [Global.asax.cs] si evolve come segue:
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)
{
// contatore delle richieste
Session["nbRequêtes"] = 0;
}
}
}
Alla riga 19, si utilizza la sessione dell’utente per memorizzarvi un contatore di richieste identificato dalla chiave "nbRequêtes". Questo contatore viene aggiornato dal gestore [ButtonValider_Click] della pagina [Default.aspx]:
using System;
namespace Intro
{
public partial class _Default : System.Web.UI.Page
{
....
protected void ButtonValider_Click(object sender, EventArgs e)
{
// si registra l'evento
ListBoxEvts.Items.Insert(0, string.Format("{0}: ButtonValider_Click", DateTime.Now.ToString("hh:mm:ss")));
// si visualizzano il nome e l'età pubblicati
LabelPost.Text = string.Format("nom={0}, age={1}", TextBoxNom.Text.Trim(), TextBoxAge.Text.Trim());
// numero di richieste
Session["nbRequêtes"] = (int)Session["nbRequêtes"] + 1;
LabelNbRequetes.Text = Session["nbRequêtes"].ToString();
// la pagina è valida?
Page.Validate();
if (!Page.IsValid)
{
...
}
...
}
}
}
- riga 16: si incrementa il contatore delle richieste
- riga 17: il contatore viene visualizzato sulla pagina
Ecco un esempio di esecuzione:
![]() |
2.6. Gestione di GET / POST durante il caricamento di una pagina
Abbiamo detto che esistono due tipi di richieste verso una pagina ASPX:
- la richiesta iniziale del browser effettuata con un comando HTTP GET. Il server risponde inviando la pagina richiesta. Supponiamo che questa pagina sia un modulo, c.a.d, e che nella pagina ASPX inviata sia presente un tag <form runat="server"...>.
- Le richieste successive effettuate dal browser in risposta ad alcune azioni dell’utente sul modulo. Il browser effettua quindi una richiesta HTTP POST.
Sia che si tratti di una richiesta GET o di una richiesta POST, viene eseguito il metodo [Page_Load]. Nel caso di GET, questo metodo viene solitamente utilizzato per inizializzare la pagina inviata al browser del cliente. Successivamente, tramite il meccanismo di VIEWSTATE, la pagina rimane inizializzata e viene modificata solo dai gestori degli eventi che generano i POST. Non è necessario reinizializzare la pagina nel Page_Load. Da qui la necessità per questo metodo di sapere se la richiesta del client è un GET o un POST.
Esaminiamo il seguente esempio. Si aggiunge un menu a tendina alla pagina [Default.aspx]. Il contenuto di questo menu sarà definito nel gestore Page_Load della richiesta GET:
![]() |
L'elenco a discesa è dichiarato in [Default.aspx.designer.cs] nel modo seguente:
protected global::System.Web.UI.WebControls.DropDownList DropDownListNoms;
Utilizzeremo i seguenti metodi M e proprietà P della classe [DropDownList]:
Nome | Tipo | Ruolo |
Elementi | P | la raccolta di tipo ListItemCollection degli elementi di tipo ListItem dall'elenco a discesa |
SelectedIndex | P | l'indice, a partire da 0, dell'elemento selezionato nell'elenco a discesa al momento dell'invio del modulo |
SelectedItem | P | l'elemento di tipo ListItem selezionato nell'elenco a discesa al momento dell'invio del modulo |
SelectedValue | P | il valore di tipo string dell'elemento di tipo ListItem selezionato nell'elenco a discesa al momento dell'invio del modulo. Definiremo tra poco questo concetto di valore. |
La classe ListItem degli elementi di un elenco a discesa serve a generare i tag <option> del tag HTML <select>:
Nel tag <option>
- textei è il testo visualizzato nell’elenco a discesa
- vali è il valore inviato dal browser se textei è il testo selezionato nell’elenco a discesa
Ogni opzione può essere generata da un oggetto LisItem creato utilizzando il costruttore ListItem(string testo, string valore).
In [Default.aspx.cs], il codice del gestore [Page_Load] si evolve come segue:
protected void Page_Load(object sender, EventArgs e)
{
// si registra l'evento
...
// si recuperano le informazioni dalla classe globale dell'applicazione
...
// inizializzazione della casella combinata dei nomi solo durante il GET iniziale
if (!IsPostBack)
{
for (int i = 0; i < 3; i++)
{
DropDownListNoms.Items.Add(new ListItem("nom"+i,i.ToString()));
}
}
}
- riga 8: la classe Page ha un attributo IsPostBack di tipo booleano. In realtà, ciò significa che la richiesta dell'utente è un POST. Le righe 10-13 vengono quindi eseguite solo sul GET iniziale del cliente.
- riga 12: si aggiunge all’elenco [DropDownListNoms] un elemento di tipo ListItem (string testo, string valore). Il testo visualizzato per l’elemento (i+1)-esimo sarà nomi e il valore inviato per tale elemento, se selezionato, sarà i.
Il gestore [ButtonValider_Click] viene modificato per visualizzare il valore inviato dal menu a tendina:
protected void ButtonValider_Click(object sender, EventArgs e)
{
// si registra l'evento
...
// vengono visualizzati i valori inviati
LabelPost.Text = string.Format("nom={0}, age={1}, combo={2}", TextBoxNom.Text.Trim(), TextBoxAge.Text.Trim(), DropDownListNoms.SelectedValue);
// numero di richieste
...
}
Riga 6: il valore inserito per l’elenco [DropDownListNoms] viene ricavato dalla proprietà SelectedValue dell’elenco. Ecco un esempio di esecuzione:
![]() |
- in [1], il contenuto dell'elenco a discesa dopo il GET iniziale e subito prima del primo POST
- in [2], la pagina dopo il primo POST.
- in [3], il valore inviato per l'elenco a discesa. Corrisponde all'attributo value del ListItem selezionato nell'elenco.
- in [4], l’elenco a discesa. Contiene gli stessi elementi presenti dopo il GET iniziale. Ciò è dovuto al meccanismo del VIEWSTATE.
Per comprendere l’interazione tra il VIEWSTATE dell’elenco DropDownListNoms e il test if (! IsPostBack) del gestore Page_Load di [Default.aspx], il lettore è invitato a ripetere il test precedente con le seguenti configurazioni:
Caso | DropDownListNoms.EnableViewState | test if(! IsPostBack) in Page_Load di [Default.aspx] |
I vari test danno i seguenti risultati:
- questo è il caso illustrato in precedenza
- l'elenco viene compilato durante il GET iniziale, ma non durante i POST successivi. Poiché EnableViewState restituisce un valore falso, l'elenco risulta vuoto dopo ogni POST
- l’elenco viene compilato sia dopo il GET iniziale sia durante i POST successivi. Poiché EnableViewState è uguale a vrai, si hanno 3 nomi dopo il GET iniziale, 6 nomi dopo il primo POST, 9 nomi dopo il secondo POST, ...
- L'elenco viene compilato sia dopo il GET iniziale sia durante i POST successivi. Poiché EnableViewState è uguale a faux, l'elenco viene compilato con soli 3 nomi per ogni richiesta, sia che si tratti della richiesta iniziale GET sia delle successive POST. Si riscontra lo stesso comportamento del caso 1. Esistono quindi due modi per ottenere lo stesso risultato.
2.7. Gestione degli elementi di una pagina ASPX tramite VIEWSTATE
Per impostazione predefinita, tutti gli elementi di una pagina ASPX hanno la proprietà EnableViewState impostata su True. Ogni volta che la pagina ASPX viene inviata al browser del cliente, essa contiene il campo nascosto __VIEWSTATE il cui valore è una stringa di caratteri che codifica l’insieme dei valori dei componenti con proprietà comprese tra EnableViewState e True. Per ridurre al minimo la lunghezza di questa stringa, si può cercare di ridurre il numero di componenti con proprietà comprese tra EnableViewState e True.
Ricordiamo come i componenti di una pagina ASPX ottengono i propri valori al termine di un POST:
- viene istanziata la pagina ASPX. I componenti vengono inizializzati con i loro valori di progettazione.
- il valore __VIEWSTATE inviato dal browser viene utilizzato per assegnare ai componenti il valore che avevano quando la pagina ASPX è stata inviata al browser la volta precedente.
- I valori inviati dal browser vengono assegnati ai componenti
- vengono eseguiti i gestori di eventi. Questi possono modificare il valore di alcuni componenti.
Da questa sequenza si deduce che i componenti che:
- hanno il loro valore inviato
- il cui valore viene modificato da un gestore di eventi
possono avere la proprietà da EnableViewState a Faux poiché il loro valore di VIEWSTATE (fase 2) verrà modificato da una delle fasi 3 o 4.
L'elenco dei componenti della nostra pagina è disponibile in [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;
}
}
Il valore della proprietà EnableViewState di questi componenti potrebbe essere il seguente:
Composant | Valore inviato | EnableViewState | Pourquoi |
TextBoxNom | valore inserito in TextBox | False | il valore del componente è stato registrato |
TextBoxAge | idem | ||
RequiredFieldValidatorNom | nessuno | False | mancanza di un valore per il componente |
RequiredFieldValidatorAge | idem | ||
RangeValidatorAge | idem | ||
LabelPost | nessuna | False | il suo valore viene determinato da un gestore di eventi |
LabelValidation | idem | ||
LabelErreursSaisie | idem | ||
LabelGlobal | idem | ||
LabelNbRequetes | idem | ||
DropDownListNoms | "valore" dell'elemento selezionato | True | si desidera mantenere il contenuto dell'elenco nel corso delle richieste senza doverlo rigenerare |
ListBoxEvts | "value" dell'elemento selezionato | False | il contenuto dell'elenco viene generato da un gestore di eventi |
ButtonValider | Etichetta del pulsante | False | il componente mantiene il proprio valore di progettazione |
2.8. Inoltro da una pagina a un'altra
Finora, le operazioni GET e POST restituivano sempre la stessa pagina [Default.aspx]. Consideriamo il caso in cui una richiesta venga elaborata da due pagine ASPX successive, [Default.aspx] e [Page1.aspx], e in cui sia quest’ultima a essere restituita al cliente. Inoltre, vedremo come la pagina [Default.aspx] possa trasmettere informazioni alla pagina [Page1.aspx] tramite una memoria che chiameremo «memoria della richiesta».
![]() |
Costruiamo la pagina [Page1.aspx]:
![]() |
- in [1], aggiungiamo un nuovo elemento al progetto
- in [2], si aggiunge un elemento [Web Form] denominato [Page1.aspx] [3]
![]() |
- in [4], la pagina aggiunta
- in [5], la pagina una volta generata
Il codice sorgente di [Page1.aspx] è il seguente:
<%@ 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>
- riga 13: un'etichetta che servirà a visualizzare un'informazione trasmessa dalla pagina [Default.aspx]
- riga 15: un link HTML alla pagina [Default.aspx]. Quando l'utente clicca su questo link, il browser richiede la pagina [Default.aspx] con un'operazione GET. La pagina [Default.aspx] viene quindi caricata come se l'utente avesse digitato direttamente il suo URL nel browser.
La pagina [Default.aspx] viene arricchita con un nuovo componente di tipo LinkButton:
![]() |
Il codice sorgente di questo nuovo componente è il seguente:
<asp:LinkButton ID="LinkButtonToPage1" runat="server" CausesValidation="False"
EnableViewState="False" onclick="LinkButtonToPage1_Click">Forward vers Page1</asp:LinkButton>
- CausesValidation="False": cliccando sul link si attiverà un POST verso [Defaul.aspx]. Il componente [LinkButton] si comporta come il componente [Button]. In questo caso, non si desidera che il clic sul link attivi l'esecuzione dei validatori.
- EnableViewState="False": non è necessario conservare lo stato del link nel corso delle richieste. Mantiene i valori di progettazione.
- onclick="LinkButtonToPage1_Click": nome del metodo che, in [Defaul.aspx.cs], gestisce l'evento Click sul componente LinkButtonToPage1.
Il codice del gestore LinkButtonToPage1_Click è il seguente:
// verso Pagina1
protected void LinkButtonToPage1_Click(object sender, EventArgs e)
{
// si inseriscono informazioni nel contesto
Context.Items["msg1"] = "Message de Default.aspx pour Page1";
// si inoltra la richiesta a Pagina1
Server.Transfer("Page1.aspx",true);
}
Riga 7: la richiesta viene inoltrata alla pagina [Page1.aspx] tramite il metodo [Server.Transfer]. Il secondo parametro del metodo, che è true, indica che è necessario passare a [Page1.aspx] tutte le informazioni che sono state inviate a [Default.aspx] durante l'esecuzione di POST. Ciò consente, ad esempio, a [Page1.aspx] di accedere ai valori inviati tramite una collezione denominata Request.Form. La riga 5 utilizza il cosiddetto contesto della richiesta. È possibile accedervi tramite la proprietà Context della classe Page. Questo contesto può fungere da memoria tra le diverse pagine che elaborano la stessa richiesta, in questo caso [Default.aspx] e [Page1.aspx]. A tal fine si utilizza il dizionario Items.
Quando [Page1.aspx] viene caricata dall’operazione Server.Transfer("Page1.aspx",true), tutto avviene come se [Page1.aspx] fosse stato chiamato da un GET di un browser. Il gestore Page_Load di [Page1.aspx] viene eseguito normalmente. Lo useremo per visualizzare il messaggio inserito da [Default.aspx] nel contesto della richiesta:
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;
}
}
}
Alla riga 9, il messaggio inserito da [Default.aspx] nel contesto della richiesta viene visualizzato in Label1.
Ecco un esempio di esecuzione:
![]() |
- nella pagina [Default.aspx] [1], si clicca sul link [2] che ci porta alla pagina Page1
- in [3], viene visualizzata la pagina Page1
- in [4], il messaggio creato in [Default.aspx] e visualizzato da [Page1.aspx]
- in [5], la pagina URL visualizzata nel browser è quella della pagina [Default.aspx]
2.9. Reindirizzamento da una pagina a un’altra
Presentiamo qui un'altra tecnica funzionalmente simile alla precedente: quando l'utente richiede la pagina [Default.aspx] tramite una POST, riceve in risposta un'altra pagina [Page2.aspx]. Nel metodo precedente, la richiesta dell’utente veniva elaborata in successione da due pagine: [Default.aspx] e [Page1.aspx]. Nel metodo di reindirizzamento della pagina che presentiamo ora, vi sono due richieste distinte da parte del browser:
![]() |
- a [1], il browser invia una richiesta POST alla pagina [Default.aspx]. Quest’ultima elabora la richiesta e invia al browser una risposta detta di reindirizzamento. Questa risposta è un semplice flusso HTTP (righe di testo) che chiede al browser di reindirizzarsi verso un altro URL [Page2.aspx]. [Default.aspx] non invia alcun flusso HTML in questa prima risposta.
- In [2], il browser effettua una richiesta GET alla pagina [Page2.aspx]. Questa viene quindi inviata come risposta al browser.
- Se la pagina [Default.aspx] desidera trasmettere informazioni alla pagina [Page2.aspx], può farlo tramite la sessione dell’utente. A differenza del metodo precedente, il contesto della richiesta non è utilizzabile in questo caso, poiché vi sono due richieste distinte e quindi due contesti distinti. È quindi necessario utilizzare la sessione dell’utente per consentire alle pagine di comunicare tra loro.
Come già fatto per [Page1.aspx], aggiungiamo al progetto la pagina [Page2.aspx]:
![]() |
- in [1], [Page2.aspx] è stata aggiunta al progetto
- in [2], l’aspetto visivo di [Page2.aspx]
- in [3], aggiungiamo alla pagina [Default.aspx] un componente LinkButton [4] che reindirizzerà l'utente verso [Page2.aspx].
Il codice sorgente di [Page2.aspx] è analogo a quello di [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>
In [Default.aspx], l’aggiunta del componente LinkButton ha generato il seguente codice sorgente:
<asp:LinkButton ID="LinkButtonToPage2" runat="server"
onclick="LinkButtonToPage2_Click">Redirection vers Page2</asp:LinkButton>
È il gestore [LinkButtonToPage2_Click] che garantisce il reindirizzamento verso [Page2.aspx]. Il suo codice in [Defaul.aspx.cs] è il seguente:
protected void LinkButtonToPage2_Click(object sender, EventArgs e)
{
// si inserisce un messaggio nella sessione
Session["msg2"] = "Message de [Default.aspx] pour [Page2.aspx]";
// si reindirizza il client a [Page2.aspx]
Response.Redirect("Page2.aspx");
}
- riga 4: si inserisce un messaggio nella sessione dell'utente
- riga 5: l'oggetto Response è una proprietà di ogni pagina ASPX. Rappresenta la risposta inviata al client. Dispone di un metodo Redirect che fa sì che la risposta inviata al client sia un comando di reindirizzamento HTTP.
Quando il browser riceverà il comando di reindirizzamento verso [Page2.aspx], eseguirà un GET su questa pagina. In quest’ultima verrà eseguito il metodo [Page_Load]. Lo useremo per recuperare il messaggio inserito da [Default.aspx] nella sessione e visualizzarlo. Il codice [Page2.aspx.cs] è il seguente:
using System;
namespace Intro
{
public partial class Page2 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// si visualizza il messaggio inserito nella sessione da [Default.aspx]
Label1.Text = Session["msg2"] as string;
}
}
}
All’esecuzione, si ottengono i seguenti risultati:
![]() |
- in [1], si clicca sul link di reindirizzamento di [Default.aspx]. Viene effettuato un reindirizzamento da POST alla pagina [Default.aspx]
- in [2], il browser è stato reindirizzato a [Page2.aspx]. Ciò è evidente dall'URL URL visualizzato dal browser. Nel metodo precedente, tale URL era quello di [Default.aspx] poiché l’unica richiesta effettuata dal browser era diretta a tale URL. In questo caso c’è un primo POST verso [Default.aspx], poi, all’insaputa dell’utente, un secondo GET verso [Page2.aspx].
- In [3], si vede che [Page2.aspx] ha recuperato correttamente il messaggio inserito da [Default.aspx] nella sessione.
2.10. Conclusion
Abbiamo introdotto, con l’ausilio di alcuni esempi, i concetti di ASP.NET che ci saranno utili nel prosieguo del documento. Questa introduzione non consente di comprendere le sottigliezze degli scambi client/server di un’applicazione web. A tal fine, si può consultare:
- Programmazione ASP.NET [Développement WEB avec ASP.NET 1.1 ]



































