Share via


Informazioni sullo stato di visualizzazione ASP.NET

 

Scott Mitchell
4GuysFromRolla.com

Maggio 2004

Si applica a:
   Microsoft® ASP.NET
   Microsoft® Visual Studio® .NET

Riepilogo: Scott Mitchell esamina i vantaggi di e confusione intorno a View State in Microsoft® ASP.NET. Inoltre, illustra come interpretare (e proteggere) i dati archiviati in View State. (25 pagine stampate)

Fare clic qui per scaricare l'esempio di codice per questo articolo.

Contenuto

Introduzione
Ciclo di vita della pagina ASP.NET
Ruolo dello stato di visualizzazione
Visualizzare lo stato e i controlli aggiunti dinamicamente
Proprietà ViewState
Temporizzazione dello stato di visualizzazione
Archiviazione di informazioni nella proprietà ViewState della pagina
Costo dello stato di visualizzazione
Disabilitazione dello stato di visualizzazione
Specifica della posizione in cui mantenere lo stato di visualizzazione
Analisi dello stato di visualizzazione
Visualizzare le implicazioni sullo stato e sulla sicurezza
Conclusione

Introduzione

Lo stato di visualizzazione di Microsoft® ASP.NET, in un breve argomento, è la tecnica utilizzata da una pagina Web ASP.NET per rendere persistenti le modifiche allo stato di un modulo Web tra postback. Nelle mie esperienze come docente e consulente, lo stato di vista ha causato la più confusione tra gli sviluppatori ASP.NET. Quando si creano controlli server personalizzati o si eseguono tecniche di pagina più avanzate, non si ha una solida comprensione dello stato di visualizzazione e come funziona può tornare a bite. I progettisti Web che si concentrano sulla creazione di pagine con larghezza di banda ridotta, spesso si trovano frustrati con lo stato di visualizzazione, nonché. Lo stato di visualizzazione di una pagina è, per impostazione predefinita, inserito in un campo modulo nascosto denominato __VIEWSTATE. Questo campo modulo nascosto può facilmente ottenere molto grande, sull'ordine di decine di kilobyte. Non solo il __VIEWSTATE campo modulo causa download più lento, ma, ogni volta che l'utente pubblica la pagina Web, il contenuto di questo campo modulo nascosto deve essere pubblicato nuovamente nella richiesta HTTP, aumentando così l'ora della richiesta.

Questo articolo mira ad essere un esame approfondito dello stato di visualizzazione ASP.NET. Si esaminerà esattamente lo stato di visualizzazione archiviato e il modo in cui lo stato di visualizzazione viene serializzato nel campo modulo nascosto e deserializzato al postback. Verranno illustrate anche le tecniche per ridurre la larghezza di banda richiesta dallo stato di visualizzazione.

Nota Questo articolo è orientato allo sviluppatore di pagine ASP.NET anziché allo sviluppatore di controllo server ASP.NET. Questo articolo non include pertanto una discussione sul modo in cui uno sviluppatore di controlli implementerebbe lo stato di salvataggio. Per una discussione approfondita su questo problema, vedere il libro Sviluppo di controlli server e componenti di Microsoft ASP.NET.

Prima di poter approfondire l'esame dello stato di visualizzazione, è importante prima di tutto prendere un momento rapido per discutere il ciclo di vita della pagina ASP.NET. Che cosa accade esattamente quando una richiesta proviene da un browser per una pagina Web ASP.NET? Questo processo verrà illustrato nella sezione successiva.

Ciclo di vita della pagina ASP.NET

Ogni volta che una richiesta arriva a un server Web per una pagina Web ASP.NET, la prima cosa che il server Web esegue la distribuzione della richiesta al motore di ASP.NET. Il motore di ASP.NET accetta quindi la richiesta tramite una pipeline composta da numerose fasi, che include la verifica dei diritti di accesso ai file per la pagina Web ASP.NET, riattivando lo stato della sessione dell'utente e così via. Alla fine della pipeline viene creata un'istanza di una classe corrispondente alla pagina Web richiesta ASP.NET e viene richiamato il ProcessRequest() metodo (vedere la figura 1).

Fare clic qui per un'immagine più grande.

Figura 1. gestione delle pagine ASP.NET

Questo ciclo di vita della pagina ASP.NET inizia con una chiamata al ProcessRequest() metodo. Questo metodo inizia inizializzando la gerarchia di controllo della pagina. Successivamente, la pagina e i relativi controlli server proseguono il blocco attraverso varie fasi essenziali per l'esecuzione di una pagina Web ASP.NET. Questi passaggi includono la gestione dello stato di visualizzazione, la gestione degli eventi postback e il rendering del markup HTML della pagina. La figura 2 fornisce una rappresentazione grafica del ciclo di vita della pagina ASP.NET. Il ciclo di vita termina passando il markup HTML della pagina Web al server Web, che lo invia al client che ha richiesto la pagina.

Nota Una discussione dettagliata dei passaggi che portano al ciclo di vita della pagina ASP.NET è oltre l'ambito di questo articolo. Per altre informazioni, vedere Michele Leroux-Bustamante's Inside IIS & ASP.NET. Per un'analisi più dettagliata dei gestori HTTP, ovvero gli endpoint della pipeline di ASP.NET, vedere l'articolo precedente sui gestori HTTP.

Ciò che è importante realizzare è che ogni volta che viene richiesta una pagina Web ASP.NET passa attraverso queste stesse fasi del ciclo di vita (illustrato nella figura 2).

ms972976.viewstate_fig02(en-us,MSDN.10).gifms972976.viewstate_fig02

Figura 2. Eventi nel ciclo di vita della pagina

Fase 0 - Creazione di istanze

Il ciclo di vita della pagina ASP.NET inizia con l'istanza della classe che rappresenta la pagina Web richiesta ASP.NET, ma come viene creata questa classe? Dove è archiviato?

ASP.NET pagine Web, come si sa, sono costituite da una parte HTML e una parte di codice, con la parte HTML contenente il markup HTML e la sintassi del controllo Web. Il motore di ASP.NET converte la parte HTML dalla relativa rappresentazione di testo in formato libero in una serie di controlli Web creati a livello di codice.

Quando viene visitata una pagina Web ASP.NET per la prima volta dopo che è stata apportata una modifica alla sintassi del markup HTML o del controllo Web nella .aspx pagina, il motore di ASP.NET genera automaticamente una classe. Se è stata creata la pagina Web ASP.NET usando la tecnica code-behind, questa classe generata automaticamente deriva dalla classe code-behind associata della pagina (si noti che la classe code-behind deve essere derivata direttamente dalla classe, direttamente o indirettamente, dalla System.Web.UI.Page classe); se è stata creata la pagina con un blocco in linea, lato <script> server, la classe deriva direttamente da System.Web.UI.Page. In entrambi i casi, questa classe generata automaticamente, insieme a un'istanza compilata della classe, viene archiviata nella WINDOWS``\Microsoft.NET\Framework\``version``\Temporary ASP.NET Files cartella, in modo che non sia necessario ricrearla per ogni richiesta di pagina.

