Il modello di programmazione ASP.NET

Capitolo 1
Il modello di
programmazione ASP.NET
In questo capitolo:
Cos’è ASP.NET, in definitiva?............................................................................ 4
Il modello di componente ASP.NET.................................................................... 15
Lo stack di sviluppo ASP.NET........................................................................... 20
Il modello di provider ASP.NET......................................................................... 27
Conclusioni ................................................................................................ 34
ASP.NET è una piattaforma di sviluppo Web che fornisce servizi, un modello di programmazione, e l’infrastruttura software necessaria a realizzare applicazioni di livello enterprise.
Benché la sintassi sia in gran parte compatibile con il popolare predecessore, Active Server
Pages (ASP), ASP.NET è un nuovo rivoluzionario framework di programmazione progettato
per consentire uno sviluppo rapido di applicazioni Web. Essendo parte della piattaforma
Microsoft .NET, ASP.NET fornisce un approccio basato su componenti, estensibile e facile
da utilizzare per l’implementazione, il deployment e l’esecuzione di applicazioni Web destinate a qualsiasi browser o dispositivo mobile. ASP.NET rappresenta l’apice delle tecnologie
di sviluppo Web che si sono rapidamente susseguite negli ultimi dieci anni, ciascuna basata
sulla precedente tecnologia, e ciascuna a colmare le lacune del proprio predecessore.
Di conseguenza, ASP.NET è al momento la piattaforma più tecnologicamente avanzata,
sofisticata e potente per realizzare applicazioni distribuite trasportate dal protocollo HTTP.
Nonostante la crescita sorprendentemente in diffusione e l’essere impiegata proficuamente
in migliaia di progetti reali, ASP.NET 1.1 è solo il primo passo di una strada chiaramente più
lunga. Più si lavora con ASP.NET, più ci si rende conto che è necessario ancora altro.
ASP.NET semplifica diversi task e rappresenta una sorta di paradiso della programmazione, in particolare per gli sviluppatori che provengono da ASP classico, dalla programmazione Internet Server Application Programming Interface (ISAPI) o da altre piattaforme
Web. ASP.NET 1.1 ha semplicemente stimolato l’appetito della comunità degli sviluppatori.
Così, dopo i primi mesi di lavoro e valutazione con ASP.NET, i membri di questa community
hanno iniziato a chiedere e a desiderare altre funzionalità: in effetti, molte di più. ASP.NET
2.0 rappresenta un principale upgrade alla piattaforma, anche se non introduce alcun paradigma di programmazione nuovo o rivoluzionario. A prima vista, non vi è alcun approccio
radicalmente nuovo alla progettazione e all’implementazione del codice, e non vi è alcun
nuovo modello di sintassi con cui acquisire familiarità.
4
Parte I Realizzare una pagina ASP .NET
Ciò nondimeno, ASP.NET 2.0 è una pietra miliare della roadmap Microsoft sullo
sviluppo Web, sia per gli architetti delle applicazioni sia per gli sviluppatori. Molte
delle classi costituenti sono state modificate, e alcune hanno subito degli interventi di
lifting estetico. Sono stati aggiunti diversi nuovi controlli per migliorare la produttività,
e alcuni moduli di sistema, nuovi o migliorati, rendono ora la pipeline di runtime più
personalizzabile, flessibile, robusta e sicura. Di conseguenza, nuove pratiche emergono
come “best practice”, sono disponibili nuove tecniche di programmazione per architetti e
sviluppatori, e nuove caratteristiche di sistema forniscono soluzioni native a noti problemi
con le precedenti versioni. Per massimizzare i vantaggi nell’utilizzo di ASP.NET, si deve
prima osservare il modello complessivo: componenti, programmabilità e infrastruttura.
Un’occhiata da vicino al modello complessivo è esattamente ciò che questo capitolo
fornisce. Per iniziare, esaminiamo alcuni concetti essenziali della piattaforma ASP.NET
e del relativo modello di programmazione.
Cos’è ASP.NET, in definitiva?
Prima dell’avvento di ASP.NET, erano disponibili tre principali tecnologie e
piattaforme per sviluppare applicazioni Web: ASP, Java Server Pages (JSP) e la
piattaforma Web opensource comunemente riferita come LAMP (Linux più Apache più
MySQL più Perl, Python o PHP come linguaggio di programmazione).
[Nota] Per completezza, vanno anche menzionate un paio di tecnologie di livello più basso,
specifiche delle piattaforme, su cui si basano ASP e JSP. ASP è in effetti una estensione
ISAPI, mentre JSP viene implementata come una speciale applicazione servlet. Le estensioni
ISAPI su piattaforme basate su IIS e le servlet su sistemi basati su Java, permettono di creare
applicazioni lato server, con un deployment Web, utilizzando un approccio più classico. Si
scrive un modulo che costruisce e renderizza la pagina piuttosto che progettare la pagina in
modo dichiarativo utilizzando un mix di testo di markup e di codice embedded.
Benché ciascuna abbia caratteristiche specifiche al linguaggio e all’architettura, tutte
queste piattaforme di sviluppo Web sono progettate per creare pagine interattive che costituiscono una parte di una applicazione Web. Entro certi limiti, tutte permettono agli sviluppatori di separare la logica di programmazione dal layout di pagina attraverso l’utilizzo di
componenti che la pagina stessa è responsabile di invocare e renderizzare. A parte questo
comune obiettivo finale, esistono importanti differenze tra queste piattaforme, gran parte
delle quali sono relative al modello di programmazione e ai linguaggi che promuovono e
supportano. Ad esempio, JSP sfrutta il framework Java di classi e, con JavaBeans, fornisce
un efficace modello di estensibilità per il riutilizzo dei componenti. Inoltre, JSP supporta la
personalizzazione dei tag e permette agli sviluppatori di associare del codice a una definizione di tag custom. Infine, essendo un elemento chiave della piattaforma Java 2 Enterprise
Edition (J2EE), JSP si basa sul linguaggio Java, un vero linguaggio compilato, al contrario
dei linguaggi di scripting utilizzati da entrambe le piattaforme ASP e LAMP. Pertanto come
si posiziona esattamente ASP.NET?
Capitolo 1 Il modello di programmazione ASP .NET
Analogamente a ASP e ad altri ambienti di sviluppo Web, anche ASP.NET utilizza il protocollo HTTP e sfrutta i comandi HTTP e le policy per impostare una comunicazione e una
cooperazione bidirezionale tra browser e server. Ciò che realmente distingue ASP.NET dalla
pletora delle altre tecnologie di sviluppo Web è il modello di programmazione astratto che
propone, il modello Web Forms. Inoltre, l’intera piattaforma ASP.NET è parte nativa del
Microsoft .NET Framework. Per essere certi di afferrare l’importanza di questo ultimo punto, permettetemi di chiarirlo. Le applicazioni ASP.NET sono blocchi di codice compilato,
sono costituite da componenti riusabili e estensibili, possono essere create con linguaggi
di tutto rispetto (tra cui C#, Microsoft Visual Basic .NET, Microsoft JScript .NET, e J#), e
possono accedere all’intera gerarchia di classi del .NET Framework.
In sintesi, ASP.NET riunisce il meglio dei diversi approcci. È semanticamente compatibile (e vi è, entro certi limiti, anche una compatibilità di linguaggio) con ASP. Fornisce le stesse caratteristiche object-oriented delle applicazioni JSP (personalizzazione dei tag, linguaggi
compilati, componenti, estensibilità e riusabilità). E come ciliegina sulla torta, ASP.NET offre una notevole scelta di squisitezze, tool e potenti caratteristiche di sistema che possono
essere effettivamente riassunte con l’espressione generica tool per astrarre il modello di
programmazione HTTP. Una gran quantità di classi di facile utilizzo per i programmatori
permettono di sviluppare pagine utilizzando i tipici metodi da applicazione desktop. Il modello Web Forms promuove un approccio complessivo a eventi, ma è distribuito sul Web.
[Nota]ASP.NET è supportato su una varietà di piattaforme, compreso Microsoft Windows
2000 con almeno il Service Pack 2, Windows XP Professional e Windows Server 2003. Per
sviluppare applicazioni server ASP.NET, è anche necessario Internet Information Services
(IIS) versione 5.0 o successiva. Altro software necessario, ad esempio Microsoft Data
Access Components (MDAC) 2.7, viene automaticamente installato quando si installa il
.NET Framework. In termini di prestazioni, robustezza e sicurezza, la combinazione ideale
di software di sistema per ospitare applicazioni ASP.NET sembra essere Windows Server
2003 (preferibilmente con Service Pack 1) e IIS 6.0.
La programmazione nell’era delle Web Forms
La motivazione alla base del modello ASP.NET Web Forms è direttamente connessa
alla ricerca di una migliore strategia per gestire la crescente richiesta di una interazione
Web economica ma potente. Difatti, il protocollo HTTP rappresenta la maggiore forza
e debolezza delle applicazioni Web. La natura stateless del protocollo HTTP introduce
concetti di programmazione del tutto differenti, estranei a molti sviluppatori desktop:
innanzitutto, tra questi concetti, la gestione dello stato di sessione. D’altro canto,
l’intrinseca semplicità e scalabilità di HTTP è la chiave della sua adozione universale e
della sua efficacia: in sintesi, probabilmente non potremmo avere Internet per come la
conosciamo senza un protocollo come HTTP. Eppure, man mano che aumenta la richiesta
di applicazioni sofisticate e potenti, i programmatori devono escogitare dei modi migliori
per stabilire una facile ed efficace comunicazione dal client al server e viceversa.
5
6
Parte I Realizzare una pagina ASP .NET
Col tempo, sono state sperimentate varie tecniche per agevolare la comunicazione tra
pagine differenti e tra più invocazioni alla stessa pagina. Gran parte dei programmatori
sono abituati a pensare in termini di azione generata dal client che produce una
reazione lato server. Questo pattern essenziale e fondamentale non può essere seguito
sul Web, perlomeno non alla lettera. È necessario un certo grado di astrazione e alcuni
servizi di sistema perché si abbia una comunicazione agevole.
ASP, molto più di JSP, pensa in modo dichiarativo ed ha un modello a oggetti
abbastanza esiguo e inadeguato. In generale, i programmatori che sono diventati
programmatori Web sono costretti ad adottare una mentalità differente e a scaraventare
dalla porta il comune paradigma di azione/reazione.
Programmazione a eventi su HTTP
I Web Forms ASP.NET estendono al Web il modello di interazione a eventi.
L’implementazione di un modello a eventi sul Web richiede che qualsiasi dato relativo all’attività dell’utente lato client sia inoltrato al server per una elaborazione corrispondente e
stateful. Il server processa l’output delle azioni del client e scatena delle reazioni. Lo stato
dell’applicazione contiene due tipi di informazioni: lo stato del client e lo stato della sessione. Lo stato del client, per lo più il contenuto dei campi di input del form collegialmente riferiti come stato della pagina, è facilmente accessibile attraverso le collection lato server che
memorizzano i valori postati. E per quanto riguarda lo stato complessivo della sessione? Il
client si aspetta che l’invio di informazioni al server attraverso una pagina sia naturalmente
correlato a qualsiasi altra pagina che può visualizzare in seguito, così come avviene quando
si aggiungono articoli a un carrello. Chi ricorda cosa un particolare utente ha nel carrello?
Di per sé, HTTP non è in grado di tener di traccia di queste informazioni; ed è qui che entrano in gioco lo stato della sessione e una opportuna infrastruttura lato server che circonda e
integra HTTP. Non è mai abbastanza enfatizzare l’importanza della comprensione dei concetti coinvolti nella programmazione stateless nello sviluppo di applicazioni Web. Come
già detto, HTTP è un protocollo stateless, il che significa che due richieste successive durante la stessa sessione non hanno alcuna conoscenza reciproca. Vengono risolte da ambienti
appena istanziati in cui non viene mantenuta alcuna informazione specifica di sessione, ad
eccezione delle informazioni che l’applicazione stessa può aver memorizzato negli oggetti
globali. In ASP, i form rientranti rappresentavano un modo comune per aggirare questa
limitazione di sistema. Un form rientrante è un elemento HTML <form> che esegue il post
alla stessa pagina che lo contiene. I form rientranti di per sé non risolvono pienamente il
problema. Tuttavia, combinandoli con blocchi di codice e campi nascosti che memorizzano
le informazioni di stato critiche per la pagina, molti sviluppatori superano elegantemente
l’ostacolo. Ciò che rappresentava una best-practice ASP è stata standardizzata e integrata
nel runtime ASP.NET per diventare la caratteristica principale che dota le applicazioni
ASP.NET di una manutenzione automatica dello stato. Il runtime ASP.NET trasporta lo stato
di pagina avanti e indietro unitamente alle richieste di pagina. Nel generare codice HTML
per una determinata pagina, ASP.NET codifica e inserisce lo stato degli oggetti lato server
in alcuni campi nascosti, creati in modo del tutto trasparente. Quando viene richiesta la
pagina, lo stesso motore del runtime ASP.NET ricerca le informazioni di stato embedded,
ossia i campi nascosti, e utilizza qualsiasi informazione decodificata per impostare le istanze
Capitolo 1 Il modello di programmazione ASP .NET
appena create degli oggetti lato server. L’effetto finale di tale meccanismo non è diverso dal
modello Windows Forms sul desktop ed è riassunto nella Figura 1-1.
Figura 1-1 Confronto tra i modelli Windows Forms e Web Forms del .NET Framework.
Il modello Windows Forms deriva dal tipico stile di programmazione a eventi desktop.
Non importa che connettività esista tra i componenti client e server, il server opera sempre
in reazione all’input del client. Il server è consapevole dello stato complessivo dell’applicazione e opera in una modalità two-tier, connessa. Il modello Web Forms necessita di
qualche meccanismo per supportare lo stesso modello di programmazione a eventi. In
Figura 1-1, il meccanismo necessario viene rappresentato dalla deserializzazione dello stato
che si verifica quando viene richiesta la pagina, e dalla serializzazione dello stato eseguita
quando sta per essere generata la risposta HTML. A farsi carico di questo lavoro di filtraggio
è il runtime HTTP di ASP.NET, un blocco di codice che estende e specializza le funzionalità
complessive del Web server ospite. I form rientranti e i campi nascosti rappresentano gli
strumenti di basso livello utilizzati per eseguire il trucco. Un tale modello non sarebbe tanto
efficace senza un sofisticato modello a oggetti di backend, che abbraccia l’intero contenuto
della pagina server. Cruciale alla realizzazione e al funzionamento efficace della piattaforma
di sviluppo ASP.NET è il modello a componenti. Il modello a componenti ASP.NET identifica e descrive i blocchi fondamentali delle pagine ASP.NET. Viene implementato attraverso
un modello a oggetti che fornisce un equivalente lato server a, virtualmente, qualsiasi elemento della pagina HTML, tra cui i tag HTML come <form> e <input>. Inoltre, il modello a oggetti ASP.NET comprende numerosi componenti (detti controlli server o controlli
Web) che rappresentano elementi più complessi dell’interfaccia utente (UI). Alcuni di questi controlli non hanno alcuna mappatura diretta con singoli elementi HTML ma vengono
implementati combinando più tag HTML. Esempi tipici di complessi elementi UI sono il
controllo Calendar e il controllo DataGrid.
7
8
Parte I Realizzare una pagina ASP .NET
Alla fine, una pagina ASP.NET è costituita da un certo numero di controlli server
mescolati con testo letterale, markup e immagini. I dati sensibili estratti dalla pagina e
lo stato dei controlli vengono memorizzati in modo non intrusivo in campi nascosti,
e formano il contesto di questa richiesta di pagina. L’associazione tra una istanza
della pagina e il relativo stato è inequivocabile, non modificabile da programma, ed è
controllato dal runtime HTTP ASP.NET. Il modello a componenti ASP.NET è la prima
sosta verso una completa comprensione della piattaforma ASP.NET. Il modello a
componenti vi scorta attraverso l’intero ciclo di sviluppo, compresa la fase di creazione
della pagina e la configurazione di sistema a runtime, come mostrato nella Figura 1-2.
Figura 1-2 Una vista dello stack di sviluppo ASP.NET. La freccia indica la tipica prospettiva
top-down dell’applicazione, che va dall’interfaccia utente ai servizi di sistema.
Prima di addentrarci nei vari elementi mostrati nella Figura 1-2, esaminiamo brevemente i fondamenti del protocollo HTTP, che rimane la base dell’interazione Web.
Dopodiché, passeremo a descrivere la struttura di una pagina ASP.NET e come scrivere
e distribuire le applicazioni ASP.NET.
Il protocollo HTTP
Questo paragrafo fornisce una rapida panoramica su come operano applicazioni
Web. Se si ha già una conoscenza operativa dell’infrastruttura Web, si può saltare
al successivo paragrafo “Struttura di una pagina ASP.NET”. L’acronimo HTTP è
diventato così comune per noi sviluppatori che talvolta non ricordiamo esattamente
Capitolo 1 Il modello di programmazione ASP .NET
cosa significa. Effettivamente, HTTP significa Hypertext Transfer Protocol. HTTP è un
protocollo testuale che definisce come comunicano i browser Web e i server Web. Il
formato dei pacchetti HTTP è descritto completamente nel documento RFC 2068 ed è
disponibile all’indirizzo http://www.w3.org/Protocols/rfc2068/rfc2068.txt. I pacchetti
HTTP viaggiano su una connessione Transmission Control Protocol (TCP) diretta di
default verso la porta 80 dell’indirizzo Internet Protocol (IP) di destinazione.
La richiesta HTTP
Quando si fa puntare il browser a una URL, il browser utilizza il Domain Name
System (DNS) disponibile per tradurre il nome del server fornito nella URL in un
indirizzo IP. Successivamente, il browser apre un socket e si connette alla porta 80 di
questo indirizzo. Il pacchetto con la richiesta di download per http://www.contoso.com/
default.aspx può assumere la seguente semplice forma:
GET /default.aspx HTTP/1.1
Host: www.contoso.com
La prima riga di testo in una richiesta è la start line della richiesta. Deve contenere
il nome del comando HTTP da eseguire (GET in questo caso), la URL della risorsa, più
la versione del protocollo HTTP che si vuole utilizzare.
Una richiesta HTTP può contenere, e di solito contiene, alcuni header. Un header
HTTP è una riga di testo che fornisce ulteriori informazioni sulla richiesta. Nella
richiesta HTTP appena mostrata, la riga che inizia con “Host:” è un header HTTP. Gli
header che si possono trovare in una richiesta HTTP comprendono i seguenti:
• User-Agent Identifica il tipo di browser che ha originato la richiesta
• Connection Chiude una connessione o mantiene attiva una connessione
• If-Modified-Since Fornisce una validazione della cache lato client
GET e POST rappresentano i comandi o verbi HTTP più comunemente utilizzati. Il
verbo GET significa recuperare qualsiasi informazione sia identificata dalla URL richiesta.
Il verbo POST viene utilizzato per richiedere che il server di origine accetti il contenuto
racchiuso nella richiesta e lo processi. Tipicamente, il verbo POST viene utilizzato per
fornire un blocco di dati (ossia, il risultato di un invio di un form) a un processo di
gestione dati.
La risposta HTTP
La risposta del server comprende una status line costituita dalla versione del
protocollo del messaggio e dal cosiddetto “exit code” (che indicano l’esito positivo
o che si è verificato un errore). La status line è seguita da un gruppo di header,
tipicamente il tipo di contenuto e la lunghezza della pagina, e dal contenuto del body.
Una riga vuota separa il contenuto del body dal resto del messaggio, come mostrato
nella seguente risposta:
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Content-Type: text/html
9
10
Parte I Realizzare una pagina ASP .NET
Content-Length: 51
<html><body><h1>ASP.NET is cool!</h1></body></html>
Il codice precedente illustra il semplice output HTML restituito dal Web server.
Richieste e risposte sono stringhe formattate in base allo schema HTTP, e viaggiano
su una connessione TCP. Il codice 200 significa che l’elaborazione della richiesta
ha avuto esito positivo. Il Web server specificato processa la richiesta e restituisce
un contenuto di una certa lunghezza espresso in un determinato tipo Multipurpose
Internet Mail Extensions (MIME), (text/html). I codici HTTP che possono essere
restituiti vengono elencati nella specifica HTTP, disponibile alla suddetta URL.
Inoltre, va notato che la riga vuota tra l’ultimo header e il contenuto della risposta
HTTP non è semplice formattazione: la coppia carriage-return/line-feed è richiesta
ed è una parte precisa dello standard.
Cosa accade dopo, dipende in gran parte dal tipo MIME e dalle funzionalità del
browser locale. Finché il tipo MIME è text/html, il browser visualizza il contenuto come
HTML. Se il tipo MIME è, ad esempio, text/xml, alcuni browser renderizzeranno il
contenuto come semplice testo, mentre altri (ad esempio, Microsoft Internet Explorer
6.0) applicheranno uno stylesheet predefinito.
Realizzare un livello di astrazione lato server
Ogni conversazione tra browser e Web server è costituita da uno scambio di
pacchetti simile a quella che abbiamo appena esaminato. Se la URL richiesta è una
pagina HTML, il server Web tipicamente legge il contenuto del file .html e lo inietta nel
corpo del pacchetto di risposta. Se la URL è una pagina ASP.NET, viene coinvolto uno
speciale modulo IIS. Il modulo è un plug-in IIS ISAPI.
Una estensione ISAPI è una dynamic-link library (DLL) registrata per gestire
una determinata estensione di file. Una estensione ISAPI registrata per gestire file
.aspx viene coinvolta ogni qualvolta giunge una richiesta per questo tipo di risorsa.
L’estensione ISAPI analizza la richiesta e configura l’ambiente lato server che processerà
effettivamente il sorgente della pagina. Quando lo stato della richiesta è stato recuperato
con esito positivo ed è stato completamente ripristinato, alla pagina viene permesso di
passare in esecuzione e di produrre l’output HTML.
Invio di form
Il tag HTML <form> è l’unico elemento autorizzato a trasmettere al server dati lato
client. Quando l’utente clicca su un pulsante di tipo “submit”, per progetto il browser
inserisce il contenuto corrente di tutti i controlli che appartengono al form in una stringa.
La stringa viene quindi passata al server come parte del comando GET o POST.
Il seguente frammento HTML illustra un semplice form che contiene una textbox e un
pulsante submit. Come si può vedere, al form viene associato il comando POST e la URL
default.aspx:
<form method=”post” action=”default.aspx”>
<input type=”text” name=”EmpCode” />
<input type=”submit” value=”Send” />
</form>
Capitolo 1 Il modello di programmazione ASP .NET
La seguente richiesta mostra il comando POST che raggiunge il server Web quando
l’utente clicca il pulsante submit:
POST /default.aspx HTTP/1.1
Host: www.contoso.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 12
EmpCode=1001
Mentre elabora la richiesta di pagina, l’estensione ISAPI scandisce il body della
richiesta ed espone qualsiasi informazione trovata attraverso un modello a oggetti più
agevole per il programmatore. Ad esempio, invece di rimanere una semplice stringa
nome/valore, la variabile EmpCode viene spostata all’interno di una collection a livello
di applicazione: la collection Request.Form. Ciò rappresenta un primo livello di
astrazione implementato sul grezzo modello di programmazione HTTP. Oggetti come
Request, Response e Server formano il contesto HTTP della invocazione e, in quanto tali,
rappresentano l’insieme minimo di oggetti che si trovano in gran parte delle piattaforme
di sviluppo Web, compreso JSP e ASP. In ASP.NET, tuttavia, se ne trovano molti altri.
Struttura di una pagina ASP.NET
Una pagina ASP.NET è un file di testo lato server salvato con estensione .aspx.
La struttura interna della pagina è estremamente modulare e comprende tre sezioni
distinte: direttive di pagina, codice e layout di pagina:
• Direttive di pagina
Le direttive di pagina impostano l’ambiente in cui verrà
eseguita la pagina, specificano come il runtime HTTP dovrà processare la pagina,
e determinano quali assunzioni è sicuro fare sulla pagina. Le direttive permettono
anche di importare i namespace per semplificare la codifica, caricare gli assembly
al momento non presenti nella Global Assembly Cache (GAC), e registrare nuovi
controlli con nomi di tag e prefissi di namespace personalizzati.
• Sezione di codice La sezione di codice contiene gli handler degli eventi di pagina
e dei controlli, più delle routine di supporto opzionali. Qualsiasi codice sorgente
pertinente alla pagina può essere inserito inline o allegato alla pagina attraverso un
file separato. Se inserito inline, il codice viene inserito in un tag con il fuorviante nome
di <script>. (Il nome <script> è stato scelto per motivi di compatibilità all’indietro).
I tag <script> lato server vengono distinti dai tag <script> lato client per mezzo
dell’utilizzo dell’attributo runat=server. (Torneremo su ciò a breve). Qualsiasi codice
di pagina viene sempre compilato prima dell’esecuzione. In ASP.NET 2.0, può anche
essere precompilato e distribuito sotto forma di assembly binario.
• Layout di pagina Il layout di pagina rappresenta lo scheletro della pagina. Comprende
i controlli server, il testo letterale e i tag HTML. L’interfaccia utente dei controlli
server può essere rimpolpata un po’ utilizzando gli attributi dichiarati e le proprietà
dei controlli.
11
12
Parte I Realizzare una pagina ASP .NET
Perché la pagina funzioni, non è necessario specificare tutte le sezioni. Benché le
pagine reali comprendano tutte le sezioni menzionate, pagine perfettamente valide e
funzionali possono comprendere solo la sezione di codice o il layout di pagina. In alcuni
casi speciali, si può anche avere una pagina ASP.NET costituita da una singola direttiva.
Nel Capitolo 2, e ancor più nel Capitolo 3, tratteremo a fondo le caratteristiche di
una pagina e i blocchi costituenti.
Una pagina ASP.NET d’esempio
È il momento di vedere come appare una pagina ASP.NET. Per iniziare, sarà sufficiente un semplice editor di testo; pertanto apriamo Notepad e lasciamo riposare il
gigante addormentato (Microsoft Visual Studio .NET).
Il codice seguente implementa una semplice pagina ASP.NET che permette di inserire una stringa e quindi di trasformarla in maiuscolo dopo aver premuto un pulsante.
Per semplicità, utilizziamo del codice inline. (Come si vedrà in seguito, non è così che
si farà nelle applicazioni reali e in qualsiasi pagina di una certa complessità).
<!-- Direttive -->
<% @Page Language=”C#” %>
<!-- Sezione Codice -->
<script runat=”server”>
private void MakeUpper(object sender, EventArgs e)
{
string buf = TheString.Value;
TheResult.InnerText = buf.ToUpper();
}
</script>
<!-- Layout -->
<html>
<head><title>Pro ASP.NET (Ch 01)</title></head>
<body>
<h1>Make It Upper</h1>
<form runat=”server”>
<input runat=”server” id=”TheString” type=”text” />
<input runat=”server” id=”Button1” type=”submit” value=”Proceed...”
OnServerClick=”MakeUpper” />
<hr>
<h3>Results:</h3>
<span runat=”server” id=”TheResult” />
</form>
</body>
</html>
Le righe vuote e i commenti nel listato precedente separano le tre sezioni:
direttive, codice e layout di pagina. Si noti l’utilizzo generoso dell’attributo runat: è
uno dei blocchi più importanti dell’intero mosaico ASP.NET. Nel paragrafo successivo,
discuteremo runat in maggior dettaglio. Per ora, basti dire che l’attributo runat
promuove un altrimenti inerte tag lato server al rango di istanza di un componente.
Il layout di pagina è costituito da testo letterale e tag HTML, alcuni dei quali
contengono il suddetto attributo runat. Tutto ciò che viene contrassegnato in
Capitolo 1 Il modello di programmazione ASP .NET
questo modo, nonostante le apparenze, non è realmente un elemento HTML. Più
precisamente, è il segnaposto di markup di un componente lato server, un controllo
ASP.NET, che è effettivamente responsabile del markup finale servito al browser. In
un sorgente ASP.NET, ogni tag contrassegnato con l’attributo runat non è output
così come è, ma subisce un processo di trasformazione sul server al termine del quale
viene generato il markup reale. Il runtime ASP.NET è responsabile della mappatura
dei tag in istanze del controllo. Esaminiamo rapidamente il codice.
Un rapido esame del codice
Grazie all’attributo runat il campo di input di testo diviene una istanza della
classe HtmlInputControl quando la pagina viene processata sul server. La proprietà
Value della classe determina il testo di default da assegnare al campo di input.
Quando l’utente clicca il pulsante submit, la pagina esegue automaticamente un
post a se stessa. La magia viene eseguita dall’attributo runat impostato dal tag
<form>. Una volta sul server, il valore postato del campo di input viene letto e
assegnato automaticamente alla proprietà Value di una istanza appena creata di
HtmlInputControl. Successivamente, viene eseguito il codice associato all’evento
OnServerClick. Questo codice prende il contenuto corrente del textbox, la stringa
postata, e lo converte in lettere maiuscole. Infine, la stringa in maiuscolo viene
assegnata alla proprietà InnerText del controllo lato server collegato al tag HTML
<span>. Quando termina il gestore d’evento MakeUpper, la pagina è pronta per il
rendering. A questo punto, il codice HTML aggiornato viene inviato al browser.
Per testare la pagina, bisogna copiare il file .aspx nella directory root del proprio
Web server. Normalmente, è c:\inetpub\wwwroot. Volendo, si può creare una directory
virtuale ad hoc. Supponiamo che la pagina si chiami hello.aspx. Successivamente, si fa
puntare il browser alla pagina. La Figura 1-3 mostra il risultato.
Figura 1-3 La prima (e piuttosto semplice) pagina ASP.NET in azione.
13
14
Parte I Realizzare una pagina ASP .NET
Sarebbe utile dare un’occhiata al sorgente HTML della pagina quando viene
visualizzata la prima volta all’utente, ossia prima che l’utente clicchi per rendere il
testo in maiuscolo.
<!-- Direttive -->
<!-- Sezione Codice -->
<!-- Layout -->
<html>
<head><title>Pro ASP.NET (Ch 01)</title></head>
<body>
<h1>Make It Upper</h1>
<form method=”post” action=”hello.aspx” id=”Form1”>
<div>
<input type=”hidden” name=”__EVENTTARGET” value=”” />
<input type=”hidden” name=”__EVENTARGUMENT” value=”” />
<input type=”hidden” name=”__VIEWSTATE” value=”/wEPDwUJNzM4N…==” />
</div>
<script type=”text/javascript”>
<!-var theForm = document.forms[‘Form1’];
if (!theForm) {
theForm = document.Form1;
}
function __doPostBack(eventTarget, eventArgument) {
if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit();
}
}
// -->
</script>
<input name=”TheString” type=”text” id=”TheString” value=”Hello, world” />
<input name=”Button1” type=”submit” id=”Button1” value=”Proceed ...” />
<hr>
<h3>Results: </h3><span id=”TheResult”></span>
</form>
</body>
</html>
Nel tag <form>, è stato aggiunto un attributo action cablato per forzare il
posting alla stessa pagina. Ciò avviene per progetto ed è uno degli aspetti più
caratteristici di ASP.NET. I vari campi nascosti che si osservano sono essenziali
per l’implementazione del meccanismo di postback e vengono generati
automaticamente. La stessa cosa può essere detta per il codice di script inglobato. I
tag <input> sono pressoché identici ai relativi equivalenti nel sorgente .aspx: solo
l’attributo runat è scomparso.
Dopo esserci sporcati le mani con il codice ASP.NET, facciamo un passo indietro
e esaminiamo gli strati che effettivamente fanno funzionare le pagine ASP.NET nel
contesto di una applicazione.
Capitolo 1 Il modello di programmazione ASP .NET
Il modello a componenti ASP.NET
ASP.NET è la principale tecnologia abilitante di tutte le funzionalità orientate
al Web fornite dal .NET Framework. Il .NET Framework è costituito interamente
da una gerarchia object oriented di classi che abbracciano tutti gli argomenti di
programmazione dei sistemi operativi Windows. In termini generali, una applicazione
Web è costituita da pagine che l’utente richiede da un server e che il server processa e
restituisce come codice di markup, per la maggior parte HTML. Come la risorsa richiesta
venga processata, e perciò come viene generato il markup, è un aspetto specifico del
server. In particolare, quando la risorsa ha una estensione .aspx, IIS delega qualsiasi
ulteriore elaborazione al sistema runtime ASP.NET.
Il runtime ASP.NET trasforma il codice sorgente della pagina .aspx richiesta
nell’istanza attiva di una classe del .NET Framework che deriva da una classe base
denominata Page. Alla fin fine, una pagina ASP.NET in esecuzione è un oggetto, e
pertanto lo è per alcuni dei suoi componenti: i controlli lato server.
Un gran numero di nuove caratteristiche ASP.NET sono semplicemente una
propagazione diretta o indiretta della infrastruttura .NET. ASP.NET trae vantaggio dalla
integrazione inter-linguaggio e dalla gestione delle eccezioni, dalla garbage collection e
dalla Code Access Security, dal deployment e dalla configurazione, e da una libreria di
classi incredibilmente sofisticata. Tutte queste caratteristiche non sono il prodotto di un
motore auto-contenuto, sono disponibili poiché le applicazioni ASP.NET rappresentano
una varietà speciale di applicazione .NET.
Un modello di interazione del componente
A qualsiasi elemento in una pagina ASP.NET contrassegnato con l’attributo runat può
essere attribuito un ID univoco, permettendo di accedere a questo elemento dal proprio
codice lato server. L’accesso agli elementi attraverso l’ID è un approccio naturale sul client
(come l’utilizzo di pagine Dynamic HTML), ma rappresenta uno schema del tutto nuovo
per le applicazioni server. Due fattori rendono possibile questo approccio rivoluzionario:
• L’architettura basata su componenti della piattaforma .NET, e il fatto che ASP.NET è
una parte integrante di questa piattaforma
• Il meccanismo predefinito ASP.NET di gestione dello stato dell’applicazione
Il design a componenti di .NET rende l’interazione del componente agevole ed
efficace in tutti gli ambienti, comprese le applicazioni ASP.NET. I componenti ASP.NET
accedono alle caratteristiche della pagina e interagiscono reciprocamente invocando
metodi e impostando proprietà.
Il fatto che tutti gli elementi della pagina siano veri componenti, e non semplicemente
testo scandibile, fornisce un modello di estensibilità flessibile e potente. La creazione
di nuovi controlli è agevole quanto la derivazione di una nuova classe; realizzare una
gerarchia di ereditarietà di pagina è agevole quanto specificare una classe genitore
differente dalla classe di base Page.
15
16
Parte I Realizzare una pagina ASP .NET
[Attenzione] Visual Studio .NET 2005 restituisce un errore in fase di progettazione se
non si assegna esplicitamente a ciascun controllo ASP.NET un ID univoco. Tuttavia, la
pagina funzionerà a dovere a runtime.
L’attributo runat
L’attributo runat è ciò che determina se un blocco di testo di markup deve essere
emesso alla lettera al momento del rendering o deve essere trasformato in una istanza
stateful di una particolare classe .NET. In quest’ultimo caso, la classe si assume la
responsabilità di emettere il relativo markup. In una pagina ASP.NET, tutti gli elementi
di markup il cui attributo runat è impostato a server vengono considerati controlli lato
server. La classe del controllo espone metodi e proprietà che permettono di configurare
lo stato del componente. Il controllo è responsabile di emettere il codice HTML quando
la pagina viene renderizzata al browser. Consideriamo il seguente semplice codice che
renderizza un elemento ancora nella pagina client:
Response.Write(“<A id=myAnchor href=www.asp.net>Click me</A>”)
L’elemento ancora viene creato da programma e non viene definito nel layout di
pagina. In ASP classico, i blocchi di codice e il metodo Response.Write rappresentano
gli unici modi disponibili per creare o configurare dinamicamente i controlli. In alcuni
ambienti di sviluppo, tra cui Microsoft Visual InterDev, i controlli design-time fornivano
un modo a oggetti per emettere in output HTML generato dinamicamente. I controlli
design-time, tuttavia, rappresentavano appunto ciò che il nome indica: ossia, controlli
che si possono utilizzare a design-time per generare markup e codice script. In ASP.NET,
è disponibile una nuova varietà di controlli che potremmo chiamare controlli run-time
per evidenziare il contrasto con i controlli design-time.
Lavorare con i controlli lato server
In una pagina ASP, non vi è modo per associare codice all’elemento myAnchor.
Si tratta solo di testo congelato, inerte, utile solo da inviare al browser. Una volta su
un client, l’elemento myAnchor riprende vita e può accettare istruzioni di scripting.
Si supponga ora di dover impostare l’attributo href dell’ancora in base a condizioni a
runtime. In ASP classico, si potrebbe prima ottenere il valore dell’attributo href e quindi
invocare Response.Write:
strHref = “www.asp.net”
strHtml = “<A id=myAnchor “
strHtml = strHtml + “href=” + strHref
strHtml = strHtml + “>Click me</A>”
Response.Write(strHtml)
Questo codice funzionerà immutato in una pagina ASP.NET ma di certo non è
quanto di meglio si possa fare. Dichiarando il tag <A> con l’attributo runat, si può dar
vita all’elemento ancora anche sul server:
<A runat=”server” id=”myAnchor”>Click me</A>
Capitolo 1 Il modello di programmazione ASP .NET
Quando la pagina viene caricata, il runtime ASP.NET scandisce il codice sorgente e
crea le istanze di tutti i controlli contrassegnati con l’attributo runat. In tutta la pagina,
l’ID di myAnchor identifica una istanza del controllo lato server mappato al tag <A>.
Il codice seguente può essere utilizzato per impostare da programma l’attributo href
quando la pagina viene caricata:
<script runat=”server” language=”C#”>
void Page_Load(object sender, EventArgs e)
{
myAnchor.HRef = “http://www.asp.net”;
}
</script>
Gli elementi di markup il cui nome corrisponde a un elemento HTML vengono
mappati al corrispondente controllo server HTML. Si noti che non tutti i possibili
tag HTML hanno dei corrispondenti controlli ASP.NET; per quelli sprovvisti, viene
utilizzato un controllo generico. L’elenco dei tag e dei relativi controlli associati è
cablato nel runtime ASP.NET. Gli elementi che appartengono al namespace <asp>
vengono mappati ai controlli Web server. Altri elementi di markup vengono mappati
all’assembly e al nome di classe dichiarato utilizzando una direttiva @Register.
Tag a livello di pagina
L’attributo runat può essere utilizzato anche con tag a livello di pagina come
<head> e <body>. Questi tag vengono rappresentati attraverso una istanza della
classe HtmlGenericControl. HtmlGenericControl è la classe .NET utilizzata per
rappresentare un tag HTML lato server non direttamente rappresentato da una classe
del .NET Framework. L’elenco di questi tag comprende anche <span>, <font> e
<iframe>.
Nella pagina seguente, il colore di sfondo viene impostato da programma quando la
pagina viene caricata:
<%@ Page Language=”C#” %>
<script runat=”server”>
private void Page_Load(object sender, EventArgs e)
{
TheBody.Style[HtmlTextWriterStyle.BackgroundColor] = “lightblue”;
}
</script>
<html>
<body id=”TheBody” runat=”server”>
<h3>The background color of this page has been set programmatically.
Open View|Source menu to see the source code.</h3>
</body>
</html>
Il codice HTML risultante è il seguente:
<html>
<head><title>Pro ASP.NET (Ch 01)</title></head>
<body id=”TheBody” style=”background-color:lightblue;”>
<form method=”post” action=”Body.aspx” id=”Form1”>
<div>
<input type=”hidden” name=”__VIEWSTATE” value=”/wEPD… RVC+” />
</div>
17
18
Parte I Realizzare una pagina ASP .NET
<h3>The background color of this page has been set programmatically.
Open View|Source menu to see the source code.</h3>
</form>
</body>
</html>
Analogamente, si può impostare uno qualsiasi degli attributi del tag <body>, decidendo
così da programma, ad esempio, quale stylesheet o immagine di sfondo utilizzare. Per
creare gli attributi del tag si utilizza la collection Attributes di HtmlGenericControl. Per
impostare il valore inner text di un tag si utilizza la proprietà InnerText.
TheBody.Attributes[“Background”] = “/proaspnet20/images/body.gif”;
Discuteremo in maggior dettaglio l’interfaccia di programmazione della classe
HtmlGenericControl nel Capitolo 4.
[Nota] In ASP.NET 2.0, si può accedere al contenuto del tag <head> da programma
finché è contrassegnato con l’attributo runat. La classe Page espone un gruppo di metodi
e proprietà ad-hoc che esploreremo nel Capitolo 3.
Tag incogniti
Nel caso di tag incogniti, cioè tag che non sono né predefiniti nello schema corrente
né definiti dall’utente, il runtime ASP.NET può comportarsi in due modi differenti. Se
il tag non contiene informazioni sul namespace, ASP.NET lo tratta come un generico
controllo HTML. Il namespace vuoto, infatti, viene valutato in namespace HTML,
inducendo perciò il runtime ASP.NET a credere che il tag sia realmente un elemento
HTML. Non viene generata nessuna eccezione, e il testo di markup viene generato sul
server. Ad esempio, consideriamo la seguente pagina ASP.NET:
<%@ Page Language=”C#” %>
<script runat=”server”>
void Page_Load(object sender, EventArgs e)
{
dinoe.Attributes[“FavoriteFood”] = “T-bone steak”;
}
</script>
<html>
<head><title>Pro ASP.NET (Ch 01)</title></head>
<body>
<form runat=”server”>
<Person id=”dinoe” runat=”server” />
Click the <b>View|Source</b> menu item...
</form>
</body>
</html>
Il tag <Person> viene comunque processato come se fosse un ordinario tag HTML,
e viene aggiunto l’attributo FavoriteFood. La Figura 1-4 mostra qual è effettivamente
il codice HTML di questa pagina. Nell’esempio precedente, il tipo dell’oggetto dinoe è
HtmlGenericControl.
Capitolo 1 Il modello di programmazione ASP .NET
Figura 1-4 ASP.NET processa anche tag custom privi di namespace, mappandoli alla classe
HtmlGenericControl.
Se il tag contiene informazioni sul namespace, è accettabile finché il namespace
è <asp> o è un namespace esplicitamente associato al nome del tag utilizzando una
direttiva @Register. Se il namespace è incognito, si verifica un errore di compilazione.
Controlli Server ASP.NET
Sostanzialmente, esistono due famiglie di controlli server ASP.NET. Si tratta dei controlli
server HTML e dei controlli server Web. System.Web.UI.HtmlControls è il namespace dei
controlli server HTML. System.Web.UI.WebControls raggruppa tutti i controlli server Web.
Controlli Server HTML
I controlli server HTML sono classi che rappresentano un tag HTML standard
supportato da gran parte dei browser. L’insieme delle proprietà di un controllo
server HTML corrisponde a un insieme utilizzato comunemente di attributi del tag
corrispondente. Il controllo espone proprietà come InnerText, InnerHtml, Style e
Value più alcune collection come Attributes. Ogni volta che nel sorgente della pagina
viene trovato il corrispondente tag HTML contrassegnato con runat=“server” vengono
create automaticamente dal runtime ASP.NET istanze di controlli server HTML.
Come già detto, l’insieme disponibile di controlli server HTML non copre tutti i
possibili tag HTML di una determinata versione dello schema HTML. Solo i tag più
comunemente utilizzati hanno trovato posto nel namespace System.Web.UI.HtmlCont
rols. Tag come <iframe>, <frameset>, <body> e <hn> sono stati lasciati fuori alla
pari di tag meno frequentemente utilizzati come <fieldset>, <marquee> e <pre>.
L’assenza di un controllo server specializzato, tuttavia, non limita la propria potenza
di programmazione quando si tratta di utilizzare e configurare questi tag sul server.
Bisogna solo utilizzare una interfaccia di programmazione più generica, la classe
HtmlGenericControl, che abbiamo descritto brevemente in questo paragrafo.
19
20
Parte I Realizzare una pagina ASP .NET
Controlli Server Web
I controlli server Web sono controlli con più caratteristiche dei controlli server
HTML. I controlli server Web comprendono non solo i controlli di input come pulsanti
e textbox, ma anche controlli di uso particolare come un calendario, un ad rotator, un
elenco a discesa, un treeview e una griglia dati. I controlli server Web comprendono
anche componenti che assomigliano molto ad alcuni controlli server HTML. I controlli
server Web, tuttavia, sono più astratti dei corrispondenti controlli server HTML nel
senso che il relativo modello a oggetti non riflette necessariamente la sintassi HTML.
Ad esempio, confrontiamo il controllo testo HTML server e il controllo server Web
TextBox. Il controllo testo HTML server ha il seguente markup:
<input runat=”server” id=”FirstName” type=”text” value=”Dino” />
Il controllo server Web TextBox ha il seguente markup:
<asp:textbox runat=”server” id=”FirstName” text=”Dino” />
Entrambi i controlli generano lo stesso codice di markup HTML. Tuttavia,
l’interfaccia di programmazione del controllo server testo HTML corrisponde
moltissimo a quella del tag HTML <input>, mentre metodi e proprietà del
controllo server Web TextBox sono denominati in un modo più astratto. Ad
esempio, per impostare il contenuto di un controllo server testo HTML si deve
utilizzare la proprietà Value poiché Value è il nome corrispondente dell’attributo
HTML. Se si lavora con il controllo server Web TextBox, si deve ricorrere a Text.
Salvo pochissime eccezioni (di cui discuterò nel Capitolo 3), l’utilizzo dei controlli
server HTML o dei controlli server Web per rappresentare elementi HTML è solo
una questione di preferenza, di facilità di sviluppo e di manutenzione.
Lo stack di sviluppo ASP.NET
Al livello più alto di astrazione, lo sviluppo di una applicazione ASP.NET
attraversa due fasi: creazione delle pagine e configurazione runtime. Si realizzano le
pagine che formano l’applicazione, si implementano i relativi prerequisiti d’utente,
e quindi si esegue una ottimizzazione fine del circostante ambiente di runtime per
far sì che serva le pagine in modo efficace e sicuro. Come mostra la Figura 1-2, il
modello di componente ASP.NET è la base di tutte le applicazioni ASP.NET e dei
relativi blocchi costituenti. Tenendo presente la Figura 1-2, esaminiamo i vari strati
logici per vedere cosa contengono e perché.
Lo strato di Presentazione
Una pagina ASP.NET è costituita da controlli, testo e markup. Quando il codice
sorgente viene trasformato in una istanza attiva di una classe di pagina, il runtime
ASP.NET non fa alcuna ulteriore distinzione tra testo letterale, markup e controlli
server: tutto è un controllo, compreso il testo letterale e i caratteri carriage-return. In
fase di esecuzione, qualsiasi pagina ASP.NET è un semplice grafo di controlli.
Capitolo 1 Il modello di programmazione ASP .NET
Controlli sofisticati
La sofisticata programmazione di ASP.NET scaturisce dall’ampia libreria di controlli
server che abbracciano i task fondamentali dell’interazione HTML, ad esempio,
raccogliendo il testo attraverso i tag input, ma anche funzionalità più avanzate come la
visualizzazione di dati basata su una griglia. Il set nativo dei controlli è abbastanza grande da
permettere di soddisfare virtualmente qualsiasi insieme di prerequisiti. Inoltre, la recente
versione di ASP.NET aggiunge alcuni nuovi controlli sofisticati per portare la produttività
dello sviluppatore in prossimità del massimo livello possibile. In ASP.NET 2.0, si trovano
controlli per creare wizard Web, viste collassabili di dati gerarchici, avanzati report di dati,
form comunemente utilizzati, data binding dichiarativo, menu, navigazione del sito. Si trova
anche una sottile API per creare pagine in stile portale. La disponibilità di controlli sofisticati
significa una riduzione del tempo di sviluppo e degli errori di codifica, più “best practice”
implementate e più funzionalità avanzate a disposizione degli utenti finali. Tratteremo
specificamente i controlli nel Capitolo 4, nel Capitolo 6 e in seguito nel Capitolo 10.
Controlli custom
I controlli principali di ASP.NET forniscono un insieme completo di tool per
realizzare funzionalità Web. L’insieme standard di controlli può essere esteso
e migliorato aggiungendo dei controlli personalizzati. Il sottostante modello di
componente ASP.NET semplifica notevolmente il compito applicando i principi e le
regole comuni della programmazione object-oriented.
Si possono costruire nuovi controlli migliorando un controllo esistente o
aggregando assieme due o più controlli per formarne uno nuovo. ASP.NET 1.x è dotato
di un piccolo set di classi di base su cui implementare controlli del tutto nuovi. Questo
set di classi è stato esteso in ASP.NET 2.0, in particolare per semplificare lo sviluppo di
nuovi controlli data-bound.
Rendering adattivo
A partire dalla versione 2.0, ASP.NET mette a disposizione una nuova architettura
di control adapter che permette a qualsiasi controllo server di creare renderizzazioni
alternative per una varietà di browser. Si noti, tuttavia, che il nuovo modello di adapter
ASP.NET 2.0 non si applica ai controlli mobile. I controlli mobile sono una particolare
famiglia di controlli Web progettati per realizzare applicazioni per dispositivi mobile.
I controlli mobile ASP.NET 2.0 utilizzano ancora il vecchio modello di adapter,
disponibile sin da ASP.NET 1.1, per controlli che derivano da MobileControl e sono
ospitati su pagine che derivano da MobilePage. In breve, se si deve scrivere una
applicazione mobile con ASP.NET 2.0, si devono utilizzare i controlli mobile, come si
sarebbe fatto con ASP.NET 1.1.
Pertanto qual è il valore aggiunto del nuovo modello di adapter? Con questa
forma di rendering adattivo, si possono scrivere control adapter per personalizzare
i controlli server per singoli browser. Ad esempio, si può scrivere un control
adapter per generare un differente markup HTML per il controllo Calendar per un
determinato browser desktop.
21
22
Parte I Realizzare una pagina ASP .NET
Il framework della pagina
Qualsiasi pagina ASP.NET funziona come istanza di una classe che ha origine dalla
classe Page. La classe Page è il punto terminale di una pipeline di moduli che processa
qualsiasi richiesta HTTP. I vari componenti di sistema che operano sulla richiesta
originale costruiscono passo per passo tutte le informazioni necessarie a localizzare
l’oggetto pagina per generare il markup. Il modello a oggetti della pagina presenta
diverse caratteristiche e funzionalità che potrebbero essere raggruppate in termini di
eventi, scripting, personalizzazione, stile e prototipizzazione.
Eventi di pagina
Il ciclo di vita di una pagina nel runtime ASP.NET è contrassegnato da una serie di
eventi. Collegando il proprio codice a questi eventi, gli sviluppatori possono modificare
dinamicamente l’output di pagina e lo stato dei controlli di cui si compone. In ASP.NET
1.x, una pagina scatena eventi come Init, Load, PreRender e Unload che sottolineano i
momenti principali della vita della pagina. ASP.NET 2.0 aggiunge parecchi nuovi eventi
per permettere di seguire più da vicino e con precisione l’elaborazione della richiesta. In
particolare, si trovano nuovi eventi per segnalare l’inizio e la fine della fase di inizializzazione
e caricamento. Il ciclo di vita di pagina verrà esaminato in modo esaustivo nel Capitolo 3.
Scripting di pagina
Il modello a oggetti di scripting di pagina permette agli sviluppatori di gestire il codice di
scripting, e ai campi nascosti di essere iniettati nelle pagine client. Questo modello a oggetti
genera codice JavaScript utilizzato per tenere assieme gli elementi HTML generati dai controlli
server, fornendo così caratteristiche altrimenti impossibili da programmare sul server. Ad
esempio, in questo modo si può impostare il focus di input ad un particolare controllo quando
la pagina viene visualizzata nel browser client. Le pagine ASP.NET possono essere architettate
per inoltrare le invocazioni client a metodi server senza eseguire un completo postback e
successivamente eseguire il refresh dell’intera pagina visualizzata. Questa sorta di engine di
scripting remoto viene implementato attraverso un meccanismo di callback che offre un chiaro
vantaggio agli sviluppatori. Quando si utilizzano gli script callback, i risultati dell’esecuzione di
un metodo lato server vengono passati direttamente a una funzione JavaScript che può quindi
aggiornare l’interfaccia utente attraverso Dynamic HTML. Un roundtrip si verifica ancora, ma la
pagina non viene del tutto completamente sottoposta a un refresh. Gli script callback, tuttavia,
non rappresentano l’unica buona notizia. Il posting cross-page è una ulteriore caratteristica
che la comunità degli sviluppatori ASP.NET ha richiesto a gran voce. Questa caratteristica
permette il posting del contenuto di un form a una ulteriore pagina. Suona come insegnare
dei vecchi trucchi a un nuovo cane? Forse. Come già detto prima in questo capitolo, uno degli
aspetti più caratteristici di ASP.NET è che ciascuna pagina contiene un solo tag <form>, che
continuamente esegue il post a sé stesso. Questo è il modo in cui ASP.NET è stato progettato, e
produce diversi vantaggi. Nelle precedenti versioni di ASP.NET, il posting cross-page potrebbe
essere implementato allo stesso modo di ASP classico, ossia, eseguire il posting attraverso un
puro <form> HTML non contrassegnato con l’attributo runat. Questo metodo funziona
bene, ma vi lascia fuori dal mondo object-oriented e a tipizzazione forte di ASP.NET. Il posting
cross-page, come è implementato in ASP.NET 2.0, colma il gap.
Capitolo 1 Il modello di programmazione ASP .NET
Personalizzazione di pagina
In ASP.NET 2.0, si possono memorizzare e recuperare informazioni e preferenze specifiche all’utente senza l’onere di dover scrivere il codice di infrastruttura. L’applicazione
definisce il proprio modello di dati personalizzati, e il runtime ASP.NET si occupa del resto
scandendo e compilando questo modello in una classe. Ciascun membro dei dati della classe personalizzata corrisponde a un blocco di informazioni specifiche all’utente corrente. Il
caricamento e il salvataggio dei dati personalizzati è del tutto trasparente agli utenti finali e
non richiede neanche all’autore della pagina di dover conoscere troppo sull’infrastruttura
interna. Le informazioni personalizzate dell’utente sono disponibili all’autore della pagina
attraverso una proprietà di pagina. Ciascuna pagina può consumare le informazioni precedentemente salvate e salvare nuove informazioni per ulteriori richieste.
Stilizzazione di pagina
In modo molto simile ai temi di Microsoft Windows XP, i temi ASP.NET assegnano un
insieme di stili e di attributi visuali agli elementi personalizzabili del sito. Questi elementi
comprendono le proprietà dei controlli, i fogli stile di pagina, le immagini e i template della
pagina. Un tema è l’unione di tutti gli stili visuali di tutti gli elementi personalizzabili delle pagine: una sorta di file super-CSS (Cascading Style Sheet). Un tema è identificato dal nome ed
è costituito da file CSS, immagini e dai cosiddetti “control skin”. Un control skin è un file di
testo che contiene le dichiarazioni di default del controllo in cui vengono impostate le proprietà visuali del controllo. Con questa caratteristica abilitata, se lo sviluppatore aggiunge, ad
esempio, un controllo DataGrid a una pagina, il controllo viene renderizzato con l’aspetto
di default definito nel tema. I temi rappresentano un’importante nuova caratteristica, poiché
permettono di modifica il look&feel delle pagine in un unico colpo e, aspetto forse ancor più
importante, di dare a tutte le pagine un aspetto consistente.
Prototipizzazione di pagina
Quasi tutti gli odierni siti Web contengono pagine con un layout simile. Per alcuni siti, il
layout è semplicemente un header e un footer; altri siti possono contenere sofisticati menu
di navigazione e vari “gadget” che racchiudono il contenuto. In ASP.NET 1.x, l’approccio
raccomandato per gli sviluppatori era di racchiudere questi blocchi di UI in controlli utente
e referenziarli in ciascuna pagina di contenuto. Come si può immaginare, questo modello
funziona abbastanza bene quando il sito contiene solo alcune pagine; sfortunatamente,
diventa ingestibile se il sito contiene centinaia di pagine. Un approccio basato sui controlli
utente presenta diversi problemi importanti per siti ricchi di contenuto. Per dirne uno,
si deve replicare il codice nelle pagine dei contenuti per referenziare i controlli utente.
Inoltre, l’applicazione di nuovi template richiede che lo sviluppatore ritocchi ogni pagina.
Infine, gli elementi HTML presenti nell’area dei contenuti sono probabilmente divisi da
controlli utente. In ASP.NET 2.0, la prototipizzazione di pagina è notevolmente migliorata grazie alle pagine master. Gli sviluppatori che lavorano a siti Web in cui molte pagine
condividono una parte di layout e di funzionalità, invece di aggiungere le informazioni di
layout a ciascuna pagina o di separare il layout tra diversi controlli utente, ora possono
creare qualsiasi funzionalità condivisa in un file master. Basandosi sul file master condiviso,
gli sviluppatori possono creare qualsiasi numero di pagine di contenuto dall’aspetto simile
23
24
Parte I Realizzare una pagina ASP .NET
semplicemente referenziando la pagina master attraverso un nuovo attributo. Tratteremo
le pagine master nel Capitolo 6.
L’ambiente di runtime HTTP
Il processo attraverso cui una richiesta Web diventa semplice testo HTML per il browser
non è molto differente in ASP.NET 2.0 rispetto a ASP.NET 1.1. La richiesta viene selezionata da
IIS, a partire da un identity token, e passata all’estensione ASP.NET ISAPI (aspnet_isapi.dll), il
punto di ingresso di qualsiasi elaborazione inerente a ASP.NET. Questo è il processo generale,
ma alcuni importanti dettagli dipendono dalla versione sottostante di IIS e dal modello di
processo in uso. Il modello di processo è la sequenza di operazioni necessarie a elaborare
una richiesta. Quando il runtime ASP.NET viene eseguito su IIS 5.x, il modello di processo si
basa su un processo worker separato, denominato aspnet_wp.exe. Questo processo Microsoft
Win32, riceve il controllo direttamente da IIS attraverso l’estensione ASP.NET ISAPI ospitata.
All’estensione viene passata qualsiasi richiesta di risorse ASP.NET, che la consegna al processo
worker. Il processo worker carica il Common Language Runtime (CLR) e avvia la pipeline di
oggetti managed che trasforma la richiesta originale da un payload HTTP in una pagina completa
per il browser. Il modulo aspnet_isapi e il processo worker implementano caratteristiche
avanzate come il process recycling, il caching dell’output di pagina, il monitoraggio della
memoria, e il pooling dei thread. Ciascuna applicazione Web viene eseguita in un AppDomain
distinto all’interno del processo worker. Per default, il processo worker è in esecuzione sotto
un account limitato, scarsamente privilegiato, denominato ASPNET.
[Nota] Nel CLR, un application domain (AppDomain) fornisce i limiti di isolamento, di
scaricamento e di sicurezza per l’esecuzione di codice managed. Un AppDomain è un
tipo di processo lightweight, specifico del CLR, in cui più assembly vengono caricati
e protetti per eseguire il codice. Più AppDomain possono essere in esecuzione in un
singolo processo di CPU. Non vi è una correlazione uno a uno tra AppDomain e thread.
Diversi thread possono appartenere a un singolo AppDomain, e mentre un determinato
thread non è confinato a un singolo application domain, in un determinato momento, un
thread viene eseguito in un singolo AppDomain.
Quando ASP.NET viene eseguito sotto IIS 6.0, il modello di processo di default è
differente e non viene utilizzato il processo aspnet_wp.exe. Il processo worker utilizzato
è il processo worker standard IIS 6.0 (w3wp.exe). Questo analizza l’URL della richiesta
e carica una specifica estensione ISAPI. Ad esempio, carica aspnet_isapi.dll per richieste
inerenti a ASP.NET. Nel modello di processo IIS 6.0, l’estensione aspnet_isapi è responsabile
del caricamento del CLR e dell’avvio della pipeline HTTP. Una volta nella pipeline HTTP
ASP.NET, la richiesta passa attraverso vari componenti, di sistema e definiti dall’utente, che
operano su di essa finché non viene trovata e istanziata con esito positivo una classe di pagina
valida. Gli sviluppatori possono modificare e adattare l’ambiente di run-time entro certi
limiti. Ciò può accadere in tre modi: modificando l’elenco dei moduli HTTP installati, i file di
configurazione, i provider di stato e di personalizzazione, e altri servizi di applicazione.
Capitolo 1 Il modello di programmazione ASP .NET
Moduli HTTP di sistema
I moduli HTTP rappresentano l’equivalente ASP.NET dei filtri ISAPI. Un modulo
HTTP è una classe del .NET Framework che implementa una particolare interfaccia.
Tutte le applicazioni ASP.NET derivano da alcuni moduli HTTP di sistema come
definiti nel file machine.config. I moduli preinstallati forniscono caratteristiche come
l’autenticazione, l’autorizzazione e i servizi inerenti alla sessione. In termini generali,
un modulo HTTP può preprocessare e postprocessare una richiesta, e intercetta e
gestisce sia gli eventi di sistema sia gli eventi generati da altri moduli.
La buona notizia è che si possono scrivere e registrare i propri moduli HTTP,
collegarli nella pipeline di runtime ASP.NET, gestire gli eventi di sistema e scatenare dei
propri eventi. Inoltre, si può adattare a livello di applicazione l’elenco dei moduli HTTP
di default. Si possono aggiungere moduli custom e rimuovere quelli non necessari.
Configurazione dell’applicazione
Il comportamento delle applicazioni ASP.NET è soggetto a una varietà di parametri;
alcuni sono impostazioni a livello di sistema, alcuni dipendono dalle caratteristiche
dell’applicazione. L’insieme comune dei parametri di sistema viene definito nel file
machine.config. Questo file contiene i valori di default e quelli specifici alla macchina
di tutte le impostazioni supportate. Le impostazioni di macchina di solito vengono
controllate dall’amministratore di sistema, e alle applicazioni non dovrebbe essere
concesso l’accesso in scrittura al file machine.config. Il file machine.config è posizionato
all’esterno dello spazio Web dell’applicazione e, in quanto tale, non può essere raggiunto
anche se un intruso riesce a iniettare del codice subdolo nel sistema.
Qualsiasi applicazione può ridefinire gran parte dei valori di default memorizzati
nel file machine.config creando uno o più file web.config specifici dell’applicazione.
Come minimo, una applicazione crea un file web.config nella propria cartella root. Il file
web.config è un subset del file machine.config, scritto in base allo stesso schema XML.
L’obiettivo del web.config è ridefinire alcune delle impostazioni di default. Attenzione,
tuttavia, che non tutte le impostazioni definite nel machine.config possono essere
ridefinite in un file di configurazione figlio. In particolare, le informazioni sul modello
di processo ASP.NET possono essere definite solo a livello di macchina utilizzando
il file machine.config. Se l’applicazione contiene directory figlie, può definire un file
web.config per ciascuna cartella. L’ambito di visibilità di ciascun file di configurazione
viene determinato in una modalità gerarchica, top-down. Le impostazioni valide per una
pagina vengono determinate dalla somma delle modifiche dei vari file web.config trovati
strada facendo applicati alla configurazione originale di macchina. Un file web.config
può estendere, ridurre e ridefinire qualsiasi tipo di impostazioni definite a un livello
superiore, compreso il livello macchina. Se non esiste alcun file di configurazione in una
cartella dell’applicazione, vengono applicate le impostazioni valide al livello superiore.
Servizi di applicazione
L’autenticazione, la gestione dello stato, e il caching sono tutti esempi di servizi
essenziali che l’ambiente di runtime ASP.NET fornisce alle applicazioni in esecuzione.
Con ASP.NET 2.0, sono stati aggiunti all’elenco altri servizi, compresa l’amministrazione,
25
26
Parte I Realizzare una pagina ASP .NET
la gestione delle membership, la gestione dei ruoli e la personalizzazione, come
mostrato nella Figura 1-5.
Figura 1-5 Una vista più dettagliata dello stack di sviluppo ASP.NET. La freccia indica la tipica
prospettiva top-down dell’applicazione, andando verso il basso dalla interfaccia utente ai servizi
di sistema.
Gran parte dei servizi di applicazione devono persistere e recuperare alcuni dati
per scopi interni. Nel farlo, un servizio sceglie un modello di dati e un supporto di
memorizzazione, e ottiene i dati attraverso una particolare sequenza di passi. Le
applicazioni basate su questi servizi vengono vincolate per progetto a utilizzare queste
impostazioni, che di solito comprendono uno schema dati prefissato, un supporto
di memorizzazione predefinito, e un comportamento cablato nel codice. Cosa
accade se non si gradiscono o non si vogliono queste restrizioni? La configurazione
a run-time, ottenuta attraverso i file machine.config e web.config, aggiunge un po’
di flessibilità in più al proprio codice. Tuttavia, la configurazione a run-time non
fornisce una soluzione definitiva abbastanza flessibile da permettere una completa
personalizzazione del servizio tale da renderla estensibile e lineare da implementare.
Una soluzione più definitiva viene fornita da ASP.NET 2.0, che formalizza e integra nel
complessivo framework di classi un design pattern che è stato in origine sviluppato e
utilizzato in diversi ASP.NET Starter Kit. Noto come modello di provider, questo pattern
definisce una API comune per una varietà di operazioni, ciascuna nota come provider.
Al contempo, l’interfaccia del provider contiene diversi hook perché gli sviluppatori
possano avere un controllo completo del comportamento interno dell’API, dello
schema dati utilizzato e del supporto di memorizzazione.
Capitolo 1 Il modello di programmazione ASP .NET
[Importante] Il modello di provider è uno degli aspetti più importanti e critici di ASP.NET.
Una buona comprensione di questo pattern è cruciale per eseguire un efficace design e
per l’implementazione di applicazioni di punta. Il modello di provider viene formalizzato in
ASP.NET 2.0, ma è semplicemente l’implementazione di un design pattern. Come tale, è
del tutto slegato come concetto da qualsiasi piattaforma e framework. Pertanto una volta
compresa l’idea di base, si può iniziare a utilizzarla in qualsiasi applicazione, anche al di
là del contesto ASP.NET.
Il modello di provider ASP.NET
Vi è un ben noto design pattern dietro il modello di provider ASP.NET: il pattern
strategy. Per definizione, il pattern strategy indica un comportamento previsto (ad
es., l’ordinamento) che può essere implementato attraverso una varietà di algoritmi
interscambiabili (ad es., Quicksort, Mergesort). Ciascuna applicazione seleziona quindi
l’algoritmo più idoneo, pur mantenendo intatto il comportamento pubblico osservabile
e l’API di programmazione. La caratteristica più considerevole del pattern strategy è che
fornisce a un oggetto, o a un intero sottosistema, un modo per esporre i propri dettagli
in modo che un client possa scollegare l’implementazione di default di una determinata
caratteristica e collegarne una propria. È esattamente ciò che accade in ASP.NET per molti
servizi, tra cui membership, ruoli, gestione dello stato, personalizzazione, navigazione del
sito. Il modello di provider ASP.NET è l’implementazione ASP.NET del pattern strategy.
La motivazione alla base del modello di provider
Il modello di provider non è una caratteristica dell’applicazione che gli utenti finali
possono vedere con i propri occhi. Di per sé, non fa sì che una applicazione mostri un
contenuto più sofisticato, che venga eseguita più velocemente o che sia più responsiva.
Il modello di provider è una caratteristica infrastrutturale che migliora l’architettura di
una applicazione abilitando gli sviluppatori e gli architetti a operare dietro le quinte di
alcuni componenti di sistema. Al contempo, abilita gli sviluppatori a realizzare nuovi
componenti che espongono degli hook, a cui i client possono collegarsi e personalizzare
comportamento e impostazioni. L’implementazione del pattern strategy non trasforma
una applicazione in un progetto open-source, permettendo a chiunque di modificare
qualcosa. Significa semplicemente che si ha un semplice, raffinato e efficace pattern per
rendere certe parti della propria applicazione personalizzabili dai client. Al contempo,
l’implementazione ASP.NET del pattern (il modello di provider), vi rende in grado di
personalizzare certe parti dell’ambiente di runtime ASP.NET attraverso classi speciali
denominate provider dalle quali si può derivarne una propria.
Esemplificazione del modello di provider
Per vedere un esempio del modello di provider e dei relativi vantaggi principali,
osserviamo la Figura 1-6. La figura riassume il classico schema per l’autenticazione di
un utente. I blocchi del diagramma seguono strettamente il flusso delle operazioni in
ASP.NET 1.1.
27
28
Parte I Realizzare una pagina ASP .NET
Figura 1-6 Il classico schema di membership delle applicazioni ASP.NET 1.1.
All’utente che tenta di connettersi a una pagina protetta viene mostrata una pagina di
login e viene invitato a digitare le credenziali. Successivamente, nome e password vengono
passate a una funzione, che è in definitiva responsabile della validazione dell’utente.
ASP.NET 1.x può verificare automaticamente gli utenti rispetto agli account Windows o
a un elenco di nomi presenti nel file web.config. Nessuno di questi approcci funziona
bene in una applicazione Web realistica; in gran parte dei casi, gli sviluppatori finiscono
per scrivere un blocco di codice custom per validare le credenziali rispetto a un proprio
data source artigianale. Lo schema e il supporto di memorizzazione del data source sono
prefissati e determinati dallo sviluppatore. Analogamente, l’algoritmo impiegato per validare
le credenziali viene vincolato dal progetto. C’è qualcosa di errato in questa soluzione? Non
necessariamente. Funziona bene, permette di controllare tutto, e può essere adattata per
funzionare in altre applicazioni. L’ostacolo è che non vi è alcun pattern ben definito che
emerge da questa soluzione. Certo, si può portarla da una applicazione a un’altra, ma
complessivamente la soluzione è correlata al pattern adapter quasi come il cut&paste è
correlato all’ereditarietà nell’approccio object-oriented. Consideriamo brevemente un
ulteriore scenario: la gestione dello stato della sessione. In ASP.NET 1.x, si può memorizzare
lo stato della sessione in un processo separato dall’applicazione in esecuzione: può essere
SQL Server o un servizio Windows (il server di stato ASP.NET). Così facendo, tuttavia, si è
vincolati a utilizzare lo schema dati che ASP.NET cabla per vostro conto. Inoltre, si immagini
di non essere un utente di SQL Server. In questo caso, o si abbandona l’idea di memorizzare
lo stato di sessione in un database o si acquista un set di licenze per SQL Server. Infine, non
c’è niente che si può fare sul comportamento intrinseco del modulo di sessione ASP.NET. Se
non si gradisce il modo in cui, ad esempio, serializza i dati nello storage out-of-process, non
si può modificarlo. Prendere o lasciare: non vi è alcuna scelta intermedia.
Siete in grado di vedere il quadro complessivo? Vi sono moduli in ASP.NET che vi
costringono a prendere (o lasciare) un determinato schema di dati, un determinato
supporto di memorizzazione, un determinato comportamento intrinseco. Il più che si
Capitolo 1 Il modello di programmazione ASP .NET
può fare è (talvolta) evitare l’utilizzo di questi moduli e scriverne uno proprio da zero,
come abbiamo delineato nell’esempio sulla membership. Tuttavia, implementare una
propria sostituzione non è necessariamente una mossa astuta. Ci si trova con un sistema
proprietario e dipendente dall’applicazione che non è automaticamente portabile da una
applicazione a un’altra. Inoltre, se si assume del nuovo personale, è necessario formarlo
prima che si abitui a utilizzare la vostra API. Infine, si deve profondere un notevole impegno
per rendere una tale API proprietaria abbastanza generale da essere riusabile ed estensibile
in una varietà di contesti. (Altrimenti, si dovrà reinventare ogni volta la ruota). In che modo
il modello di provider è una soluzione migliore? In primo luogo, fornisce un’interfaccia di
programmazione ben documentata e comune per eseguire dei compiti comuni. Inoltre, si
ottiene la capacità di controllare completamente l’attività interna e la logica di accesso ai dati
di ciascuna API che ricade sotto questo ombrello. Alla fin fine, in ASP.NET 1.1 spesso non si
ha alcuna altra scelta se non scrivere una propria API per disporre di certe funzioni nel modo
in cui si vuole. In ASP.NET 2.0, il modello di provider offre una alternativa molto migliore.
Tanto migliore che è praticamente un crimine non utilizzarla.
Figura 1-7 L’esempio di membership rivisitato per utilizzare il modello di provider di ASP.NET 2.0.
La Figura 1-7 rivisita la Figura 1-6 alla luce del modello di provider. ASP.NET 2.0
rende disponibile un gruppo di metodi statici di una classe globale, Membership.
(Tratteremo la API di membership in maggior dettaglio nel Capitolo 15). A livello di
applicazione, si invoca sempre lo stesso metodo per eseguire la stessa operazione (ad
esempio, validazione delle credenziali utente, creazione di nuovi utenti, modifica delle
password). Sotto questa API comune, tuttavia, si può collegare il proprio provider per
svolgere il lavoro nel modo voluto. Scrivere un nuovo provider è facile come derivare
una nuova classe da una classe base nota e ridefinire alcuni metodi ben noti. La selezione
del provider corrente per un determinato compito avviene nel file di configurazione.
29
30
Parte I Realizzare una pagina ASP .NET
Vantaggi del modello di provider
Nella implementazione ASP.NET, il pattern strategy comporta due principali vantaggi: un’estensiva personalizzazione dell’ambiente run-time dell’applicazione e la riusabilità del codice. Diverse aree in ASP.NET vengono interessate dal modello di provider.
Si possono scrivere provider per gestire membership e ruoli dell’utente, persistere lo
stato della sessione, gestire i profili utente attraverso la personalizzazione, e caricare
informazioni sulla mappa del sito da diverse fonti. Ad esempio, scrivendo un provider si
può modificare lo schema dei dati utilizzato per persistere le credenziali, memorizzare
questi dati in un database Oracle o DB2, e memorizzare le password in formato hash
piuttosto che in testo in chiaro. Questo livello di personalizzazione dei componenti di
sistema è del tutto nuovo, e spalanca un nuovo mondo di possibilità per gli sviluppatori
di applicazioni. Al contempo, fornisce un eccellente punto di partenza per la scrittura di
nuovi provider ed anche per estendere il modello ai propri componenti. Se si osserva
ASP.NET 2.0 dalla prospettiva delle applicazioni esistenti, il modello di provider ottiene
anche più rilevanza tecnica poiché è la chiave per il riuso del codice e per una successiva conservazione degli investimenti in termini di programmazione e di tempo di
sviluppo. Come abbiamo evidenziato, un sistema di membership realistico in ASP.NET
1.1 richiede di dover fornire una propria API quando si tratta di validazione e gestione
dell’utente. Cosa si dovrebbe fare dopo aver deciso di eseguire l’upgrade a ASP.NET
2.0? Si deve scartare tutto questo codice per abbracciare la nuova impressionante API
di membership di ASP.NET 2.0? O si farebbe meglio a restare incollati all’antiquata e
proprietaria API di membership? Il modello di provider fornisce la risposta (ed è, effettivamente, una buona risposta) grazie alla straordinaria capacità di scambiare l’algoritmo
sottostante pur preservando il comportamento complessivo. Tuttavia, questa capacità
di per sé non sarebbe sufficiente. Si deve anche adattare il proprio codice esistente per
renderlo collegabile al nuovo ambiente runtime. In questo caso, un ulteriore pattern
popolare viene in soccorso: il pattern adapter. L’intento dichiarato del pattern adapter
è convertire una classe A in una interfaccia B che un client C comprende. Si racchiude il codice esistente in una nuova classe provider che può essere collegata in modo
trasparente nel framework ASP.NET 2.0 esistente. Si modifica l’implementazione sottostante della API di membership, e si utilizza il proprio schema e il proprio supporto
di memorizzazione pur lasciando intatta l’interfaccia di massimo livello. E, aspetto più
importante, si può riusare completamente il proprio codice.
Una rapida panoramica all’implementazione di ASP.NET
L’implementazione del modello di provider di ASP.NET consiste di tre elementi distinti: la classe provider, lo strato di configurazione e lo strato di storage. La
classe provider è il componente che si collega nel framework esistente per fornire
una funzionalità desiderata nel modo voluto. Lo strato di configurazione fornisce
informazioni utilizzate per identificare e istanziare il provider effettivo. Lo strato di
storage è il supporto fisico in cui sono memorizzati i dati. In base alla particolare
caratteristica, può essere Active Directory, una tabella Oracle o SQL Server, un file
XML, o qualcos’altro.
Capitolo 1 Il modello di programmazione ASP .NET
La classe Provider
Una classe provider implementa un’interfaccia nota ai suoi client. In questo modo, la
classe fornisce ai client la funzionalità promessa da quella interfaccia. Ai client non è richiesto
conoscere ogni aspetto sui dettagli di implementazione dell’interfaccia. Questa opacità del
codice permette la magia di far sì che del codice guidi dell’altro codice di cui non è neanche
consapevole. Nel modello di provider ASP.NET, la sola variazione rispetto alla definizione
originale del pattern strategy è che al posto delle interfacce vengono utilizzate le classi base.
In ASP.NET, una classe provider non può essere semplicemente qualsiasi classe che implementa una determinata interfaccia. In effetti, è vero piuttosto il contrario. Una classe provider
deve derivare da una classe base ben nota. Esiste una classe base per ciascun tipo supportato
di provider. La classe base definisce l’interfaccia di programmazione del provider attraverso
un gruppo di metodi astratti. Tutte le classi base provider derivano da una classe comune
denominata ProviderBase. Questa classe base fornisce un metodo overridable, Initialize,
attraverso cui l’ambiente di run-time passa qualsiasi impostazione pertinente dai file di configurazione. La Figura 1-8 riassume la gerarchia delle classi provider di membership.
Figura 1-8 La gerarchia delle classi provider di membership.
Interfacce vs. classi base
Alzi la mano chi, in qualità di sviluppatore, non è mai stato coinvolto in ore e ore di
dibattito sull’argomento interfacce versus classi base. È una discussione che raramente
giunge a un termine e lascia sempre chi proviene da fazioni differenti fermamente aggrappati alle rispettive posizioni. Si dovrebbero utilizzare le interfacce, o sono migliori
le classi base? Su quali considerazioni si basa la vostra risposta? Consideriamo, innan-
31
32
Parte I Realizzare una pagina ASP .NET
zitutto, la seguente realtà di fatto. Le versioni pre-beta di ASP.NET 2.0 implementavano il
modello di provider alla lettera rispetto alla definizione del pattern strategy: ossia, attraverso
interfacce. Al momento della Beta 1, le interfacce furono sostituite con classi base, e così è
nella versione rilasciata. Il team ASP.NET è giunto apparentemente a una conclusione sul
problema, o no? Un’interfaccia è una collezione di metodi logicamente correlati che contiene solo definizioni di membro e nessun codice. Un tipo interfaccia è una descrizione parziale
di un tipo, che più classi possono potenzialmente supportare. In altri termini, una buona
interfaccia è un’interfaccia che viene implementata da diversi tipi differenti e incapsula un
blocco di funzionalità, utile e generalizzato, che i client vogliono utilizzare. Ecco perché
molte interfacce terminano proprio con il suffisso “able”, come accade per IDisposable,
IComparable e IFormattable. Se un’interfaccia ha una sola classe di implementazione utile,
è probabilmente il frutto di una pessima scelta di design. Come regola pratica, delle nuove
interfacce devono essere introdotte con parsimonia e con una dovuta riflessione. Una classe
base definisce un comportamento comune e una comune interfaccia di programmazione
per un albero di classi figlie. Le classi sono più flessibili delle interfacce e supportano il versioning. Se si aggiunge un nuovo metodo alla versione 2.0 di una classe, ogni classe derivata
esistente continua a funzionare immutata, purché il nuovo metodo non sia astratto. Ciò
non è vero per le interfacce. Alla luce di queste considerazioni, la regola emergente è che
si dovrebbero utilizzare le classi base invece delle interfacce ogni qualvolta sia possibile (il
che non va interpretato come “utilizzare sempre le classi base”). A mio avviso, le classi base
appaiono come una scelta eccellente, per quanto concerne il modello di provider.
Lo strato di configurazione
A ciascun tipo di provider supportato viene assegnata una sezione nel file
di configurazione, dove viene impostato il provider di default della specifica
caratteristica e dove vengono elencati tutti i provider disponibili. Se il provider
espone proprietà pubbliche, i valori di default di queste proprietà possono essere
specificati attraverso attributi. Il contenuto della sezione viene passato come
argomento al metodo Initialize della classe ProviderBase: l’unico metodo che tutti
i provider hanno in comune. All’interno di questo metodo, ciascun provider utilizza
le informazioni passate per inizializzare il proprio stato. Ecco un’istantanea della
sezione di configurazione del provider di membership.
<membership defaultProvider=”AspNetSqlProvider”>
<providers>
<add name=”AspNetSqlProvider”
type=”System.Web.Security.SqlMembershipProvider, System.Web”
connectionStringName=”LocalSqlServer”
enablePasswordRetrieval=”false”
enablePasswordReset=”true”
requiresQuestionAndAnswer=”true”
...
passwordFormat=”Hashed” />
...
</providers>
</membership>
Capitolo 1 Il modello di programmazione ASP .NET
Lo strato storage
Tutti i provider devono leggere e scrivere informazioni su un supporto persistente di
memorizzazione. In molti casi, due provider dello stesso tipo differiscono solo per lo storage
che impiegano. I dettagli del supporto di storage vengono impacchettati negli attributi del
provider nella sezione <providers>, come mostrato nel precedente esempio di codice. Ad
esempio, il precedente provider AspNetSqlProvider è il provider di membership predefinito
che legge e scrive su una tabella SQL Server. La stringa di connessione del provider viene
specificata attraverso l’attributo connectionStringName, che a sua volta si riferisce a un’altra
sezione centralizzata dei file di configurazione che elencano tutte le stringhe di connessione
disponibili. Perché il provider possa operare, deve esistere ogni infrastruttura necessaria
(ossia, database, tabelle, relazioni). L’impostazione dell’ambiente di lavoro è un compito
tipicamente eseguito al momento del deployment. ASP.NET semplifica molto questo
compito grazie alla console di amministrazione del sito Web, mostrata nella Figura 1-9.
Figura 1-9
.NET 2005.
La console ASP.NET di amministrazione del sito Web invocabile da Visual Studio
Tipi di provider disponibili
Il modello di provider viene utilizzato per conseguire diversi compiti, i più
importanti dei quali sono i seguenti:
• L’implementazione di un meccanismo read/write per persistere il profilo utente
• La creazione di un repository definito dall’utente di credenziali utente che supporta
le più comuni operazioni, tra cui il controllo dell’esistenza di un utente, l’aggiunta
e la cancellazione di utenti, e la modifica delle password
• La creazione di un repository definito dall’utente per i ruoli utente
• La definizione della mappa del sito
• L’introduzione di tipi più recenti di storage dei dati per lo stato della sessione
33
34
Parte I Realizzare una pagina ASP .NET
La Tabella 1-1 mostra l’elenco delle classi provider disponibili in ASP.NET.
Tabella 1-1. Classi base provider ASP.NET disponibili
Classe
Descrizione
Classe base dei provider di membership utilizzata per gestire le
informazioni dell’account utente.
Classe base dei provider di personalizzazione utilizzata per persistere
ProfileProvider
e recuperare le informazioni sul profilo dell’utente.
Classe base dei provider di ruolo utilizzata per gestire le informazioni
RoleProvider
sul ruolo dell’utente.
Classe base dei provider di memorizzazione dello stato di sessione.
SessionStateStoreProviderBase Questi provider vengono utilizzati per salvare e recuperare le informazioni
sullo stato di sessione da supporti persistenti di memorizzazione.
MembershipProvider
SiteMapProvider
Classe base per la gestione delle informazioni sulla mappa del sito.
Le classi elencate nella Tabella 1-1 definiscono un metodo astratto per ciascun aspetto
personalizzabile della caratteristica che rappresentano. Ad esempio, riguardo alla gestione
della membership, la classe MembershipProvider espone metodi come ValidateUser,
CreateUser, DeleteUser, ChangePassword e via dicendo. Si noti che non si utilizzerà mai
MembershipProvider nel codice, proprio perché è una classe astratta. Invece, si utilizzerà
una classe derivata come SqlMembershipProvider o, probabilmente, ActiveDirectoryMem
bershipProvider. La stessa considerazione vale per altri tipi di provider.
Infine, se si deve scrivere un provider di membership custom che avvolge il proprio
codice esistente, se sono coinvolte altre caratteristiche basate sul provider si deve creare
una classe che deriva da MembershipProvider o da classi simili.
[Nota] L’architettura dei provider è una delle nuove caratteristiche più importanti di
ASP.NET 2.0 ed è anche una delle più delicate per quanto concerne le applicazioni. Per
impedire che gli sviluppatori producano provider difettosi, il team ASP.NET fornisce un apposito provider toolkit che dettaglia ciò che si può e non si può fare in un provider oltre a un
gran quantità di codice d’esempio da utilizzare come guida. La scrittura di provider custom
può essere complicata, almeno per un paio di motivi. Primo, i provider ASP.NET devono
essere thread-safe. Secondo, la fase di inizializzazione del provider può condurvi dritto dritto a una rientranza letale. Assicuratevi di scaricare il provider toolkit ASP.NET dall’ASP.NET
Developer Center prima di imbarcarvi in un nuovo progetto di provider.
Conclusioni
Essendo parte integrante del .NET Framework, ASP.NET permette di sfruttare
appieno il vantaggio delle caratteristiche del Common Language Runtime (CLR), tra cui
la type safety, l’ereditarietà, l’interoperabilità di linguaggio e il versioning. Essendo la
più recente piattaforma per le applicazioni Web, ASP.NET si basa sul successo di diverse
Capitolo 1 Il modello di programmazione ASP .NET
altre piattaforme, tra cui ASP classico, JSP e LAMP. ASP.NET promuove un modello di
programmazione che, benché sia implementato sul protocollo HTTP che è stateless,
appare ai programmatori come stateful e a eventi.
In questo capitolo, abbiamo prima descritto il modello di componente che supporta
le pagine Web ASP.NET e poi abbiamo analizzato lo stack di sviluppo dall’alto (strato di
presentazione e controlli sofisticati) in basso (infrastruttura e provider). Il modello di
provider, in definitiva un’implementazione del pattern strategy, è un elemento chiave
nella nuova architettura ASP.NET e un pilastro di supporto per le nuove applicazioni.
Applicato in modo estensivo, permette di personalizzare diversi aspetti di basso livello
dell’ambiente di run-time dell’applicazione e di riusare grosse porzioni del codice
esistente. Compreso a fondo, fornisce un modo per implementare nuovi componenti
flessibili e estensibili al di là di ogni immaginazione e, in quanto tali, collegarli in modo
omogeneo in una varietà di progetti e più facili da personalizzare da parte dei client.
In sintesi
•
In ASP.NET, si sfrutta appieno il vantaggio di ogni caratteristica del CLR, tra cui la
type safety, l’ereditarietà , la Code Access Security e l’interoperabilità tra linguaggi
•
In fase di esecuzione, le pagine ASP.NET vengono rappresentate da una istanza di
una classe che deriva dalla classe Page
•
La classe Page è il punto terminale di una pipeline di moduli che processano ogni
richiesta HTTP
•
Solo agli elementi di una pagina ASP.NET contrassegnati con l’attributo runat si può
accedere da programma quando la pagina viene eseguita sul server
•
Gli elementi di pagina privi dell’attributo runat non vengono processati sul server e
vengono emessi verbatim
•
L’attributo runat si applica virtualmente a ogni tag possibile che si può utilizzare in una
pagina ASP.NET, compresi i tag custom e quelli incogniti
•
Il modello di processo è la sequenza di operazioni necessarie a processare una
richiesta. Il modello di processo è determinato da IIS e determina quale processo
worker si fa carico dell’esecuzione delle applicazioni ASP.NET e sotto quale account
•
Le applicazioni ASP.NET vengono eseguite con un account non privilegiato
•
Il comportamento delle applicazioni ASP.NET può essere configurato per mezzo di un
gruppo di file d configurazione
•
Il modello di provider ASP.NET è una caratteristica infrastrutturale che migliora
l’architettura di una applicazione abilitando gli sviluppatori e gli architetti a operare
dietro le quinte di alcuni componenti di sistema
•
Il modello di provider ASP.NET comporta due principali vantaggi: una vasta
personalizzazione dell’ambiente run-time dell’applicazione e la riusabilità del codice
35