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:&nbsp;</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