Lo scopo di questa classe generata automaticamente consiste nel creare a livello di codice la gerarchia di controllo della pagina. Ovvero, la classe è responsabile della creazione a livello di codice dei controlli Web specificati nella parte HTML della pagina. Questa operazione viene eseguita traducendo la sintassi del controllo Web,<asp:``WebControlName Prop1="Value1" ...`` /> nel linguaggio di programmazione della classe (C# o Microsoft® Visual Basic® .NET, in genere). Oltre alla sintassi del controllo Web da convertire nel codice appropriato, il markup HTML presente nella parte HTML della pagina Web di ASP.NET viene convertito nei controlli Letterale.

Tutti i controlli server ASP.NET possono avere un controllo padre, insieme a un numero variabile di controlli figlio. La System.Web.UI.Page classe è derivata dalla classe di controllo di base (System.Web.UI.Control) e può quindi avere un set di controlli figlio. I controlli di primo livello dichiarati in una parte HTML di una pagina Web ASP.NET sono i figli diretti della classe generata automaticamente Page . I controlli Web possono anche essere annidati tra loro. Ad esempio, la maggior parte delle pagine Web ASP.NET contiene un singolo modulo Web lato server, con più controlli Web all'interno del Web Form. Il Modulo Web è un controllo HTML (System.Web.UI.HtmlControls.HtmlForm). Tali controlli Web all'interno del Web Form sono figlio del Web Form.

Poiché i controlli server possono avere elementi figlio e ognuno dei propri figli può avere elementi figlio e così via, un controllo e i relativi discendenti formano un albero dei controlli. Questo albero dei controlli viene chiamato gerarchia di controllo. La radice della gerarchia di controllo per una pagina Web ASP.NET è la Pageclasse derivata generata automaticamente dal motore di ASP.NET.

Ben fatto. Questi ultimi paragrafi potrebbero essere stati un po' confusi, perché questo non è l'argomento più semplice da discutere o digerire. Per cancellare eventuali potenziali confusioni, si esaminerà un esempio rapido. Si supponga di avere una pagina Web ASP.NET con la parte HTML seguente:

<html>
<body>
  <h1>Welcome to my Homepage!</h1>
  <form runat="server">
    What is your name?
    <asp:TextBox runat="server" ID="txtName"></asp:TextBox>
    <br />What is your gender?
    <asp:DropDownList runat="server" ID="ddlGender">
      <asp:ListItem Select="True" Value="M">Male</asp:ListItem>
      <asp:ListItem Value="F">Female</asp:ListItem>
      <asp:ListItem Value="U">Undecided</asp:ListItem>
    </asp:DropDownList>
    <br />
    <asp:Button runat="server" Text="Submit!"></asp:Button>
  </form>
</body>
</html>

Quando questa pagina viene visitata per la prima volta, verrà generata automaticamente una classe che contiene codice per compilare a livello di codice la gerarchia di controllo. La gerarchia di controllo per questo esempio può essere vista nella figura 3.

ms972976.viewstate_fig03(en-us,MSDN.10).gifms972976.viewstate_fig03(en-us,MSDN.10)

Figura 3. Gerarchia di controllo per la pagina di esempio

Questa gerarchia di controllo viene quindi convertita in codice simile al seguente:

Page.Controls.Add( 
  new LiteralControl(@"<html>\r\n<body>\r\n
    <h1>Welcome to my Homepage!</h1>\r\n"));
HtmlForm Form1 = new HtmlForm();
Form1.ID = "Form1";
Form1.Method = "post";
Form1.Controls.Add(
  new LiteralControl("\r\nWhat is your name?\r\n"));
TextBox TextBox1 = new TextBox();
TextBox1.ID = "txtName";
Form1.Controls.Add(TextBox1);
Form1.Controls.Add(
  new LiteralControl("\r\n<br />What is your gender?\r\n"));
DropDownList DropDownList1 = new DropDownList();
DropDownList1.ID = "ddlGender";
ListItem ListItem1 = new ListItem();
ListItem1.Selected = true;
ListItem1.Value = "M";
ListItem1.Text = "Male";
DropDownList1.Items.Add(ListItem1);
ListItem ListItem2 = new ListItem();
ListItem2.Value = "F";
ListItem2.Text = "Female";
DropDownList1.Items.Add(ListItem2);
ListItem ListItem3 = new ListItem();
ListItem3.Value = "U";
ListItem3.Text = "Undecided";
DropDownList1.Items.Add(ListItem3);
Form1.Controls.Add(
  new LiteralControl("\r\n<br /> \r\n"));
Button Button1 = new Button();
Button1.Text = "Submit!";
Form1.Controls.Add(Button1);
Form1.Controls.Add(
  new LiteralControl("\r\n</body>\r\n</html>"));
Controls.Add(Form1);

Nota Il codice sorgente C# precedente non è il codice preciso generato automaticamente dal motore di ASP.NET. Invece, è una versione più pulita e più semplice da leggere del codice generato automaticamente. Per visualizzare il codice generato automaticamente completo, che non vincerà alcun punto per la leggibilità, passare alla WINDOWS``\Microsoft.NET\Framework\``Version``\Temporary ASP.NET Files cartella e aprire uno dei .cs file o .vb .

Una cosa da notare è che, quando viene creata la gerarchia di controllo, le proprietà impostate in modo esplicito nella sintassi dichiarativa del controllo Web vengono assegnate nel codice. Ad esempio, il controllo Web Button ha la proprietà Text impostata su "Invia!" nella sintassi dichiarativa, Text="Submit!" oltre che nella classeButton1.Text = "Submit!"; generata automaticamente.

Fase 1 - Inizializzazione

Dopo aver compilato la gerarchia di controllo, , Pageinsieme a tutti i controlli nella gerarchia di controllo, immettere la fase di inizializzazione. Questa fase è contrassegnata Init dalla presenza degli eventi e dei Page controlli. A questo punto nel ciclo di vita della pagina, la gerarchia di controllo è stata creata e le proprietà del controllo Web specificate nella sintassi dichiarativa sono state assegnate.

La fase di inizializzazione verrà esaminata in dettaglio più avanti in questo articolo. Per quanto riguarda lo stato di visualizzazione, è importante per due motivi; prima di tutto, i controlli server non iniziano a tenere traccia delle modifiche dello stato della visualizzazione fino alla fine della fase di inizializzazione. In secondo luogo, quando si aggiungono controlli dinamici che devono usare lo stato di visualizzazione, questi controlli dovranno essere aggiunti durante l'evento Pagedell'evento Init anziché l'evento Load , come si vedrà brevemente.

Fase 2 - Stato visualizzazione carico

La fase dello stato della visualizzazione di carico si verifica solo quando la pagina è stata pubblicata di nuovo. Durante questa fase, i dati sullo stato di visualizzazione salvati dalla pagina precedente vengono caricati e popolati in modo ricorsivo nella gerarchia di controllo dell'oggetto Page. È durante questa fase che lo stato di visualizzazione viene convalidato. Come illustrato più avanti in questo articolo, lo stato di visualizzazione può diventare non valido a causa di un numero di motivi, ad esempio la manomissione dello stato di visualizzazione e l'inserimento di controlli dinamici al centro della gerarchia di controllo.

Fase 3 - Caricare i dati postback

La fase dei dati di postback di caricamento avviene solo quando la pagina è stata pubblicata di nuovo. Un controllo server può indicare che è interessato ad esaminare i dati di backup pubblicati implementando l'interfaccia IPostBackDataHandler . In questa fase del ciclo di vita della pagina, la classe Page enumera i campi modulo restituiti e cerca il controllo server corrispondente. Se trova il controllo, verifica se il controllo implementa l'interfaccia IPostBackDataHandler . In caso contrario, passa i dati di postback appropriati al controllo server chiamando il metodo del controllo del LoadPostData() controllo. Il controllo server aggiornerebbe quindi lo stato in base ai dati di postback.

Per chiarire le cose, esaminiamo un semplice esempio. Una cosa interessante di ASP.NET è che i controlli Web in un modulo Web ricordano i relativi valori nel postback. Ovvero, se si dispone di un controllo Web TextBox in una pagina e l'utente immette un valore nella casella di testo e pubblica la pagina, la proprietà TextBox Text viene aggiornata automaticamente al valore immesso dall'utente. Ciò avviene perché il controllo Web TextBox implementa l'interfaccia IPostBackDataHandler e la classe Page disattiva il valore appropriato alla classe TextBox, che aggiorna quindi la relativa Text proprietà.

Per concretizzare le cose, si supponga di avere una pagina Web ASP.NET con un controllo TextBox la cui ID proprietà è impostata su txtName. Quando la pagina viene prima visitata, verrà eseguito il rendering del codice HTML seguente per TextBox: <input type="text" id="txtName" name="txtName" />. Quando l'utente immette un valore in questa casella di testo , ad esempio "Hello, World!", e invia il modulo, il browser eseguirà una richiesta alla stessa pagina Web ASP.NET, passando di nuovo i valori del campo modulo nelle intestazioni HTTP POST. Questi includono i valori del campo modulo nascosti (ad esempio __VIEWSTATE), insieme al valore di txtName TextBox.

Quando la pagina Web ASP.NET viene pubblicata nuovamente nella fase dei dati di postback di caricamento, la Page classe vede che uno dei campi del modulo di backup pubblicato corrisponde all'interfaccia IPostBackDataHandler . Esiste un tale controllo nella gerarchia, quindi viene richiamato il metodo TextBox, passando il valore immesso nell'oggetto LoadPostData() TextBox ("Hello, World!"). Il metodo textBox LoadPostData() assegna semplicemente questo valore passato alla relativa Text proprietà.

Si noti che nella discussione sulla fase dei dati di postback di caricamento non è stata menzionata lo stato di visualizzazione. Si potrebbe naturalmente chiedersi, quindi, perché mi sono preoccupato di menzionare la fase dei dati di postback di caricamento in un articolo sullo stato di visualizzazione. Il motivo è notare l'assenza di stato di visualizzazione in questa fase. Si tratta di un errore comune tra gli sviluppatori che visualizzano lo stato è in qualche modo responsabile della presenza di Caselle di testo, CheckBoxes, DropDownLists e altri controlli Web ricordano i valori tra postback. Questo non è il caso, poiché i valori vengono identificati tramite i valori dei campi modulo restituiti e assegnati nel LoadPostData() metodo per tali controlli che implementano IPostBackDataHandler.

Fase 4 - Caricamento

Questa è la fase con cui tutti gli sviluppatori di ASP.NET hanno familiarità, come abbiamo creato tutti un gestore eventi per l'evento di Load una pagina (Page_Load). Quando l'evento Load viene attivato, lo stato di visualizzazione è stato caricato (dalla fase 2, Stato visualizzazione carico), insieme ai dati di postback (dalla fase 3, Load Postback Data). Se la pagina è stata pubblicata di nuovo, quando l'evento viene generato è noto che la Load pagina è stata ripristinata allo stato della pagina precedente.

Fase 5 - Generare l'evento Postback

Alcuni controlli server generano eventi rispetto alle modifiche che si sono verificate tra postback. Ad esempio, il controllo Web DropDownList ha un SelectedIndexChanged evento, che viene generato se il controllo DropDownList SelectedIndex è cambiato dal SelectedIndex valore nel caricamento della pagina precedente. Un altro esempio: se il modulo Web è stato pubblicato a causa di Click un controllo Web Button da fare clic, l'evento Button viene attivato durante questa fase.

Esistono due tipi di eventi postback. Il primo è un evento modificato . Questo evento viene generato quando alcuni dati vengono modificati tra postback. Un esempio è l'evento DropDownLists SelectedIndexChanged o l'evento TextChanged TextBox. I controlli server che forniscono eventi modificati devono implementare l'interfaccia IPostBackDataHandler . L'altro sapore degli eventi postback è l'evento generato . Questi sono eventi generati dal controllo server per qualsiasi motivo il controllo del controllo del server rileva l'idoneità. Ad esempio, il controllo Web Button genera l'evento Click quando viene fatto clic e il controllo Calendar genera l'evento VisibleMonthChanged quando l'utente passa a un altro mese. I controlli generati dagli eventi generati devono implementare l'interfaccia IPostBackEventHandler .

Poiché questa fase controlla i dati di postback per determinare se è necessario generare eventi, la fase si verifica solo quando la pagina è stata pubblicata. Come per la fase dei dati di postback di caricamento, la fase dell'evento postback di generazione non usa le informazioni sullo stato di visualizzazione. Se viene generato o meno un evento dipende dai dati pubblicati di nuovo nei campi del modulo.

Fase 6 - Salva stato di visualizzazione

Nella fase di salvataggio dello stato della visualizzazione, la Page classe costruisce lo stato di visualizzazione della pagina, che rappresenta lo stato che deve essere persistente tra postback. La pagina esegue questa operazione chiamando in modo ricorsivo il SaveViewState() metodo dei controlli nella gerarchia di controllo. Questo stato combinato e salvato viene quindi serializzato in una stringa codificata base-64. Nella fase 7, quando viene eseguito il rendering del Modulo Web della pagina, lo stato di visualizzazione viene mantenuto nella pagina come campo modulo nascosto.

Fase 7 - Rendering

Nella fase di rendering viene generato il codice HTML generato al client che richiede la pagina. La Page classe esegue questa operazione richiamando in modo ricorsivo il RenderControl() metodo di ognuno dei controlli nella gerarchia.

Queste sette fasi sono le fasi più importanti rispetto allo stato di visualizzazione della comprensione. Si noti che ho omesso un paio di fasi, ad esempio le fasi PreRender e Unload. Mentre si continua attraverso l'articolo, tenere presente che ogni volta che viene richiesta una pagina Web ASP.NET, procede attraverso queste serie di fasi.

Ruolo dello stato di visualizzazione

Lo scopo dello stato di visualizzazione nella vita è semplice: è lì per mantenere lo stato tra postback. Per una pagina Web ASP.NET, lo stato è i valori delle proprietà dei controlli che costituiscono la gerarchia di controllo. Si tratta di una domanda: "Quale tipo di stato deve essere persistente?" Per rispondere a tale domanda, iniziamo esaminando lo stato che non deve essere mantenuto tra postback. Tenere presente che nella fase di creazione di istanze del ciclo di vita della pagina viene creata la gerarchia di controllo e vengono assegnate le proprietà specificate nella sintassi dichiarativa. Poiché queste proprietà dichiarative vengono riassegnate automaticamente in ogni postback quando viene creata la gerarchia di controllo, non è necessario archiviare questi valori di proprietà nello stato di visualizzazione.

Si supponga, ad esempio, di avere un controllo Web Label nella parte HTML con la sintassi dichiarativa seguente:

<asp:Label runat="server" Font-Name="Verdana" 
  Text="Hello, World!"></asp:Label>

Quando la gerarchia di controllo viene compilata nella fase di creazione di istanze, la proprietà dell'etichetta Text verrà impostata su "Hello, World!" e la Font relativa Name proprietà sarà impostata su Verdana. Poiché queste proprietà verranno impostate ogni pagina e ogni pagina visita durante la fase di creazione di istanze, non è necessario rendere persistenti queste informazioni nello stato di visualizzazione.

Ciò che deve essere archiviato nello stato di visualizzazione è qualsiasi modifica a livello di codice allo stato della pagina. Si supponga, ad esempio, che oltre a questo controllo Web Etichetta, la pagina contenga anche due controlli Web Button, un pulsante Change Message e un pulsante Postback vuoto. Il pulsante Change Message ha un Click gestore eventi che assegna la proprietà dell'etichetta Text a "Addio, Tutti!"; il pulsante Postback vuoto causa solo un postback, ma non esegue alcun codice. È necessario salvare la modifica alla proprietà dell'etichetta Text nel pulsante Cambia messaggio nello stato di visualizzazione. Per vedere come e quando questa modifica verrà apportata, verrà illustrato un esempio rapido. Supponendo che la parte HTML della pagina contenga il markup seguente:

<asp:Label runat="server" ID="lblMessage" 
  Font-Name="Verdana" Text="Hello, World!"></asp:Label>
<br />
<asp:Button runat="server" 
  Text="Change Message" ID="btnSubmit"></asp:Button>
<br />
<asp:Button runat="server" Text="Empty Postback"></asp:Button>

E la classe code-behind contiene il gestore eventi seguente per l'evento Click del pulsante:

private void btnSubmit_Click(object sender, EventArgs e)
{
  lblMessage.Text = "Goodbye, Everyone!";
}

La figura 4 illustra la sequenza di eventi che traspire, evidenziando il motivo per cui la modifica alla proprietà dell'etichetta deve essere archiviata nello stato di Text visualizzazione.

ms972976.viewstate_fig04(en-us,MSDN.10).gifms972976.viewstate_fig04

Figura 4. Eventi e stato di visualizzazione

Per comprendere il motivo per cui salvare la proprietà modificata Text dell'etichetta nello stato di visualizzazione è fondamentale, considerare cosa accadrebbe se queste informazioni non erano persistenti nello stato di visualizzazione. Si supponga che nella fase di salvataggio dello stato di visualizzazione del passaggio 2 non siano state mantenute informazioni sullo stato di visualizzazione. Se si trattasse del caso, nel passaggio 3 la proprietà Text dell'etichetta verrà assegnata a "Hello, World!" nella fase di creazione di istanze, ma non verrà riassegnata a "Addio, Tutti!" nella fase di visualizzazione del carico. Pertanto, dal punto di vista dell'utente finale, la proprietà dell'etichetta Text sarebbe "Addio, Tutti!" nel passaggio 2, ma sembra essere reimpostata al valore originale ("Hello, World!") nel passaggio 3, dopo aver fatto clic sul pulsante Vuoto Postback.

Visualizzare lo stato e i controlli aggiunti dinamicamente

Poiché tutti i controlli server ASP.NET contengono una raccolta di controlli figlio esposti tramite la Controls proprietà, i controlli possono essere aggiunti dinamicamente alla gerarchia di controllo aggiungendo nuovi controlli alla raccolta di Controls un controllo server. Una discussione approfondita dei controlli dinamici è un po' oltre l'ambito di questo articolo, quindi non verrà illustrato in dettaglio questo argomento; Si concentrerà invece su come gestire lo stato di visualizzazione per i controlli aggiunti in modo dinamico. Per una lezione più dettagliata sull'uso di controlli dinamici, vedere Controlli dinamici in ASP.NET e Uso di controlli creati in modo dinamico.

Tenere presente che nel ciclo di vita della pagina viene creata la gerarchia di controllo e le proprietà dichiarative vengono impostate nella fase di creazione di istanze. Successivamente, nella fase dello stato di visualizzazione del carico viene ripristinato lo stato modificato nella pagina precedente. Pensando a questo, tre cose diventano chiare quando si riguardano i controlli dinamici:

  1. Poiché lo stato di visualizzazione persiste solo lo stato di controllo modificato tra postback e non i controlli effettivi, i controlli aggiunti dinamicamente devono essere aggiunti alla pagina Web ASP.NET, sia nella visita iniziale che in tutti i postback successivi.
  2. I controlli dinamici vengono aggiunti alla gerarchia di controllo nella classe code-behind e quindi vengono aggiunti a un certo punto dopo la fase di creazione di istanze.
  3. Lo stato di visualizzazione per questi controlli aggiunti in modo dinamico viene salvato automaticamente nella fase di salvataggio dello stato della visualizzazione. (Cosa accade nel postback se i controlli dinamici non sono ancora stati aggiunti al momento del rollback dello stato della visualizzazione del carico, tuttavia?)

Quindi, i controlli aggiunti dinamicamente devono essere aggiunti a livello di codice alla pagina Web in ogni pagina e ogni pagina visita. Il momento migliore per aggiungere questi controlli è durante la fase di inizializzazione del ciclo di vita della pagina, che si verifica prima della fase di visualizzazione del carico. Ciò significa che la gerarchia di controllo viene completata prima dell'arrivo della fase dello stato della visualizzazione di carico. Per questo motivo, è consigliabile creare un gestore eventi per l'evento Page della Init classe nella classe code-behind e aggiungere i controlli dinamici.

Nota È possibile uscire dal caricamento dei controlli nel Page_Load gestore eventi e mantenere correttamente lo stato di visualizzazione. Tutto dipende dal fatto che si impostano o meno proprietà dei controlli caricati in modo dinamico a livello di codice e, in tal caso, quando lo si sta eseguendo rispetto alla Controls.Add(``dynamicControl``) riga. Una discussione approfondita di questo articolo è un po'oltre l'ambito di questo articolo, ma il motivo per cui può funzionare è perché il Controls metodo della Add() proprietà carica in modo ricorsivo lo stato di visualizzazione dell'elemento padre nei relativi elementi figlio, anche se la fase dello stato di visualizzazione del carico è passata.

Quando si aggiunge un controllo dinamico c a un controllo padre p in base a una condizione , ovvero quando non li caricano in ogni pagina e ogni visita, è necessario assicurarsi di aggiungere c alla fine della raccolta diControls p. Il motivo è dovuto al fatto che lo stato di visualizzazione per p contiene anche lo stato di visualizzazione per gli elementi figlio di p e, come si esaminerà nella sezione "Analisi dello stato di visualizzazione", lo stato di visualizzazione p specifica lo stato di visualizzazione per i relativi elementi figlio per indice. Nella figura 5 viene illustrato come inserire un controllo dinamico in un punto diverso dalla fine dell'insieme Controls può causare uno stato di visualizzazione danneggiato.

ms972976.viewstate_fig05(en-us,MSDN.10).gifms972976.viewstate_fig05

Figura 5. Effetto dell'inserimento dei controlli sullo stato di visualizzazione

Proprietà ViewState

Ogni controllo è responsabile dell'archiviazione del proprio stato, che viene eseguito aggiungendo il relativo stato modificato alla proprietà ViewState . La ViewState proprietà è definita nella System.Web.UI.Control classe, ovvero tutti i controlli server ASP.NET dispongono di questa proprietà. Quando si parla dello stato di visualizzazione in generale, si useranno lettere minuscole con uno spazio tra visualizzazione e stato; quando si parla della ViewState proprietà, si userà la maiuscola corretta e il testo formattato dal codice.

Se si esaminano le semplici proprietà di qualsiasi controllo server di ASP.NET, si noterà che le proprietà vengono lette e scritte direttamente nello stato di visualizzazione. È possibile visualizzare il codice sorgente decompilato per un assembly .NET usando uno strumento come Reflectionor. Si consideri ad esempio la proprietà del NavigateUrl controllo Web HyperLink. Il codice per questa proprietà è simile al seguente:

public string NavigateUrl
{
  get
  {
    string text = (string) ViewState["NavigateUrl"];
    if (text != null)
       return text;
    else
       return string.Empty;
  }
  set
  {
    ViewState["NavigateUrl"] = value;
  }
}

Come illustrato nell'esempio ViewState di codice, ogni volta che viene letto la proprietà di un controllo, viene consultato il controllo. Se non è presente una voce in ViewState, viene restituito il valore predefinito per la proprietà. Quando la proprietà viene assegnata, il valore assegnato viene scritto direttamente nell'oggetto ViewState.

Nota Tutti i controlli Web usano il modello precedente per le proprietà semplici. Le proprietà semplici sono quelle che sono valori scalari, ad esempio stringhe, numeri interi, booleani e così via. Proprietà complesse, ad esempio la proprietà Font dell'etichetta, che potrebbero essere classi stesse, usare un approccio diverso. Per altre informazioni sulle tecniche di manutenzione dello stato per i controlli server di ASP.NET server, consultare il libro Sviluppo di controlli server di Microsoft ASP.NET Server.

La proprietà ViewState è di tipo System.Web.UI.StateBag. La StateBag classe fornisce un mezzo per archiviare coppie nome e valore, usando un System.Collections.Specialized.HybridDictionary oggetto dietro le quinte. Come illustrato nella sintassi della NavigateUrl proprietà, gli elementi possono essere aggiunti a e sottoposti a query dall'uso StateBag della stessa sintassi che è possibile usare per accedere agli elementi da una tabella Hash.

Temporizzazione dello stato di visualizzazione

Ricorda che in precedenza ho detto che lo stato di visualizzazione archivia solo lo stato che deve essere persistente tra postback. Un bit di stato che non deve essere mantenuto tra postback è le proprietà del controllo specificate nella sintassi dichiarativa, poiché vengono reinstatate automaticamente nella fase di creazione di istanze della pagina. Ad esempio, se si dispone di un controllo Web HyperLink in una pagina Web ASP.NET e si imposta in modo dichiarativo la NavigateUrl proprietà su http://www.ScottOnWriting.NET , queste informazioni non devono essere archiviate nello stato di visualizzazione.

La visualizzazione del codice della proprietà del NavigateUrl controllo HyperLink, tuttavia, sembra che se il controllo ViewState viene scritto in ogni volta che il valore della proprietà è impostato. Nella fase di creazione di istanze, pertanto, dove avremmo qualcosa di simile HyperLink1.NavigateUrl = http://www.ScottOnWriting.NET;a , sarebbe solo opportuno che queste informazioni vengano archiviate nello stato di visualizzazione.

Indipendentemente da ciò che potrebbe sembrare evidente, questo non è il caso. Il motivo è dovuto al fatto che la classe tiene traccia solo delle modifiche apportate ai relativi membri dopoTrackViewState() che il StateBag metodo è stato richiamato. Ovvero, se si dispone di un StateBagoggetto , qualsiasi e tutte le aggiunte o le modifiche apportate prima TrackViewState() non verranno salvate quando viene richiamato il SaveViewState() metodo. Il TrackViewState() metodo viene chiamato alla fine della fase di inizializzazione, che si verifica dopo la fase di creazione di istanze. Pertanto, le assegnazioni di proprietà iniziali nella fase di creazione di istanze, mentre vengono scritte ViewState nelle funzioni di accesso del set di proprietà, non vengono mantenute durante la SaveViewState() chiamata al metodo nella fase dello stato di visualizzazione di salvataggio, perché il TrackViewState() metodo deve ancora essere richiamato.

Nota Il motivo per cui il StateBagTrackViewState() metodo è quello di mantenere lo stato di visualizzazione il più possibile ridotto. Anche in questo caso, non si vuole archiviare i valori delle proprietà iniziali nello stato di visualizzazione, perché non devono essere mantenuti tra postback. Pertanto, il TrackViewState() metodo consente alla gestione dello stato di iniziare dopo le fasi di creazione e inizializzazione.

Archiviazione di informazioni nella proprietà ViewState della pagina

Poiché la Page classe è derivata dalla System.Web.UI.Control classe, è anche una ViewState proprietà. In effetti, è possibile usare questa proprietà per rendere persistenti informazioni specifiche della pagina e specifiche dell'utente tra postback. Dalla classe code-behind di una pagina Web di ASP.NET, la sintassi da usare è semplicemente:

ViewState[keyName] = value

Esistono diversi scenari quando è possibile archiviare le informazioni nell'oggetto Pageè ViewState utile. L'esempio canonico consiste nella creazione di una datagrid ordinabile ordinabile (o ordinabile, modificabile DataGrid), poiché l'espressione di ordinamento deve essere persistente tra postback. Ovvero, se i dati di DataGrid vengono prima ordinati e quindi visualizzati, quando si associa la pagina successiva dei dati a DataGrid, è importante ottenere la pagina successiva dei dati quando viene ordinato dall'espressione di ordinamento specificata dell'utente. L'espressione di ordinamento deve pertanto essere mantenuta in modo permanente. Esistono tecniche assortite, ma la più semplice, a mio parere, consiste nell'archiviare l'espressione di ordinamento nell'oggetto Page.ViewState

Per altre informazioni sulla creazione di datagrid ordinabili e ordinabili (o su un datagrid ordinabile, modificabile, modificabile), selezionare una copia del mio libro ASP.NET Data Web Controls Kick Start.

Costo dello stato di visualizzazione

Niente viene disponibile gratuitamente e lo stato di visualizzazione non è un'eccezione. Lo stato di visualizzazione ASP.NET impone due risultati delle prestazioni ogni volta che viene richiesta una pagina Web ASP.NET:

  1. In tutte le visite di pagina, durante la fase Page di salvataggio dello stato di visualizzazione la classe raccoglie lo stato di visualizzazione collettiva per tutti i controlli nella gerarchia di controllo e serializza lo stato in una stringa codificata base-64. Questa è la stringa generata nel file di modulo nascosto __VIEWSTATE . Analogamente, nei postback, la fase dello stato di visualizzazione del carico deve deserializzare i dati dello stato di visualizzazione persistente e aggiornare i controlli pertinenti nella gerarchia di controllo.
  2. Il __VIEWSTATE campo modulo nascosto aggiunge dimensioni aggiuntive alla pagina Web che il client deve scaricare. Per alcune pagine con stato di visualizzazione elevato, questo può essere decine di kilobyte di dati, che possono richiedere diversi secondi aggiuntivi (o minuti!) per gli utenti modem da scaricare. Inoltre, quando si esegue la registrazione, il __VIEWSTATE campo modulo deve essere inviato al server Web nelle intestazioni HTTP POST, aumentando così il tempo di richiesta postback.

Se si progetta un sito Web a cui si accede comunemente dagli utenti che arrivano tramite una connessione modem, si dovrebbe essere particolarmente interessati al bloat lo stato di visualizzazione potrebbe aggiungere a una pagina. Fortunatamente, esistono numerose tecniche che possono essere usate per ridurre le dimensioni dello stato di visualizzazione. Verrà innanzitutto illustrato come indicare in modo selettivo se un controllo server deve salvare lo stato di visualizzazione. Se lo stato di un controllo non deve essere persistente tra postback, è possibile disattivare il rilevamento dello stato di visualizzazione per tale controllo, salvando così i byte aggiuntivi che altrimenti sono stati aggiunti da tale controllo. In seguito, si esaminerà come rimuovere completamente lo stato di visualizzazione dai campi del modulo nascosti della pagina, archiviando invece lo stato di visualizzazione nel file system del server Web.

Disabilitazione dello stato di visualizzazione

Nella fase di salvataggio dello stato di visualizzazione del ciclo di vita della pagina di ASP.NET la Page classe scorre in modo ricorsivo i controlli nella gerarchia di controllo, richiamando il metodo di SaveViewState() ogni controllo. Questo stato collettivo è ciò che è persistente nel campo del modulo nascosto __VIEWSTATE . Per impostazione predefinita, tutti i controlli nella gerarchia di controllo registrano lo stato di visualizzazione quando viene richiamato il SaveViewState() metodo. Come sviluppatore di pagine, tuttavia, è possibile specificare che un controllo non deve salvare lo stato di visualizzazione o lo stato di visualizzazione dei controlli figlio impostando la proprietà del EnableViewState controllo su False (il valore predefinito è True).

La EnableViewState proprietà è definita nella System.Web.UI.Control classe, quindi tutti i controlli server dispongono di questa proprietà, inclusa la Page classe . È quindi possibile indicare che lo stato di visualizzazione di un'intera pagina non deve essere salvato impostando la Page classe EnableViewState su False. Questa operazione può essere eseguita nella classe code-behind con Page.EnableViewState = false; o come @Pagedirettiva a livello di<%@Page EnableViewState="False" %> .

Non tutti i controlli Web registrano la stessa quantità di informazioni nello stato di visualizzazione. Il controllo Web Etichetta, ad esempio, registra solo modifiche a livello di codice alle relative proprietà, che non influisce notevolmente sulle dimensioni dello stato di visualizzazione. DataGrid archivia tuttavia tutto il contenuto nello stato di visualizzazione. Per un DataGrid con molte colonne e righe, le dimensioni dello stato di visualizzazione possono essere aggiunte rapidamente. Ad esempio, DataGrid illustrato nella figura 6 (e incluso nel download del codice di questo articolo come HeavyDataGrid.aspx) ha una dimensione dello stato di visualizzazione di circa 2,8 kilobyte e una dimensione totale di pagina pari a 5.791 byte. (Quasi la metà delle dimensioni della pagina è dovuta al __VIEWSTATE campo modulo nascosto!) La figura 7 mostra uno screenshot dello stato di visualizzazione, che può essere visualizzato visitando la pagina Web ASP.NET, eseguendo una visualizzazione\origine e quindi individuando il __VIEWSTATE campo modulo nascosto.

ms972976.viewstate_fig06(en-us,MSDN.10).gifms972976.viewstate_fig06

Figura 6. Controllo DataGrid

ms972976.viewstate_fig07(en-us,MSDN.10).gifms972976.viewstate_fig07(en-us,MSDN.10)

Figura 7. Visualizza stato per il controllo DataGrid

Il download per questo articolo include anche una pagina Web ASP.NET denominata LightDataGrid.aspx, che ha lo stesso DataGrid illustrato nella figura 6, ma con la EnableViewState proprietà impostata su False. Dimensioni totali dello stato di visualizzazione per questa pagina? 96 byte. L'intero orologio delle dimensioni della pagina in 3.014 byte. LightDataGrid.aspx vanta una dimensione dello stato di visualizzazione circa 1/30th la dimensione di e una dimensione totale di HeavyDataGrid.aspxdownload che è circa metà di HeavyDataGrid.aspx. Con datagrid più ampie con più righe, questa differenza sarebbe ancora più pronunciata. Per altre informazioni sui confronti delle prestazioni tra DataGrid abilitate per lo stato e DataGrid disabilitate per la visualizzazione, vedere Decidere quando usare DataGrid, DataList o Repeater.

Si spera che l'ultimo paragrafo convinsi del vantaggio di impostare in modo intelligente la EnableViewState proprietà su False, soprattutto per i controlli dello stato di visualizzazione "pesante" come DataGrid. La domanda è ora: "Quando posso impostare in modo sicuro la EnableViewState proprietà su False?" Per rispondere a tale domanda, considerare quando è necessario usare lo stato di visualizzazione, solo quando è necessario ricordare lo stato tra postback. DataGrid archivia il contenuto nello stato di visualizzazione in modo che lo sviluppatore di pagine non debba ribinare i dati del database in DataGrid in ogni pagina e ogni caricamento di pagina, ma solo nel primo. Il vantaggio è che il database non deve essere accessibile come spesso. Se, tuttavia, si imposta la proprietà di DataGrid su False, è necessario ribinare i dati del EnableViewState database su DataGrid sia nel caricamento della prima pagina che in ogni postback successivo.

Per una pagina Web con dataGrid di sola lettura, ad esempio quella nella figura 6, è consigliabile impostare la proprietà di DataGrid EnableViewState su False. È anche possibile creare DataGrid ordinabili e visualizzabili con lo stato di visualizzazione disabilitato (come può essere visualizzato nella pagina, incluso nel LightDataGrid-WithFeatures.aspx download), ma, di nuovo, è necessario essere certi di associare i dati del database a DataGrid nella prima pagina visita, nonché su tutti i postback successivi.

Nota La creazione di un oggetto DataGrid modificabile con stato di visualizzazione disabilitato richiede una programmazione complessa, che comporta l'analisi dei campi del modulo di backup pubblicati in DataGrid modificabile. Tale sforzo strenuo è necessario perché, con una ribindatura cieco di DataGrid modificabile, i dati del database a DataGrid sovrascriveranno eventuali modifiche apportate dall'utente (vedere questa domande frequenti per altre informazioni).

Specifica della posizione in cui mantenere lo stato di visualizzazione

Dopo aver raccolto le informazioni sullo stato di visualizzazione per tutti i controlli nella gerarchia di controllo nella fase di salvataggio dello stato di visualizzazione, lo __VIEWSTATE mantiene nel campo modulo nascosto. Questo campo modulo nascosto può, naturalmente, aggiungere notevolmente alla dimensione complessiva della pagina Web. Lo stato di visualizzazione viene serializzato nel campo modulo nascosto nel Page metodo della SavePageStateToPersistenceMedium() classe durante la fase dello stato della visualizzazione di salvataggio e viene deserializzato dal metodo della classe LoadPageStateFromPersistenceMedium() Page nella fase dello stato della visualizzazione di carico. Con solo un po' di lavoro è possibile avere lo stato di visualizzazione persistente nel file system del server Web, anziché come campo di modulo nascosto che pesa la pagina. A questo scopo, è necessario creare una classe che deriva dalla classe e esegue l'override Page dei SavePageStateToPersistenceMedium() metodi e LoadPageStateFromPersistenceMedium() .

Nota Esiste un prodotto di terze parti denominato Flesk.ViewStateOptimizer che riduce il bloat dello stato di visualizzazione usando una tecnica simile.

Lo stato di visualizzazione viene serializzato e deserializzato dalla System.Web.UI.LosFormatter classe, ovvero los per la serializzazione limitata degli oggetti, ed è progettato per serializzare in modo efficiente determinati tipi di oggetti in una stringa codificata base-64. L'oggetto LosFormatterBinaryFormatter può serializzare qualsiasi tipo di oggetto che può essere serializzato dalla classe, ma viene compilato per serializzare in modo efficiente gli oggetti dei tipi seguenti:

  • Stringhe
  • Integer
  • valori booleani
  • Matrici
  • ArrayLists
  • Hashtables
  • Pairs
  • Triplets

Nota L'oggetto PairSystem.Web.UI e Triplet sono due classi presenti nello spazio dei nomi e forniscono una singola classe per archiviare due o tre oggetti. La Pair classe ha proprietà First e Second per accedere ai due elementi, mentre Triplet dispone Firstdi , Seconde Third come proprietà.

Il SavePageStateToPersistenceMedium() metodo viene chiamato dalla Page classe e passato nello stato di visualizzazione combinato della gerarchia di controllo della pagina. Quando si esegue l'override di questo metodo, è necessario usare per serializzare lo LosFormatter() stato di visualizzazione in una stringa codificata base-64 e quindi archiviare questa stringa in un file nel file system del server Web. Esistono due sfide principali con questo approccio:

  1. In arrivo con uno schema di denominazione di file accettabile. Poiché lo stato di visualizzazione per una pagina probabilmente varia in base alle interazioni dell'utente con la pagina, lo stato di visualizzazione archiviato deve essere univoco per ogni utente e per ogni pagina.
  2. Rimozione dei file di stato di visualizzazione dal file system quando non sono più necessari.

Per affrontare la prima sfida, verrà assegnare un nome al file di stato di visualizzazione persistente in base all'URL dell'utente SessionID e all'URL della pagina. Questo approccio funzionerà perfettamente per tutti gli utenti i cui browser accettano cookie a livello di sessione. Coloro che non accettano i cookie, tuttavia, avranno un ID sessione univoco generato per loro in ogni visita di pagina, rendendo quindi questa tecnica di denominazione non utilizzabile per loro. Per questo articolo sto solo per illustrare l'uso dello SessionID schema di nome file/URL, anche se non funzionerà per coloro i cui browser sono configurati non per accettare i cookie. Inoltre, non funzionerà per una Web farm a meno che tutti i server archiviino i file di stato di visualizzazione in una posizione centralizzata.

Nota Una soluzione alternativa consiste nell'usare un identificatore univoco globale (GUID) come nome file per lo stato di visualizzazione persistente, salvando questo GUID in un campo modulo nascosto nella pagina Web ASP.NET. Questo approccio, purtroppo, richiederebbe molto più sforzo rispetto all'uso dello schema /URL, poiché comporta l'inserimento SessionID di un campo modulo nascosto nel Modulo Web. Per questo motivo, mi basterà illustrare l'approccio più semplice per questo articolo.

La seconda sfida si verifica perché, ogni volta che un utente visita una pagina diversa, verrà creato un nuovo file che contiene lo stato di visualizzazione della pagina. Nel tempo questo comporta migliaia di file. Per pulire periodicamente i file di stato di visualizzazione precedenti a una determinata data, è necessario un'attività automatizzata. Lascio questo come esercizio per il lettore.

Per visualizzare in modo permanente le informazioni sullo stato in un file, si inizia creando una classe che deriva dalla Page classe . Questa classe derivata, quindi, deve eseguire l'override dei SavePageStateToPersistenceMedium() metodi e LoadPageStateFromPersistenceMedium() . Il codice seguente presenta una classe di questo tipo:

public class PersistViewStateToFileSystem : Page
{
   protected override void 
     SavePageStateToPersistenceMedium(object viewState)
   {
      // serialize the view state into a base-64 encoded string
      LosFormatter los = new LosFormatter();
      StringWriter writer = new StringWriter();
      los.Serialize(writer, viewState);
      // save the string to disk
      StreamWriter sw = File.CreateText(ViewStateFilePath);
      sw.Write(writer.ToString());
      sw.Close();
   }
   protected override object LoadPageStateFromPersistenceMedium()
   {
      // determine the file to access
      if (!File.Exists(ViewStateFilePath))
         return null;
      else
      {
         // open the file
         StreamReader sr = File.OpenText(ViewStateFilePath);
         string viewStateString = sr.ReadToEnd();
         sr.Close();
         // deserialize the string
         LosFormatter los = new LosFormatter();
         return los.Deserialize(viewStateString);
      }
   }
   public string ViewStateFilePath
   {
      get
      {
         string folderName = 
           Path.Combine(Request.PhysicalApplicationPath, 
           "PersistedViewState");
         string fileName = Session.SessionID + "-" + 
           Path.GetFileNameWithoutExtension(Request.Path).Replace("/", 
           "-") + ".vs";
         return Path.Combine(folderName, fileName);
      }
   }
}

La classe contiene una proprietà ViewStateFilePathpubblica , che restituisce il percorso fisico del file in cui verranno archiviate le informazioni sullo stato di visualizzazione specifiche. Questo percorso di file dipende dall'url dell'utente SessionID e dall'URL della pagina richiesta.

Si noti che il SavePageStateToPersistenceMedium() metodo accetta un object parametro di input. Si tratta object dell'oggetto stato di visualizzazione creato dalla fase di salvataggio dello stato della visualizzazione. Il processo di SavePageStateToPersistenceMedium() consiste nel serializzare questo oggetto e renderlo permanente in qualche modo. Il codice del metodo crea semplicemente un'istanza dell'oggetto LosFormatter e richiama Serialize() il relativo metodo, serializzando le informazioni sullo stato di visualizzazione passate all'oggetto StringWriter writer. In seguito, il file specificato viene creato (o sovrascritto, se esiste già) con il contenuto della stringa di stato di visualizzazione con codifica base 64.

Il LoadPageStateFromPersistenceMedium() metodo viene chiamato all'inizio della fase di visualizzazione del carico. Il processo consiste nel recuperare lo stato di visualizzazione persistente e deserializzare nuovamente in un oggetto che può essere propagato nella gerarchia di controllo della pagina. Questa operazione viene eseguita aprendo lo stesso file in cui lo stato di visualizzazione persistente è stato archiviato nell'ultima visita e restituendo la versione deserializzata tramite il Deserialize() metodo in LosFormatter() .

Anche in questo caso, questo approccio non funzionerà con gli utenti che non accettano cookie, ma per quelli che fanno, lo stato di visualizzazione viene mantenuto interamente nel file system del server Web, aggiungendo così 0 byte alla dimensione complessiva della pagina!

Nota Un altro approccio per ridurre il bloat imposto dallo stato di visualizzazione consiste nel comprimere il flusso dello stato di visualizzazione serializzato nel SavePageStateToPersistenceMedium() metodo e quindi decomprimerlo nel formato originale nel LoadPageStateFromPersistenceMedium() metodo. Scott Galloway ha una voce di blog in cui illustra le sue esperienze con l'uso di #ziplib libreria per comprimere lo stato di visualizzazione.

Analisi dello stato di visualizzazione

Quando viene eseguito il rendering di una pagina, serializza lo stato di visualizzazione in una stringa codificata base-64 usando la LosFormatter classe e (per impostazione predefinita) lo archivia in un campo modulo nascosto. Al postback, il campo modulo nascosto viene recuperato e deserializzato nella rappresentazione dell'oggetto dello stato di visualizzazione, che viene quindi usato per ripristinare lo stato dei controlli nella gerarchia di controllo. Un dettaglio trascurato fino a questo punto nell'articolo è quello che, esattamente, è la struttura dell'oggetto stato di Page visualizzazione della classe?

Come illustrato in precedenza, l'intero stato di visualizzazione dell'oggetto Page è la somma dello stato di visualizzazione dei controlli nella gerarchia di controllo. Mettere in un altro modo, in qualsiasi punto della gerarchia di controllo, lo stato di visualizzazione di tale controllo rappresenta lo stato di visualizzazione di tale controllo insieme allo stato di visualizzazione di tutti i controlli figlio. Poiché la Page classe forma la radice della gerarchia di controllo, lo stato di visualizzazione rappresenta lo stato di visualizzazione per l'intera gerarchia di controllo.

La Page classe contiene un SavePageViewState()oggetto , richiamato durante la fase di salvataggio del ciclo di vita della pagina. Il SavePageViewState() metodo inizia creando un Triplet contenente i tre elementi seguenti:

  1. Codice hash della pagina. Questo codice hash viene usato per assicurarsi che lo stato di visualizzazione non sia stato manomesso tra postback. Verranno illustrate altre informazioni sull'hashing dello stato di visualizzazione nella sezione "Visualizza implicazioni sullo stato e sulla sicurezza".
  2. Stato di visualizzazione collettiva della gerarchia di controllo del gruppo Page.
  3. Oggetto ArrayList di controlli nella gerarchia di controllo che devono essere richiamati in modo esplicito dalla classe di pagina durante la fase di evento postback di generazione del ciclo di vita.

Gli elementi e nell'oggetto Triplet sono relativamente semplici. L'elemento FirstSecond è il punto in cui viene mantenuto lo stato di visualizzazione per la Pagegerarchia di controllo .Third L'elemento Second viene generato dalla pagina chiamando il SaveViewStateRecursive() metodo, definito nella System.Web.UI.Control classe . SaveViewStateRecursive() salva lo stato di visualizzazione del controllo e i relativi discendenti restituendo un Triplet con le informazioni seguenti:

  1. Stato presente nell'oggetto Control.ViewStateStageBag
  2. Oggetto ArrayList di interi. In questo ArrayList modo vengono mantenuto gli indici dei Controlcontrolli figlio che hanno uno stato nonnull di visualizzazione.
  3. Uno ArrayList degli stati di visualizzazione per i controlli figlio. Lo stato di visualizzazione iin questo ArrayList oggetto viene mappato all'indice del controllo figlio nell'elemento inell'elemento ArrayListTripletdell'elemento .Second

La Control classe calcola lo stato di visualizzazione, restituendo un Tripletoggetto . L'elemento Second dell'oggetto contiene lo stato di Triplet visualizzazione dei Controldiscendenti di . Il risultato finale è che lo stato di visualizzazione è costituito da molti ArrayListTriplets all'interno di Triplets, all'interno di s, all'interno diTriplet... (Il contenuto preciso nello stato di visualizzazione dipende dai controlli nella gerarchia. I controlli più complessi potrebbero serializzare il proprio stato allo stato di visualizzazione usando Pairs o object matrici. Come si vedrà brevemente, lo stato di visualizzazione è composto da un numero di Triplets e ArrayListannidato come la gerarchia di controllo.

Passaggio a livello di codice dello stato di visualizzazione

Con un po' di lavoro è possibile creare una classe che può analizzare lo stato di visualizzazione e visualizzarne il contenuto. Il download per questo articolo include una classe denominata ViewStateParser che fornisce tali funzionalità. Questa classe contiene un ParseViewState() metodo che esegue in modo ricorsivo lo stato di visualizzazione. Richiede tre input:

  1. Oggetto stato di visualizzazione corrente.
  2. Quanti livelli profondi siamo nella ricorsione dello stato di visualizzazione.
  3. Etichetta di testo da visualizzare.

Gli ultimi due parametri di input sono solo a scopo di visualizzazione. Il codice di questo metodo, illustrato di seguito, determina il tipo dell'oggetto stato di visualizzazione corrente e visualizza il contenuto dello stato di visualizzazione di conseguenza, chiamando in modo ricorsivo ognuno dei membri dell'oggetto corrente. La variabile tw è un'istanza TextWriter a cui viene scritto l'output.

protected virtual void ParseViewStateGraph(
  object node, int depth, string label)
{
   tw.Write(System.Environment.NewLine);
   if (node == null)
   {
      tw.Write(String.Concat(Indent(depth), label, "NODE IS NULL"));
   } 
   else if (node is Triplet)
   {
      tw.Write(String.Concat(Indent(depth), label, "TRIPLET"));
      ParseViewStateGraph(
        ((Triplet) node).First, depth+1, "First: ");
      ParseViewStateGraph(
        ((Triplet) node).Second, depth+1, "Second: ");
      ParseViewStateGraph(
        ((Triplet) node).Third, depth+1, "Third: ");
   }
   else if (node is Pair)
   {
      tw.Write(String.Concat(Indent(depth), label, "PAIR"));
      ParseViewStateGraph(((Pair) node).First, depth+1, "First: ");
      ParseViewStateGraph(((Pair) node).Second, depth+1, "Second: ");
   }
   else if (node is ArrayList)
   {
      tw.Write(String.Concat(Indent(depth), label, "ARRAYLIST"));
      // display array values
      for (int i = 0; i < ((ArrayList) node).Count; i++)
         ParseViewStateGraph(
           ((ArrayList) node)[i], depth+1, String.Format("({0}) ", i));
   }
   else if (node.GetType().IsArray)
   {
      tw.Write(String.Concat(Indent(depth), label, "ARRAY "));
      tw.Write(String.Concat("(", node.GetType().ToString(), ")"));
IEnumerator e = ((Array) node).GetEnumerator();
      int count = 0;
      while (e.MoveNext())
         ParseViewStateGraph(
           e.Current, depth+1, String.Format("({0}) ", count++));
   }
   else if (node.GetType().IsPrimitive || node is string)
   {
      tw.Write(String.Concat(Indent(depth), label));
      tw.Write(node.ToString() + " (" + 
        node.GetType().ToString() + ")");
   }
   else
   {
      tw.Write(String.Concat(Indent(depth), label, "OTHER - "));
      tw.Write(node.GetType().ToString());
   }
}

Come illustrato dal codice, il ParseViewState() metodo esegue l'iterazione dei tipi previsti, ovveroTriplet , ArrayListPairmatrici e tipi primitivi. Per i valori scalari: numeri interi, stringhe e così via, il tipo e il valore vengono visualizzati; per i tipi di aggregazione: matrici, coppie, Triplets e così via, i membri che compongono il tipo vengono visualizzati richiamando in ParseViewState()modo ricorsivo .

La ViewStateParser classe può essere usata da una pagina Web ASP.NET (vedere la ParseViewState.aspx demo) o può essere accessibile direttamente dal SavePageStateToPersistenceMedium() metodo in una classe derivata dalla Page classe (vedere la ShowViewState classe). Le figure 8 e9 mostrano la ParseViewState.aspx demo in azione. Come illustrato nella figura 8, l'utente viene presentato con una casella di testo a più righe in cui è possibile incollare il campo modulo nascosto __VIEWSTATE da una pagina Web. La figura 9 mostra un frammento dello stato di visualizzazione analizzato per una pagina che visualizza le informazioni sul file system in un DataGrid.

ms972976.viewstate_fig08(en-us,MSDN.10).gifms972976.viewstate_fig08(en-us,MSDN.10)

Figura 8. Decodifica di ViewState

ms972976.viewstate_fig09(en-us,MSDN.10).gifms972976.viewstate_fig09

Figura 9. ViewState decodificato

Oltre al parser dello stato di visualizzazione fornito nel download di questo articolo, Paul Wilson fornisce un parser dello stato di visualizzazione nel suo sito Web. Fritz Onion ha anche un'applicazione WinForms per il decodificatore dello stato di visualizzazione disponibile per il download dalla sezione Risorse nel suo sito Web.

Visualizzare le implicazioni sullo stato e sulla sicurezza

Lo stato di visualizzazione per una pagina Web ASP.NET viene archiviato, per impostazione predefinita, come stringa con codifica base-64. Come illustrato nella sezione precedente, questa stringa può essere facilmente decodificata e analizzata, visualizzando il contenuto dello stato di visualizzazione per tutti da visualizzare. Ciò genera due problemi relativi alla sicurezza:

  1. Poiché lo stato di visualizzazione può essere analizzato, cosa impedisce a qualcuno di modificare i valori, di ri serializzarlo e di usare lo stato di visualizzazione modificato?
  2. Poiché lo stato di visualizzazione può essere analizzato, significa che non è possibile inserire informazioni sensibili nello stato di visualizzazione ,ad esempio password, stringhe di connessione e così via?

Fortunatamente, la LosFormatter classe ha funzionalità per risolvere entrambi questi problemi, come vedremo nelle due sezioni successive. Prima di esaminare le soluzioni per questi problemi, è importante tenere presente che lo stato di visualizzazione deve essere usato solo per archiviare dati non sensibili. Lo stato di visualizzazione non ospita il codice e non deve essere sicuramente usato per inserire informazioni riservate come stringhe di connessione o password.

Protezione dello stato di visualizzazione dalla modifica

Anche se lo stato di visualizzazione deve archiviare solo lo stato dei controlli Web nella pagina e altri dati non sensibili, gli utenti non sensibili potrebbero causare mal di testa se potrebbero modificare correttamente lo stato di visualizzazione per una pagina. Si supponga, ad esempio, di aver eseguito un sito Web eCommerce che ha usato dataGrid per visualizzare un elenco di prodotti da vendere insieme al costo. A meno che non si imposta la proprietà di DataGrid su False, il contenuto di EnableViewState DataGrid, i nomi e i prezzi della merce, verrà mantenuto nello stato di visualizzazione.

Gli utenti nefario possono analizzare lo stato di visualizzazione, modificare i prezzi in modo da leggere tutti $0,01 e quindi deserializzare lo stato di visualizzazione in una stringa codificata base-64. Potrebbero quindi inviare messaggi di posta elettronica o pubblicare collegamenti che, quando si fa clic, hanno inviato un modulo che ha inviato l'utente alla pagina dell'elenco dei prodotti, passando lo stato di visualizzazione modificato nelle intestazioni HTTP POST. La pagina legge lo stato di visualizzazione e visualizza i dati di DataGrid in base a questo stato di visualizzazione. Risultato finale? Avresti un sacco di clienti pensando che sarebbero stati in grado di acquistare i tuoi prodotti solo per un centesimo!

Un semplice mezzo per proteggere da questo tipo di manomissione consiste nell'usare un controllo di autenticazione del computer o MAC. I controlli di autenticazione del computer sono progettati per garantire che i dati ricevuti da un computer siano gli stessi dati trasmessi, ovvero che non siano stati manomessi. Questo è esattamente ciò che vogliamo fare con lo stato di visualizzazione. Con ASP.NET stato di visualizzazione, l'oggetto LosFormatter esegue un MAC eseguendo l'hashing dei dati dello stato di visualizzazione serializzati e aggiungendo questo hash alla fine dello stato di visualizzazione. Un hash è un digest calcolato rapidamente comunemente usato negli scenari di sicurezza simmetrici per garantire l'integrità dei messaggi. Quando la pagina Web viene pubblicata di nuovo, i LosFormatter controlli consentono di verificare che l'hash aggiunto corrisponda al valore hash dello stato di visualizzazione deserializzato. Se non corrisponde, lo stato di visualizzazione è stato modificato in route.

Per impostazione predefinita, la LosFormatter classe applica il MAC. È tuttavia possibile personalizzare se il MAC si verifica impostando la Page proprietà della EnableViewStateMac classe. Il valore predefinito, True, indica che il MAC deve essere eseguito; un valore di False indica che non deve essere necessario. È possibile personalizzare ulteriormente il MAC specificando quale algoritmo di hashing deve essere usato. machine.config Nel file cercare l'attributo <machineKey> dell'elementovalidation. L'algoritmo di hash predefinito usato è SHA1, ma è possibile modificarlo in MD5 se si desidera. Per altre informazioni su SHA1, vedere RFC 3174; per altre informazioni su MD5, leggere RFC 1321.

Nota Quando si usa Server.Transfer() è possibile che venga visualizzato un problema con l'autenticazione dello stato di visualizzazione. Alcuni articoli online hanno menzionato che l'unica soluzione alternativa consiste nell'impostare EnableViewStateMac su False. Anche se questo risolverà certamente il problema, si apre un buco di sicurezza. Per altre informazioni, inclusa una soluzione alternativa sicura, vedere questo articolo della KNOWLEDGE Base.

Crittografia dello stato di visualizzazione

Idealmente lo stato di visualizzazione non deve essere crittografato, perché non deve mai contenere informazioni sensibili. Se necessario, tuttavia, l'oggetto LosFormatter fornisce un supporto di crittografia limitato. L'unico LosFormatter consente un singolo tipo di crittografia: Triple DES. Per indicare che lo stato di visualizzazione deve essere crittografato, impostare l'attributo <machineKey> dell'elemento validation nel machine.config file su 3DES.

Oltre alla convalida attribute, l'elemento <machineKey> contiene validationKey e decryptionKey attributi, nonché. L'attributo validationKey specifica la chiave utilizzata per MAC. decryptionKey Indica la chiave utilizzata nella crittografia Triple DES. Per impostazione predefinita, questi attributi vengono impostati sul valore "AutoGenerate, IsolateApp", che genera in modo univoco le chiavi per ogni applicazione Web nel server. Questa impostazione funziona bene per un singolo ambiente server Web, ma se si dispone di una Web farm, è fondamentale che tutti i server Web usino le stesse chiavi per MAC e/o crittografia e decrittografia. In questo caso è necessario immettere manualmente una chiave condivisa tra i server nella Web farm. Per altre informazioni su questo processo e l'elemento in generale, vedere la documentazione tecnica di MachineKey> e l'articolo<<machineKey> di Susan Warren Che prende un bit fuori ASP.NET ViewState.

Proprietà ViewStateUserKey

Microsoft® ASP.NET versione 1.1 ha aggiunto una proprietà di classe aggiuntivaPage:ViewStateUserKey Questa proprietà, se usata, deve essere assegnata un valore stringa nella fase di inizializzazione del ciclo di vita della pagina (nel Page_Init gestore eventi). Il punto della proprietà consiste nell'assegnare una chiave specifica dell'utente allo stato di visualizzazione, ad esempio un nome utente. Se ViewStateUserKeyspecificato, viene usato come sale per l'hash durante il MAC.

Ciò che la ViewStateUserKey proprietà protegge da è il caso in cui un utente nefarioso visita una pagina, raccoglie lo stato di visualizzazione e quindi consente a un utente di visitare la stessa pagina, passando il relativo stato di visualizzazione (vedere Figura 10). Per altre informazioni su questa proprietà e sulla relativa applicazione, vedere Compilazione di pagine e controlli sicuri ASP.NET.

ms972976.viewstate_fig10(en-us,MSDN.10).gifms972976.viewstate_fig10(en-us,MSDN.10)

Figura 10. Protezione dagli attacchi tramite ViewStateUserKey

Conclusione

In questo articolo è stato esaminato lo stato di visualizzazione ASP.NET, studiando non solo il suo scopo, ma anche la sua funzionalità. Per comprendere al meglio il funzionamento dello stato di visualizzazione, è importante avere un'importante comprensione sul ciclo di vita della pagina ASP.NET, che include fasi per il caricamento e il salvataggio dello stato di visualizzazione. Nelle discussioni sul ciclo di vita della pagina si è visto che alcune fasi, ad esempio il caricamento dei dati di postback e la generazione di eventi postback, non erano in alcun modo correlati allo stato di visualizzazione.

Mentre lo stato di visualizzazione consente di mantenere senza sforzo lo stato tra postback, si tratta di un costo e questo costo è bloat di pagina. Poiché i dati dello stato di visualizzazione vengono mantenuti in un campo modulo nascosto, lo stato di visualizzazione può aggiungere facilmente decine di kilobyte di dati a una pagina Web, aumentando così sia i tempi di download che di caricamento per le pagine Web. Per ridurre il peso della pagina imposto dallo stato di visualizzazione, è possibile indicare in modo selettivo a vari controlli Web di non registrare lo stato di visualizzazione impostando la EnableViewState proprietà su False. Lo stato di visualizzazione può infatti essere disattivato per un'intera pagina impostando la EnableViewState proprietà su false nella @Page direttiva. Oltre a disattivare lo stato di visualizzazione a livello di pagina o di controllo, è anche possibile specificare un archivio di backup alternativo per lo stato di visualizzazione, ad esempio il file system del server Web.

Questo articolo è stato completato con un'analisi dei problemi di sicurezza con lo stato di visualizzazione. Per impostazione predefinita, lo stato di visualizzazione esegue un MAC per assicurarsi che lo stato di visualizzazione non sia stato manomesso tra i postback. ASP.NET 1.1 fornisce la ViewStateUserKey proprietà per aggiungere un livello di sicurezza aggiuntivo. I dati dello stato di visualizzazione possono essere crittografati anche usando l'algoritmo di crittografia Triple DES.

Programmazione felice!

Lavori consultati

Esistono numerose risorse valide per altre informazioni sullo stato di visualizzazione ASP.NET. Paul Wilson ha fornito una serie di risorse, ad esempio View State: All You Wanted to Know e the Page View State Parser. Dino Esposito ha creato un articolo per MSDN Magazine nel febbraio 2003, intitolato The ASP.NET View State, che illustra una tecnica per archiviare lo stato di visualizzazione nel file system del server Web. L'uso di un Bite Out of ASP.NET View State, scritto da Susan Warren, offre una panoramica generale dello stato di visualizzazione, inclusa una discussione sulla crittografia dello stato di visualizzazione. Il blog di Scott Galloway ha anche alcuni buoni post sull'uso dello stato di visualizzazione ASP.NET.

Grazie speciali a...

Prima di inviare il mio articolo all'editor MSDN, ho una manciata di volontari che aiutano a leggere l'articolo e fornire commenti e suggerimenti sul contenuto, la grammatica e la direzione dell'articolo. I collaboratori principali al processo di revisione per questo articolo includono James Avery, Bernard Vander Beken, Dave Donaldson, Scott Elkin e Justin Lovell. Se sei interessato a partecipare all'elenco in continua crescita dei revisori, lasciami una riga in mitchell@4guysfromrolla.com.

Informazioni sull'autore

Scott Mitchell, autore di cinque libri e fondatore di 4GuysFromRolla.com, ha lavorato con le tecnologie Web Microsoft negli ultimi cinque anni. Scott lavora come consulente indipendente, allenatore e scrittore. Può essere raggiunto all'indirizzo mitchell@4guysfromrolla.com o tramite il suo blog, che può essere trovato in http://ScottOnWriting.NET.

© Microsoft Corporation. Tutti i diritti sono riservati.