MASSIMO UBERTINI SERVER SIDE WWW.UBERTINI.IT MODULO 4 WEB Applicazioni web Server DB Server web Sicurezza dei server web DB centrale e DB remoti RIA LightSwitch Server side um 1 di 243 APPLICAZIONI WEB Introduzione Il primo problema da affrontare quando s’inizia a sviluppare un’applicazione web a contenuto dinamico è che tecnologia utilizzare: le più conosciute sono quattro. 1. La piattaforma .NET di Microsoft, programmabile per applicazioni web con il suo linguaggio ASP.NET (Active Server Page) e utilizzabile con il server web IIS (Internet Information Server). 2. La piattaforma Java, programmabile con il linguaggio JSP (JavaServer Pages) e tramite blocchi di codice Java chiamati Servlet e utilizzabile con il server web Tomcat. 3. La piattaforma AMP (Apache MySQL PHP), programmabile con il linguaggio PHP (Hypertext PreProcessor) e utilizzabile con il server web Apache. 4. La piattaforma MX, programmabile con un linguaggio a TAG chiamato CFML (ColdFusion Markup Language) e utilizzabile con il server web ColdFusionMX di Macromedia. LINGUAGGI Il browser invia una richiesta al server web, quest’ultimo si occupa d’interpretare le richieste contenute all’interno della pagina e scritte con un linguaggio server side e produce una pagina HTML (HyperText Markup Language) che è rimandata al client. Il browser da solo non è in grado d’interpretare una pagina scritta in linguaggio server side ma solo e soltanto puro HTML, con qualche eccezione. Al contrario, non esiste un modo per il server d’interagire con il browser, ad esempio non c’è modo per il server di accorgersi che l’utente ha digitato qualcosa in una casella di testo, oppure se ha ridimensionato la finestra del browser stesso. Certo, è sempre possibile effettuare dei PostBack, inviando la pagina al server come quando si compila il modulo e si fa clic sul pulsante Invia ma ciò richiede continui reload e questo non è certamente possibile e accettabile in molti casi. Basti pensare all’evento MouseMove, se ad ogni spostamento del mouse, si volesse informare il server della nuova posizione del puntatore, la pagina sarebbe certamente bloccata in continui PostBack e non più utilizzabile. Per questo tipo d’interazione, esistono i linguaggi client side che sono eseguiti nel browser dell’utente e dunque possono reagire immediatamente alle azioni del mouse. I linguaggi utilizzati nelle applicazioni sono di due tipi. Primo Tipo Alcune operazioni, quali ad esempio l’accesso a DB (DataBase) o scrittura di file, è possibile effettuarle solo avvalendosi di linguaggi di scripting server side, mentre per l’interazioni con gli elementi della pagina è più conveniente utilizzare linguaggi client side. Client side Permettono d’interagire con gli elementi HTML componenti la pagina senza dover scambiare i dati con il server. Questo permette di ridurre i tempi di esecuzione delle istruzioni. Sito web statico: richiede l’uso di linguaggi client side. Il funzionamento di un sito statico è semplice: il browser richiede una pagina, il server web la legge nel file system e la invia in risposta al browser. Vantaggio: non si occupano le risorse del server. Svantaggi: browser sempre aggiornati, problemi di compatibilità. Server side um 2 di 243 Applet Java Sono classi Java che sono avviate a partire da una pagina HTML, quando il browser incontra un TAG che richiede la visualizzazione di un’applet ne scarica il codice e lo esegue visualizzando l’output nella finestra del documento. Per poter eseguire le applet il browser dev’essere dotato della JVM (Java Virtual Machine). Le applet sono utilizzate per realizzare grafica, per gestire l’interazione con l’utente e per creare animazioni, hanno alcune limitazioni per motivi di sicurezza. Non possono leggere e scrivere file su disco, per non veicolare virus; possono solo salvare informazioni sul PC da cui proviene la pagina web. Non possono aprire nuove connessioni di rete per stabilire collegamenti con altri PC. Per eseguire un’applet in una pagina HTML si usa il TAG <applet>. <applet width="128" height="128" code="prima.class"></applet> ActiveX Tecnologia sviluppata da Microsoft su OLE (Object Linking & Embedding) e COM (Component Object Model), non è un linguaggio di programmazione ma un insieme di specifiche che consentono la cooperazione tra applicazioni a livello binario, quindi un ActiveX può essere sviluppato con diversi linguaggi. DOM (Document Object Model) Modellizza i documenti HTML e fornisce un’interfaccia di programmazione per accedere, navigare e manipolare le pagine. JavaScript/TypeScript Server side Sono interpretati dal server che esegue le istruzioni e presenta i dati. È compito del programmatore manipolare i dati dinamici e presentarli sotto forma di codice HTML. Sito web dinamico: richiede l’uso di linguaggi server side. Si utilizza codice di programmazione all’interno di una pagina HTML nei punti in cui si desidera interagire con l’utente, con le fonti dati o con il sistema per stabilire cosa sarà mostrato al client. Il compito del server in questo contesto è quello di fornire al client la pagina indicata nella request previa esecuzione, sul server, del codice di scripting. Vantaggio: non dipendono dal browser che li richiama, di conseguenza, il codice server side è del tutto invisibile al client che accede soltanto all’output dello script, in pratica la pagina generata dalla sua esecuzione. Svantaggi: server potente. CGI (Common Gateway Interface) ISAPI (Internet Server Application Programming Interface) Nel periodo iniziale d’Internet le applicazioni web erano implementate con programmi scritti in C o PERL (Practical Extraction and Report Language) che si appoggiavano ad un’API (Application Programming Interface) dei server web, la CGI o ISAPI che richiedevano l’avvio di una nuova istanza di un’applicazione ogni volta che si doveva elaborare una richiesta: rischi per la sicurezza. Un browser può, così, inviare una richiesta ad un server il quale, invece di rispondere con una pagina statica, può eseguire uno script e inviare un flusso di dati HTML creato al momento. Nel server web, al di sotto della radice del sito, esiste una cartella di nome CGI-BIN, i file Server side um 3 di 243 presenti sono di tipo eseguibile, per esempio EXE. Lo svantaggio di queste tecnologie risiede nella difficoltà di realizzazione e di gestione delle applicazioni, perché le applicazioni CGI non sono integrate nei file HTML e richiedono conoscenze e un processo di sviluppo completamente diversi da quelli necessari per realizzare pagine HTML. Inoltre, trattandosi di tecnologie che si basano interamente sul server, il carico su quest’ultimo aumenta, portando ad un progressivo degrado delle prestazioni. Servlet Java Il codice Java non è più eseguito nel browser dei client come nel caso delle applet ma è eseguito su una JVM residente su un server. Bisogna separare la business logic dall’UI (User Interface). JSP Permette d’includere codice Java direttamente nelle pagine web e supporta componenti chiamati JavaBeans che nascondono funzionalià complesse al di là di un’interfaccia semplice. AJAX (Asynchronous JavaScript And XML [eXtensible Markup Language]) È la tecnologia che sfrutta le capacità dei browser di “capire” l’XML per creare applicazioni web dinamiche, per esempio permette d’inserire dinamicamente dati all’interno di elementi della pagina, prelevandoli da origini esterne. AJAX non è né un nuovo linguaggio di programmazione né una nuova piattaforma tecnologica per lo sviluppo web, è soltanto il nome di una particolare maniera di usare insieme una serie di tecnologie web: JavaScript, HTML, CSS (Cascading Style Sheets) e XML, miscelandole però in una maniera nuova. ASP.NET Scripting server side in IIS, possibilità di scrivere le applicazioni usando qualunque linguaggio di Visual Studio e altri. PERL Le estensioni di ActiveState che interpretano PERLScript. PHP Il modulo per Apache che implementa il linguaggio PHP. ColdFusion Di Macromedia, prima soluzione commerciale. Secondo tipo Sono orientati ai componenti. EJB (Enterprise JavaBeans) COM+ CORBA (Common Object Request Broker Architecture) È la specifica di un’architettura standard per ORB, ovvero intermediari di richieste a oggetti. .NET Framework Tecnologia Microsoft per sviluppare applicazioni distribuite in rete tramite server web, centrate sul server e indipendenti dal browser. Server side um 4 di 243 Web Service Sono componenti S/W in rete in grado di fornire dati e servizi ad altre applicazioni. CMS (Content Management System) Applicazioni che organizzano, gestiscono e pubblicano contenuti web. Anche se è più facile sviluppare e installare siti dinamici con i server basati su tecniche di scripting, i sistemi di questo tipo uniscono il codice usato per la visualizzazione della pagina, la presentation logic, con quello che implementa, invece, la business logic che normalmente preleva o inserisce in un DB le informazioni opportune. Ciò dà origine a codice molto contorto e a volte impossibile da aggiornare e mantenere in efficienza. I server applicativi di fascia più elevata usano, invece, un modello di sviluppo orientato ai componenti che separa la business logic dalla presentation logic. Questa divisione in compartimenti consente ai programmatori di ristrutturare rapidamente anche i siti di più grandi dimensioni: si possono, infatti, aggiungere nuovi componenti ogni qualvolta se ne presenta la necessità e non è necessario ricostruire l’intero sistema. I server applicativi sono affidabili, supporto per il clustering, il carico di lavoro è ripartito tra più macchine e scalabili, gestiscono un aumento del carico. In molti casi è opportuno che il server s’interfacci con il server di directory aziendale, di solito basato su LDAP (Lightweight Directory Access Protocol) o su Active Directory di Microsoft. Esistono due tipi di applicazioni web. 1. SERVER BASED WEB APPLICATION Un’applicazione web interagisce con i dati mediante tecnologia server side. Il client, browser, si limita a richiedere una determinata pagina ma il vero lavoro è svolto dal server. 1. Interpreta i parametri passati dal client con l’URL (Universal Resource Locator), GET o con una form, POST. 2. Compie le azioni associate ai parametri. 3. Recupera i dati dal DB. 4. Formatta i dati in HTML dinamicamente. 5. Restituisce i dati già formattati al client. In pratica, sia il back end sia il front end dell’applicazione sono demandati al server. Limitazioni: il server è uno, i client sono molti. Quindi, più sono i client, più sono le risorse richieste al server che deve fare tutto il lavoro. In questi casi, all’aumentare degli utenti dell’applicazione web si può far fronte solo in un Server side um 5 di 243 modo: aumentare connettività e potenza del server. Il rapporto tra l’utente di un’applicazione web e il server che la ospita è soltanto di tipo sincrono, perché ogni operazione, prima di cominciare, attende il completamento di quella che la precede: all’inizio è il server che resta fermo in attesa del client; poi tocca al client aspettare, visto che dopo aver premuto il tasto INVIO non si può fare altro che attendere l’esito dell’elaborazione. 2. CLIENT BASED WEB APPLICATION Chiede al server web una risorsa ASP, JSP, JPG (Joint Photographic Group), una pagina HTML; il client indica la risorsa voluta attraverso l’URL, il browser prepara la richiesta secondo le regole del protocollo HTTP (HyperText Transfer Protocol). Se l’URL contiene www.miosito.it, il browser chiede allo strato rete di risolverlo al fine di ottenere l’equivalente indirizzo IP. Il browser inoltra al server web la richiesta formulata precedentemente, per farlo si appoggia sullo strato di rete, il TCP/IP (Transmission Control Protocol/Internet Protocol). Se individuato, il server web recepisce la richiesta ed effettua quanto chiesto; per farlo potrebbe anche fare cose molto complesse: per esempio, accedere ad un DB; prepara la risposta, codice HTML e lo rimanda indietro al browser che ne ha fatto richiesta. Anche la risposta è codificata secondo le regole del protocollo HTTP, il browser, una volta ottenuta la risposta, provvede ad elaborarla facendo il rendering del codice HTML. Non è rilevante la piattaforma H/W e/o SW su cui gira il browser. I browser non sono solo viewer di documenti HTML ma sono in grado di aprire immagini, file TXT, XML, documenti Word e PDF (Portable Document Format). Per gli altri formati si appoggiano su plugin, per esempio SWF (ShockWave Flash). Supportano l’interazione del codice JavaScript con gli oggetti HTML. I browser, con il supporto di JavaScript, la possibilità d’interagire con il DOM di HTML e le capacità di accedere a dati, sono diventati un soggetto attivo dell’applicazione web, un vero e proprio front end dell’applicazione. Questa funzionalità permette di manipolare la pagina attraverso il DOM, modificandone di volta in volta solo singole parti: è lo scambio “asincrono”, evitando il modello di PostBack dove la pagina è aggiornata nella sua totalità. Ciò ha portato a progettare applicazioni che lasciano al server le operazioni di back end e demandano al browser la costruzione dell’UI e le altre operazioni client. In un flusso di questo tipo si nota. 1. Il client richiede al server dei dati. 2. Il server recupera i dati e li restituisce in formato XML. 3. Il client elabora questi dati e li utilizza per costruire dinamicamente l’UI. I benefici che si ottengono sono di due tipi. 1. Si alleggerisce il carico del server esimendolo dal dover costruire la pagina HTML e Server side um 6 di 243 quindi risparmiando banda e risorse server. 2. Si possono compiere diverse operazioni anche senza ricaricare l’intera pagina ma richiedendo solo i dati che servono. In AJAX queste operazioni sono svolte in maniera asincrona, sfruttando una particolare funzionalità di JavaScript, è possibile fare in modo che non ci sia sempre bisogno di caricare nuove pagine all’interno del browser del visitatore. Il dialogo con il server, infatti, può anche avvenire in sottofondo. Il documento HTML caricato la prima volta è modificato di continuo, per interagire con l’utente e mostrargli nuove informazioni. Le Client Based Web Applications si basano su due pilastri fondamentali. 1. Server side L’attività di programmazione è veramente minimale, si tratta di fornire al client i dati nel formato richiesto, generalmente XML. Il client richiede, ad esempio, i dati di una tabella, nel sito ci sarà una pagina ASP.NET, PHP o altro che di occupa di leggere i dati dal DB e restituirli nel formato richiesto; ma se XML è il formato di elezione per questo tipo di colloqui, la tecnologia server side che “parla” in XML è il web service, oltretutto, utilizzandolo si ha anche il vantaggio di poter riutilizzare le stesse funzioni di accesso ai dati anche con applicazioni desktop. 2. Client side Come richiamare e utilizzare i dati con il client e quindi con JavaScript. Il fulcro della tecnologia è rappresentato da due oggetti. 1. XMLHttpRequest: si occupa di recuperare i dati da un’URL con i metodi GET o POST. 2. XMLDOMParser: si occupa di leggere un documento XML. MODELLI DI PRESENTAZIONE E DI PROGRAMMAZIONE In una programmazione in stile classico si è abituati a considerare l’elaborazione, l’output della pagina HTML, come una parte della definizione del codice di scripting. Questo ha lo svantaggio d’impedire ad un web designer di lavorare separatamente dal programmatore. La sintassi HTML con dei TAG protetti dalle ({}) è gestita anche da chi non ha conoscenza di tecniche di scripting, utilizzando questa tecnica programmatore e web designer possono lavorare in modo separato. Le tecnologie di sviluppo su browser sono CSS/DHTML (Dynamic HTML) nel modello di presentazione e JavaScript/AJAX/ASP.NET nel modello di sviluppo. Sul desktop, XAML (eXtensible Application Markup Language) fornisce il modello di presentazione e il .NET Framework fornisce il modello di sviluppo. Vi è una sovrapposizione tra queste ed è qui che il browser dotato di Silverlight fornisce il seguente approccio: “il meglio di entrambi i mondi”. Server side um 7 di 243 Microsoft Silverlight Permette di progettare applicazioni web con la stessa qualità delle applicazioni desktop, colmando il gap tecnologico tra programmatori e web designer fornendo loro un formato comune, XAML, con cui lavorare. L’applicazione può essere progettata come XAML e siccome XAML è un linguaggio basato su XML e poiché XML è solamente testo, l’applicazione è firewall compatibile e accessibile ai motori di ricerca. Il browser riceve il markup XAML e lo visualizza, se combinato con una tecnologia come AJAX, JavaScript e il .NET Framework: questo processo può essere dinamico. L’architettura di un’applicazione in esecuzione sul browser è la seguente. Server side um 8 di 243 SERVER DB Introduzione Quello di rendere accessibili i dati da qualunque parte del mondo in cui ci si trovi è il sogno di tutti i programmatori ma il compito è sempre risultato improbo. Problemi di compatibilità tra protocolli e problemi di sicurezza non trascurabili hanno sempre tarpato le ali a progetti di fruibilità diretta dei dati attraverso un qualsiasi canale. Con l’avvento d’Internet, il protocollo HTTP si è imposto come standard e insieme a questo protocollo sono nati anche i server web. Non si può connettere il browser di un utente web remoto direttamente ad un sistema DB, anche se ci sono eccezioni a questo, le AppletJava ma nella maggioranza dei casi il browser dialoga con un’applicazione che gira su un server web che ha funzioni d’intermediario rispetto al DB. Quest’applicazione può essere uno script CGI, una servlet Java o una parte di codice che sta all’interno di un documento ASP.NET o JSP. L’utente non sa se la pagina è un documento HTML ordinario oppure l’output di un qualche script che genera l’HTML. A seconda del tipo di tecnologia che si usa ci sono quattro possibilità. 1. Chiamata ad una pagina PHP: è la soluzione migliore, si può far ritornare alla pagina un documento XML con le informazioni che servono. 2. Chiamata ad un web service ASP.NET: è la soluzione più semplice. 3. Espressione regolare sulla pagina web: è la soluzione più costosa ma anche la più instabile, infatti è frequente che le pagine di risultati siano aggiornate sia nel markup sia nei CSS, inclusi i nomi delle classi, questo significa modificare l’espressione regolare ad ogni cambio pagina e aggiornare in qualche modo il prodotto distribuito; inoltre, bisogna anche tener conto del fatto che se si mette la regexpr in un file di configurazione, un utente potrebbe provare a modificarla. 4. Utilizzare le API del servizio: è la soluzione ottimale, significa interfacciarsi alle API e richiamarle secondo gli schemi forniti. L’architettura web/DB si sviluppa verso diverse direzioni. Soluzione Open Source LAMP (Linux AMP* si può sostituire con PERL, Python o PHP). WAMP (Windows AMP*). Soluzione Microsoft ASP.NET e ADO.NET. query SQL L’esecuzione di una query è una delle attività più importanti per un DB. Ogni richiesta da parte di un utente che implichi l’interazione con il DB dà il via ad una query. 1. L’utente compila un form per ricercare in una libreria online i libri scritti da un certo autore. 2. Ricevuta la richiesta da Internet, un’applicazione sul server estrae i campi del modulo per costruire una query che è inviata al DB tramite un driver. 3. Una volta ricevuta la query dal server, il DB può applicare svariate tecniche di ottimizzazione delle prestazioni per recuperare rapidamente i dati richiesti: un indice organizzato come struttura gerarchica ad albero, tecnica di parallelismo intraquery che sfrutta più di una CPU per eseguire una singola query, query rewrites, analizzano le Server side um 9 di 243 query in arrivo e le riscrivono dinamicamente usando tecniche avanzate. 4. La query è elaborata. 5. Il DB restituisce i risultati della query al server che li riformatta e l’inserisce in una pagina HTML che poi invia all’utente. Rendere sicure le applicazioni web Gli attacchi DOS (Denial Of Service) approfittano del fatto che i router, sprovvisti di adeguate misure di filtraggio, trasmettono il traffico dati senza controllare adeguatamente l’indirizzo IP di provenienza, quello di destinazioni o la tipologia dei dati trasmessi. I socket sono un’API standard che i programmatori usano per stabilire connessioni Internet. Un socket standard collega un’applicazione allo strato IP del SO (Sistema Operativo) tramite il livello di protocollo TCP o UDP (User Datagram Protocol) che predispone i dati in un formato standard e crea gli opportuni pacchetti di controllo. I raw socket a tutti gli effetti mettono a disposizione una backdoor per l’accesso diretto al sistema di rete sottostante, consentendo ai programmatori di creare pacchetti e protocolli a loro piacimento. Un’applicazione dannosa potrebbe usare i raw socket per impersonare l’indirizzo di un’altra macchina, in modo che non sia possibile risalire all’indirizzo effettivo del PC che sta generando i dati, raw socket sono stati implementati in origine nel SO Unix, dove risultano accessibili solo alle applicazioni che lavorano con i privilegi di root. Quando un malintenzionato prende di mira un’applicazione web, generalmente si pone quattro domande. 1. Posso vedere ciò che gli utenti stanno guardando? Il metodo per prevenire che altri possano leggere i dati in transito è la cifratura della connessione fra un sito e i suoi utenti. 2. Posso impersonare un utente? Come fa un sito web a riconoscere i propri utenti? La maggior parte dei server web supporta due schemi di autenticazione tramite password. 1. Semplice da implementare ma anche meno sicuro. 2. Robusto, basato sul cosiddetto digest. Entrambi gli schemi operano mandando una “sfida” al browser: quando il browser la riceve per la prima volta, fa comparire una finestra di dialogo che richiede il nome utente e la password. Usando il metodo di autenticazione più semplice il browser trasmette al server il nome utente la password sotto forma di testo semplice e in chiaro, in altre parole non cifrato. Con il secondo metodo di autenticazione il browser trasmette, invece, un particolare testo cifrato, detto appunto digest, ottenuto a partire dal nome utente e dalla password. In entrambi i casi il server verifica i dati ricevuti e, se sono corretti, risponde con un messaggio di approvazione; il browser a sua volta memorizza in cache le informazioni di logon per tutta la sessione di lavoro in modo che l’utente non debba reinserirle ad ogni pagina visitata. Il primo problema riguarda ancora lo spionaggio dei dati in transito: se gli utenti mandano il nome utente e password in chiaro, un attaccante può catturarli. Questo problema può essere risolto in modo semplice trasmettendo informazioni dell’utente usando l’SSL (Secure Sockets Layer). Il codice seguente mostra come farlo. <form action = "https://foo.com/login.asp" method = "post "> User ID:<input type = "text" name = "user"> <br>Password:<input type = "password" name = "password"> Server side um 10 di 243 <br><input type = "submit" value = "Login"> </form> Non potendo spiare le comunicazioni, è possibile impersonare un utente. Il punto debole che rende questo possibile è proprio lo stesso utente. La maggior parte dei navigatori del web scelgono password non sicure e, ancora peggio, tendono a usare la stessa coppia nome utente/password per tutti siti che richiedono una procedura di logon. 3. Posso impersonare il gestore del sito? Non c’è connessione permanente fra il browser e il server: è stabilita una connessione separata per ogni pagina richiesta. Come fa quindi un server web a verificare le credenziali dell’utente dopo il logon iniziale? Il browser memorizza il nome utente e la password per tutto il tempo in cui la propria finestra rimane aperta e li ritrasmette al server ogni volta che si ricollega ad esso. Il server quindi confronta ogni volta le informazioni ricevute con quelle presenti nel suo DB degli utenti e consente o nega l’accesso a seconda che esista o meno una corrispondenza. Il DNS (Domain Name System) è un anello vulnerabile della catena della sicurezza: se un malintenzionato riuscisse ad accedere al server DNS e ne modificasse i registri facendo in modo da farli puntare a uno dei propri PC invece che a quelli legittimi, tale PC potrebbe poi reindirizzare tutte le richieste HTTPS (Hypertext Transfer Protocol over Secure Socket Layer). Dato che durante il reindirizzamento il browser mostra di default la parte finale dell’indirizzo, se quest’ultimo è abbastanza lungo da mettere la porzione fuori dal campo di vista, la maggior parte degli utenti non noteranno di essere collegati ad un sito dal nome diverso da quello desiderato. Se il gestore ottenesse poi un certificato digitale, il browser indicherebbe una connessione del tutto lecita e non darebbe alcun messaggio di avviso. Solo facendo clic sull’icona a forma di lucchetto per visionare i dettagli del certificato digitale l’utente potrebbe rendersi conto dell’inganno. Dato che è molto facile costruire una pagina web identica a un’altra, il malintenzionato a questo punto potrebbe realizzare una schermata di accesso simile in tutto e per tutto e usarla per catturare nomi utente e password, inserite dai visitatori ignari di essere collegati a un sito di un pirata informatico. 4. Posso usare i PC del sito per eseguire i miei comandi? Un tipo di attacco molto noto è quello del buffer overflow, letteralmente tracimazione del buffer che prevede l’invio di una quantità eccessiva di dati al server. Il seguente codice C è vulnerabile ad un attacco di buffer overflow perché non controlla se i dati in arrivo superano il limite previsto di 255 caratteri. void prova(char*pszData) { char szBuffer [255 ]; strcpy(szBuffer,pszData ); } Cosa accade se la funzione strcpy provoca un overflow del buffer basato su stack? La figura mostra l’aspetto dello stack delle chiamate di sistema dopo che è stata eseguita la funzione prova: lo stack cresce a partire dalla parte alta della memoria e il buffer si riempie dal fondo della memoria. Scrivendo troppi dati all’interno del buffer, un attaccante può arrivare a sovrascrivere il cosiddetto function call record: si tratta di una struttura di dati che contiene i valori del registro salvati dal codice della funzione insieme con l’indirizzo di ritorno della funzione Server side um 11 di 243 stessa. Sovrascrivendo quest’ultimo con un altro valore diventa possibile mandare in esecuzione sul PC attaccato un’applicazione a piacere. Un pirata informatico naturalmente vorrà mandare in esecuzione il codice da lui stesso caricato sul sistema ma come può riuscire ad introdurlo sul PC attaccato? Scrivendolo nel buffer dei dati. Gli hacker sanno che le funzioni vulnerabili, come quella appena mostrata, sono di solito richiamate dal codice che elabora l’input dell’utente. Di conseguenza provano a mandare stringhe d’input lunghissime al server e stanno a vedere che cosa succede. Come si può evitare che un’applicazione web sia sfruttata illecitamente? Bisogna cercare tutto il codice esistente che è stato scritto usando linguaggi che permettono un accesso diretto alla memoria, come il C, il C++ ed esaminarlo per trovare eventuali vulnerabilità, per esempio errori di buffer overflow. Si può evitare completamente il problema usando linguaggi di programmazione che non consentono accesso diretto alla memoria. Per esempio meccanismi di scripting come JavaScript e PERL. Linguaggi interpretati come Java. Sono altrettanto sicuri i linguaggi della piattaforma .NET, il Visual C# e il Visual Basic. Server side um 12 di 243 SERVER Web INTRODUZIONE È un S/W residente sul server, più precisamente un processo che gira in background, un servizio, in grado di evadere richieste HTTP. I server web sono tipicamente multitask/multithread e quindi in grado di evadere più richieste contemporaneamente e possono gestire diversi domini. Inoltre, bisogna determinare quanti browser possono avere accesso al sito contemporaneamente e con quale velocità otterranno risposta. All’avvio apre un socket TCP/IP sulla porta 80 (default), sul quale rimane in ascolto delle richieste dei browser, i server web sono chiamati a svolgere una varietà di ruoli che vanno oltre la semplice fornitura ai browser di pagine e immagini. Il lavoro essenziale svolto da un server web è semplice: accetta la richiesta di un agente, il browser, determina se concedere o meno l’accesso all’elemento desiderato e poi risponde, a seconda dei casi, inviando un file che risiede nel file system, passando la richiesta stessa a una routine per la generazione di codice dinamico o restituendo un messaggio di errore. I server web sono in grado d’interpretare linguaggi server side in modo da rendere la pagina web dinamica nel contenuto. Architettura Modello Forked Usa un processo dedicato d’I/O per ogni richiesta di connessione. Pone, però, un grosso carico sulle risorse del sistema e ha un limite prefissato sul numero di connessioni per porta. Modello Select Chiamato anche modello Event-Driven, azionato dagli eventi, usa un sistema d’I/O non bloccante nel quale un piccolo numero di processi a thread unico sfruttano la chiamata di sistema SELECT per gestire un gran numero di connessioni. Modello Multithread Ogni processo sfrutta un pool di thread di esecuzione. Per conservare le risorse del sistema, un algoritmo specializzato distribuisce il tempo di elaborazione della CPU tra i vari thread. Il modello d’implementazione del multithreading nel SO usato influisce sulle prestazioni, usato da IIS. Il web non conosce orari di chiusura! Come fare, allora, per tenere aperte le porte virtuali del sito 24 ore al giorno per 7 giorni la settimana? La risposta è nei sistemi ad alta disponibilità che si avvicinano al 100% di uptime. I principi dell’alta disponibilità definiscono livelli precisi di backup e di recovery. Fino a poco tempo addietro, l’alta disponibilità implicava semplicemente la capacità di recupero H/W e/o S/W tramite la tecnologia RAID (Redundant Array of Indipendent Disks) che permette di ottenere una condizione di fault tolerance per i dati ma non risolve il problema nel caso di un collasso completo del DB. Per raggiungere un uptime superiore, gli amministratori di DB usano il clustering che opera associando server che hanno la capacità di condividere un gruppo di unità disco. Server side um 13 di 243 Ogni nodo ha nel suo cluster un nodo di backup. In caso di guasto del nodo uno, il nodo due è in grado di subentrare istantaneamente prendendosi carico delle risorse, della logica e delle funzioni transazionali del DB in avaria. Un vantaggio aggiuntivo del clustering è che i nodi non devono necessariamente risiedere nello stesso luogo ma possono essere distanti anche numerosi chilometri tramite connessioni in fibra ottica. Le tecnologie di clustering sono costose, una soluzione alternativa e molto efficace dal punto di vista economico è quella del log shipping, spedizione dei log che prevede la sincronizzazione di DB separati tramite l’invio dei log delle transazioni da un server all’altro. In caso di guasto, si possono usare i log per ripristinare la situazione fino al momento dell’avaria. Altri sistemi prevedono le copie snapshot dei DB, “istantanee” effettuate in momenti prestabiliti o l’uso di tecnologie di replica. Come lavora un server. Server side um 14 di 243 IIS IIS 6.0 Architettura a struttura monolitica. IIS 7.0 Architettura a struttura modulare. Server side um 15 di 243 IIS 7.0 utilizza lo stesso processo per diverse request. FTP: permette FTPS over SSL da non confondere con SFTP over SSH (Secure SHell). Media Services: gestione contenuti multimediali senza Windows Media Server. Installazione IIS comprende il server web, FTP, NNTP (Network News Transfer Protocol) e SMTP solo per l’invio di posta. È possibile installare IIS, nonché aggiungere o rimuovere componenti facoltativi di IIS. Prima d’installare IIS, è necessario installare le utilità Windows per la connettività, il protocollo TCP/IP, il servizio DNS e le estensioni web per creare e modificare pagine HTML per il sito web. 1. Start/Pannello di controllo/Programmi/Programmi e funzionalità. 2. Fare clic su Attivazione o disattivazione delle funzionalità di Windows, è visualizzata la finestra Funzionalità Windows. Per controllare il funzionamento del server web basta inserire nella barra degli indirizzi del browser il seguente indirizzo. http://localhost/iisstart.htm o http://127.0.0.1/iisstart.htm Essendo il localhost, 127.0.0.1, il nome che identifica il server web nel PC locale. In output compare la pagina IISSTART.HTM che conferma l’attivazione del server web, è una home page di default che si trova nella cartella C:\INETPUB\WWWROOT. IIS permette una gestione integrata tramite lo snapin seguente. Start/Tutti i programmi/Strumenti di amministrazione/Gestione IIS. Per avviare il servizio web da console, fare clic su Start/Esegui… cmd. Server side um 16 di 243 C:\>net start w3svc <INVIO> Ulteriori informazioni sono disponibili con i comandi seguenti. C:\>net helpmsg 2182<INVIO> C:\>net start<INVIO> Per far sì che le applicazioni siano gestite correttamente da IIS, i file che le compongono devono essere correttamente collocati all’interno delle cartelle del server. Il processo d’installazione crea una cartella INETPUB e, al suo interno, la sotto cartella WWWROOT per le pagine web. Estensioni del servizio web È un tipo di documento o di funzionalità non statica che richiede un’elaborazione speciale. Le estensioni che possono essere configurate su IIS includono le seguenti caratteristiche. ASP: controlla se possono essere usati gli script server side scritti come ASP, questa estensione è sempre installata. ASP.NET: controlla se possono essere usate le applicazioni ASP.NET, questa estensione è resa disponibile al momento dell’installazione di ASP.NET quale componente del server applicazioni web. Estensioni Server BITS (Background Intelligent Transfer Service): controlla se può essere usato il trasferimento Internet in background, questa estensione è resa disponibile quando s’installa l’estensione BITS come componente d’IIS. Servizio d’indicizzazione: controlla se il contenuto del server web possa essere indicizzato e soggetto a ricerche, questa estensione è resa disponibile quando s’installa il servizio d’indicizzazione come componente di Microsoft Windows. Internet Data Connector: controlla se le pagine possono utilizzare Internet Data Connector, questa estensione è sempre installata. Stampa Internet: controlla se il server può essere usato per la stampa Internet, questa estensione è resa disponibile quando s’installa Stampa Internet come componente d’IIS. SSI (Server Side Includes): controlla se le pagine possono utilizzare le SSI, questa estensione è sempre installata. WebDAV (Web Distributed Authoring and Versioning): controlla se le pagine possono utilizzare WebDAV, questa estensione è sempre installata. Server side um 17 di 243 Configurazione Nel file MACHINE.CONFIG sono impostati i parametri relativi a tutto il server su cui sono in esecuzione i siti web. Nel file WEB.CONFIG è specificata l’intera configurazione di un sito ASP.NET, dalle stringhe di connessione fino alle impostazioni di protezione delle risorse. La sezione <system.webServer> ospita tutte le configurazioni dell’applicazione, trovano posto tutte le impostazioni che potrebbero essere fatte attraverso la console di gestione d’IIS che in effetti ora scrive direttamente dentro il file APPLICATIONHOSTING.CONFIG. All’interno di questo file sono definite le policy globali, così come eventuali application pool, siti web e impostazioni di quest’ultimo. Ovviamente il livello cui sono applicati è il medesimo: eventuali impostazioni locali sovrascrivono quelle dell’APPLICATIONHOSTING.CONFIG. Il vantaggio di questo approccio basato sulla possibilità di stabilire le impostazioni nel WEB.CONFIG è soprattutto nella possibilità di poter definire le impostazioni del sito durante la fase di sviluppo e poi portarle, insieme a tutta l’applicazione, in produzione, senza dover toccare nient’altro. Qualsiasi configurazione, dalle pagine di errore alle estensioni personalizzate, passando per il logging, le impostazione di security per inibire l’accesso ad alcuni IP, sono copiate insieme al WEB.CONFIG, garantendo dunque che non sia necessario, quando si sposta un sito, ripeterle tutte. È un vantaggio, in questo modo è possibile trasferire un sito copiandolo, piuttosto che un intero server web, per avere meccanismi di fault tolerance facili da applicare. Esempio, rimuovere il supporto per le CGI. <configuration> <system.webServer> <serverFeatures> <remove name="Cgi" /> </serverFeatures> </system.webServer> </configuration> Essendo XML è possibile manipolarlo in qualsiasi modo. Per evitare che il browsing delle cartelle che non hanno una pagina di default sia attivo, ad esempio, è sufficiente inserire nel WEB.CONFIG locale al sito, oppure all’interno dell’APPLICATIONHOSTING.CONFIG, una sezione di configurazione seguente. <configuration> <system.webServer> <directoryBrowse enabled="false" /> </system.webServer> </configuration> Un APPLICATIONHOSTING.CONFIG che nega la visualizzazione di file ASA e MASTER, rispettivamente necessari per il GLOBAL.ASA di ASP o le Master Page di ASP.NET. <configuration> <system.webServer> <requestFiltering> <fileExtensions allowUnlisted="true"> <add fileExtension=".asa" allowed="false" /> <add fileExtension=".master" allowed="false"/> Server side um 18 di 243 </fileExtensions> </system.webServer> </configuration> È possibile specificare all’interno di questo file anche gli IP cui negare l’accesso. <system.webServer> <security> <ipSecurity allowUnlisted="true"> <add ipAddress="192.168.10.7" allowed="false" /> <add ipAddress="192.168.5.111" allowed="false" /> </ipSecurity> </security> </system.webServer> L’idea alla base d’IIS è quella di fornire una pipeline che integri all’interno della stessa sia moduli managed, in pratica quelli basati su .NET Framework sia nativi, unificandola. Questo concetto è già implementato in ASP.NET attraverso gli HttpModule. Si tratta di classi particolari che si registrano per alcuni eventi dell’applicazione e sono invocate per eseguire il codice associato. IIS porta questo modello all’interno del server web stesso, con il vantaggio che un modulo managed può fare le stesse identiche cose di uno nativo, quindi agire anche su altre tipologie di risorse, come le pagine ASP. IIS è distribuito già con tantissimi moduli che hanno praticamente gli usi più disparati, dalla gestione della pagina di default agli errori, passando per l’autenticazione. Tutti questi componenti sono registrati globalmente nell’APPLICATIONHOSTING.CONFIG e possono essere rimossi, se la sezione è sbloccata, anche nei WEB.CONFIG locali. C’è la possibilità di definire una pagina di errore globale, non solo per le applicazioni ASP.NET. <httpErrors> <error statusCode="404" url="/errors/404.aspx"/> </httpErrors> È possibile tracciare l’intera vita della richiesta, così da verificare in quale punto mancano i permessi o si verifica un errore. Da questo punto di vista, esiste un meccanismo di tracing automatico per tutte le richieste con errori, così come uno specifico per quelle che vanno in timeout e ci mettono troppo tempo per completare, con un errore del tipo “takes too long”. Grazie alla pipeline unificata gli eventi di ASP.NET e di IIS vanno a finire nello stesso trace log, con la possibilità di accedere agli stessi nello stesso identico modo. Per i fornitori di hosting condiviso c’è la possibilità di delegare, da parte dell’amministratore, parte della configurazione al programmatore, in pratica la persona che ha fisicamente accesso al WEB.CONFIG. Ogni nodo può essere delegato e quindi modificato nel WEB.CONFIG, attraverso tre modalità differenti. 1. L’utilizzo dell’interfaccia di amministrazione. 2. La modifica del file di configurazione. 3. La modifica di un eseguibile da riga di comando. Nel secondo caso, è possibile modificare un nodo aggiungendo l’attributo seguente che Server side um 19 di 243 può assumere il valore true se questa modifica dev’essere ammessa nei CONFIG figli, false se invece dev’essere bloccata. <authorization allowOverride="true" /> È anche possibile specificare un’impostazione da ereditare nei CONFIG figli, semplicemente aggiungendo l’attributo seguente. <inheritInChildApplications = "true"/> Operazione analoga è possibile lanciando da riga di comando. %SYSTEMROOT%\SYSTEM32\INETSRV\APPCMD UNLOCKCONFIG /SECTION:AUTHORIZATION IIS nasce con l’esigenza di fornire un’architettura tale da garantire l’hosting non soltanto di applicazioni web, infatti rappresenta un sistema generalizzato per l’attivazione di processi e l’health monitoring degli stessi. Il tutto è possibile grazie a WAS (Windows Activation Service), un componente che si occupa di garantire che gli application pool girino senza problemi ed è stato esteso per supportare un modello generico per applicazioni distribuite. IIS ha già incluso il supporto per HTTP, NET.TCP, NET.PIPE e NET.MSMQ (MicroSoft Message Queue). Questo vuol dire che è in grado di far girare applicazioni HTTP e non HTTP side-by-side, in altre parole contemporaneamente, al punto tale che ASP.NET e WAS possono utilizzare lo stesso application domain, nello stesso worker process. Il motivo di questa architettura è quello di favorire un ambiente nel quale gestire i servizi di WCF (Windows Communication Foundation) per la comunicazione inter process per tipi di applicazioni differenti. Il vantaggio principale è che i servizi WCF possono utilizzare funzionalità esistenti in ASP.NET, come State Service, Globalization, Membership, Roles o Profile. Sicurezza In IIS c’è l’utente IUSR che è disponibile direttamente all’interno del SO, si tratta di un nuovo account con privilegi minimi. Di default, IIS utilizza l’autenticazione anonima del processo, gira in pratica con l’account specificato all’interno dell’APPLICATIONHOSTING.CONFIG. <configuration> <system.webServer> <anonymousAuthentication enabled="true" userName="" defaultLogonDomain="" /> </system.webServer> </configuration> OSPITARE PIÙ SITI WEB Le intestazioni host, anche note come nomi di dominio o nomi host, consentono di assegnare più siti ad un unico indirizzo IP di un server web, senza assegnare un indirizzo IP univoco a ogni sito web o definire numeri di porta TCP non standard. L’utilizzo d’intestazioni host rappresenta la soluzione più comune per ospitare più siti web. Dopo aver completato i passaggi seguenti, è necessario registrare il nome o i nomi d’intestazione host con il sistema di risoluzione dei nomi appropriato. Se il PC si trova in una intranet, registrare il nome o i nomi d’intestazione host con il Server side um 20 di 243 sistema di risoluzione dei nomi dell’intranet. Se il PC si trova in Internet, registrare il nome o i nomi d’intestazione host con DNS. Per quanto attiene al server web i passi da seguire per la configurazione sono i seguenti. Creare una cartella principale al cui interno s’inseriscono tante sotto cartelle ciascuna rappresentante la root del sito web di un dominio. Editare il file C:\WINDOWS\SYSTEM32\DRIVERS\ETC\HOSTS e associare all’indirizzo IP di loopback i nomi di dominio specificati. Aggiungere un sito web Quando si aggiunge un sito web in IIS, una voce del sito è creata nel file APPLICATIONHOST.CONFIG, specifica il binding di rete per il sito, esegue il mapping del sito ad un percorso nel file system e, facoltativamente, specifica le credenziali utente per l’accesso al contenuto. 1. Interfaccia utente start/Tutti i programmi/Strumenti di amministrazione/Gestione IIS Server side um 21 di 243 2. Riga di comando CUI: C:\WINDOWS\SYSTEM32\INETSRV\APPCMD.EXE Strumento di amministrazione generico di IIS per la riga dei comandi. APPCMD (comando) (tipo-oggetto) <identificatore> </parametro1:valore1 ...> Tipi di oggetto supportati: SITE Amministrazione di siti virtuali APP Amministrazione di applicazioni VDIR Amministrazione di directory virtuali APPPOOL Amministrazione di pool di applicazioni CONFIG Amministrazione di sezioni di configurazione generale WP Amministrazione di processi di lavoro REQUEST Amministrazione di richieste HTTP MODULE Amministrazione di moduli del server BACKUP Amministrazione di backup della configurazione del server TRACE Utilizzo di registri di traccia di richieste non riuscite (Per elencare i comandi supportati da ogni oggetto utilizzare /?, ad esempio 'ap pcmd.exe site /?') Parametri generali: /? Visualizza un messaggio della Guida sensibile al contesto. /text[:valore] Genera l'output in formato testo (predefinito). /text:* mostra in dettaglio tutte le proprietà degli oggetti. /text:<attributo> mostra il valore dell'attributo specificato per ogni oggetto. /xml Genera l'output in formato XML. Usare per produrre output che può essere inviato a un altro comando in esecuzione nella modalità /in. /in o Legge e utilizza l'input XML da un input standard. Usare per un input prodotto da un altro comando in esecuzione nella modalità /xml. /config<:*> Mostra la configurazione per gli oggetti visualizzati. /config:* include anche la configurazione ereditata. /metadata Mostra i metadati di configurazione durante la visualizzazione della configurazione. /commit Imposta il percorso di configurazione quando si salvano modific he di configurazione. Può indicare un percorso di configurazione specifico, "site", "app", "parent" o "url" per salvare la porzione corretta del percorso modificato dal comando, "apphost", "webroot" o "machine" per il livello di configurazione corrispondente. /debug Mostra le informazioni di debug per l'esecuzione del comando. -Utilizzare "!" per ignorare i parametri con lo stesso nome di quelli generici, quali ad esempio "/!debug:value" utilizzato per l'impostazione della proprietà d i configurazione "debug". appcmd add site /name:stringa /id:uint /physicalPath:stringa /bindings:stringa 3. WMI Metodo Site.Create. Configurare un binding per un sito 1. Interfaccia utente Aprire Gestione IIS. Nel riquadro Connessioni espandere il nodo Siti nell’albero, quindi selezionare il sito per Server side um 22 di 243 cui si desidera aggiungere un binding. Nel riquadro Azioni fare clic su Binding…. Nella finestra di dialogo Binding sito fare clic su Aggiungi. Nella finestra di dialogo Aggiungi binding sito aggiungere le informazioni sul binding, quindi scegliere OK. 2. Riga di comando appcmd set site /site.name:str/+bindings.[protocol='str',bindingInformation='str'] 3. WMI Metodo SSLBinding.Create. Configurare nomi d’intestazione host per un sito 1. Interfaccia utente Aprire Gestione IIS. Nel riquadro Connessioni espandere il nodo Siti nell’albero, quindi selezionare il sito per per il quale si desidera configurare un’intestazione host. Nel riquadro Azioni fare clic su Binding…. Nella finestra di dialogo Binding sito selezionare il binding per il quale si desidera aggiungere un’intestazione host, quindi fare clic su Modifica o su Aggiungi per aggiungere un nuovo binding con un’intestazione host. Nella casella Nome host: digitare un’intestazione host per il sito. Fare clic su OK. Per aggiungere un’ulteriore intestazione host, creare un nuovo binding con gli stessi indirizzi IP e porta e la nuova intestazione host. Ripetere l’operazione per ogni intestazione host in cui si desidera che siano utilizzati l’indirizzo IP e la porta specificata. 2. Riga di comando appcmd set site /site.name:str/bindings.[protocol='str',bindingInformation ='str'].bindingInformation:str Server side um 23 di 243 3. WMI Proprietà Site.Bindings. Proprietà BindingElement.BindingInformation. INSTALLAZIONE DI PHP IN IIS Può essere fatta in tre modi. 1. Filtro ISAPI: garantisce buone prestazioni nell’esecuzione degli script ma può riservare spiacevoli sorprese in quanto a stabilità. 2. CGI: attraverso le CGI IIS è in grado di chiamare il PHP ad ogni esecuzione di script, intercettarne l’output e inviarlo al browser; questa tecnica è stabile, fornisce però prestazioni mediocri non sufficienti per la realizzazione di un server di produzione. 3. Fast CGI: è la migliore sia sul fronte delle prestazioni sia della stabilità. Impostazione del PATH Per evitare di dover spostare le librerie di PHP nelle cartelle di sistema, modificare la variabile Path in modo che contenga il percorso alla cartella di PHP appena creata. Aprire il Pannello di controllo, cliccare sull’icona Sistema e nella finestra che appare selezionare la scheda Avanzate e selezionare Variabili d'ambiente. Dall’elenco delle Variabili di sistema selezionare Path e cliccare sul tasto Modifica. A questo punto si deve modificare il valore della variabile aggiungendo in coda la scritta C:\EasyPHP\php5; attenzione a non dimenticare il (;). Fatto ciò si deve riavviare Windows per far sì che la modifica effettuata abbia effetto. Modifica del PHP.INI Nella cartella C:\EASYPHP\PHP5 rinominare il file PHP.INI-DIST in PHP.INI, è il file di Server side um 24 di 243 configurazione di PHP, aprirlo con un editor di testi. Modificare le direttive seguenti. cgi.force_redirect = 0 doc_root = "C:\Inetpub\wwwroot" extension_dir = "C:\EasyPHP\php5\ext" Togliere il (;) all’inizio delle direttive seguenti. extension=php_mbstring.dll, l’estensione mbstring è necessaria al corretto funzionamento di phpMyAdmin con i set di caratteri multibyte. extension=php_gd2.dll per caricare la libreria per la manipolazione delle immagini. extension=php_mysql.dll. L’estensione mysql è necessaria per l'interazione con l’omonimo DB. Modificare il valore della direttiva session.save_path = "C:\Lavoro\Film\Temp" in modo che punti ad una cartella di file temporanei del sistema, quindi salvare il file. Configurazione di IIS IIS deve usare l’interprete PHP (php-cgi.exe) per l’esecuzione dei file con estensione PHP. Avviare Gestione IIS espandere la struttura ad albero a sinistra fino a mostrare il Default Web Site. Fare clic su questa icona con il tasto destro e selezionare la voce Proprietà del menu contestuale, nella finestra che compare selezionare la scheda Home directory. Fare clic sul pulsante Configurazione… per accedere alle impostazioni di mapping. Per aggiungere il supporto agli script PHP si deve fare clic sul pulsante Aggiungi. Server side um 25 di 243 Esempio, verificare l’esito della procedura con la seguente applicazione che permette di visualizzare i parametri di configurazione di PHP nel server in cui è eseguito il codice. <html> <head> <title>PHP</title> </head> <body> <?php phpinfo(); ?> </body> </html> Salvare il file creato nella cartella C:\INETPUB\WWWROOT con il nome INFO.PHP e aprire il browser al’indirizzo: http://localhost/info.php. Server side um 26 di 243 A volte si verificano problemi di accesso negato durante l'esecuzione di script PHP. Questi sono riconducibili principalmente a due fattori. 1. Possono dipendere o dalle normali impostazioni sui diritti di accesso dei file in partizioni NTFS (New Technology File System), tali problemi si risolvono agendo sulle impostazioni di accesso di Windows relative ai file o alle cartelle coinvolte. 2. Possono essere legate alle politiche di accesso del server IIS, i permessi vanno modificati dalla console di amministrazione di IIS. Server side um 27 di 243 EASYPHP INTRODUZIONE Sono avviati Apache e MySQL server. Start/Tutti i programmi/EasyPHP La prima volta che si avvia EasyPHP sarà aggiunta un’icona nell’area di notifica, fare clic su di essa per accedere al menu. 1. 2. 3. 4. 5. Aiuto. File di log: riporta ogni errore generato da Apache e MySQL. Configurazione: un’interfaccia per configurare EasyPHP. Esplora file F8: apre la cartella C:\EASYPHP\WWW. Amministrazione CTRL+A: apre l’URL http://127.0.0.1/home/ permette di gestire il DB e di amministrare gli alias. 6. Sito locale F7: apre l’URL http://127.0.0.1/. 7. Riavvia F5: avvia i server Apache e MySQL. 8. Ferma F3: ferma i server Apache e MySQL. 9. Esci. Prima di aprire Amministrazione o il Sito Locale, verificare che EASYPHP.EXE sia avviato e che i server funzionino. Doppio clic sull’icona di EasyPHP nell’area di notifica: gli stati di Apache e di MySQL devono essere posizionati sul verde. Apache nasce nel 1995 grazie al lavoro di una fondazione no profit chiamata “Apache Software Foundation” e si diffonde in tutto il mondo per la sua affidabilità e l’elevate Server side um 28 di 243 performance. Tra i moduli più utilizzati in Apache, c’è il modulo mod_rewrite che utilizza un motore di riscrittura basato su un parser di regular expression. Amministrazione CTRL+A: apre l’URL http://127.0.0.1/home/. Uso della cartella WWW (World Wide Web) Per fare in modo che lo script possa essere eseguito, assicurarsi di aver posizionato il file della cartella WWW. Il server Apache è configurato automaticamente per aprire un file di nome INDEX.HTML, quando è digitato l’indirizzo http://127.0.0.1. Questa è per definizione la pagina di partenza e verifica che EasyPHP è in esecuzione. È consigliabile creare una cartella per ciascun progetto all’interno della cartella WWW, in questo modo diviene più semplice gestire diversi progetti. Esempio, visualizzare la data corrente nel file DATA.PHP. <html> <head> <title>PHP</title> </head> <body> Data Corrente: <?php print (Date("l F d, Y")); ?> Server side um 29 di 243 </body> </html> Creare una nuova cartella all’interno di WWW oppure utilizzare la cartella creata durante l’installazione: PROJET1. Salvare la pagina contenente lo script PHP in una delle seguenti estensioni: PHP, PHP3, PHP e PHP5, queste estensioni sono state settate da EasyPHP. Le estensioni possono cambiare con la società che offre l’hosting: ricordare di cambiare le estensioni se necessario. Azioni da evitare. Cercare di lanciare lo script facendo doppio clic sul file all’interno della cartella del progetto: questo genera una pagina di errore. Azioni corrette. Lanciare EasyPHP, connettersi tramite il browser a http://127.0.0.1, aprire la cartella del progetto, quindi cliccare su DATA.PHP. A questo punto si deve vedere la pagina correttamente interpretata dal server Apache. PROGRAMMARE IN .NET UTILIZZANDO APACHE Installare il modulo seguente: MOD_ASPDOTNET-2.0.0.MSI. Creare una cartella, al di fuori della root di Apache, da usare come contenitore per le applicazioni ASP.NET, per esempio F:\STORE. File HTTPD.CONF Aggiungere le righe seguenti. LoadModule aspdotnet_module modules/mod_aspdotnet.so AddHandler asp.net asax ascx ashx asmx aspx axd config cs csproj\licx rem resources resx soap vb vbproj vsdisco webinfo Eseguire il mount di una cartella virtuale di Apache su quella fisica creata in precedenza. AspNetMount /Store"F:/Store" Creare un alias per la cartella creata. Alias /Store "F:/Store" Garantire i permessi di esecuzione alla cartella creata. <Directory "F:/Store"> Options FollowSymlinks ExecCGI Order allow,deny Allow from all DirectoryIndex Default.htm Default.aspx </Directory> Mappare tutte le richieste delle pagine ASP.NET verso il framework. AliasMatch/aspnet_client/system_web/(\d+)_(\d+)_(\d+)_(\d+)/(.*) \ "C:/Windows/Microsoft.NET/Framework/v$1.$2.$3/ASP.NETClientFiles/$4" <Directory \"C:/Windows/Microsoft.NET/Framework/ v*/ ASP.NETClientFiles"> Options FollowSymlinks Order allow,deny Allow from all </Directory> Server side um 30 di 243 XAMPP INTRODUZIONE È un acronimo con cui s’indica una piattaforma di sviluppo web/DB che prende il nome dalle iniziali dei componenti S/W con cui è realizzata. X significa cross-platform. Apache. MySQL. PHP. PERL. È una distribuzione “tutto-compreso”, in altre parole s’installa e si configura il server web di Apache, l’interprete PHP, il DBMS MySQL. Programmatore: Kai ‘Oswald’ Seidler - Kay Vogelgesang. Mette a disposizione un pannello di controllo per l’avvio dei componenti configurati. Server side um 31 di 243 Configurare Eclipse come ambiente di programmazione Bisogna installare il plugin PHPEclipse. Non appena l’installazione è terminata, PHPEclipse dev’essere configurato per agganciare gli strumenti configurati da XAMPP. Fare clic su Window/Preferences, per aprire la maschera di configurazione di Eclipse e specificare il percorso d’installazione di XAMPP. Server side um 32 di 243 Eclipse è dotato ora di una nuova prospettiva, per selezionarla fare clic su Window/Open Perspective/Other…/PHP. Per create un progetto, fare clic su File/New (ALT+MAIUSC+N)/Project… Una volta creato, il progetto può essere amministrato nella vista Navigator. File TEST.PHP <html> <head> <title>Test</title> </head> <body> <?php for ($i = 0; $i < 10; $i++) { echo ($i +"<br />"); } ?> <body> </html> Server side um 33 di 243 Selezionare il nome del progetto e dal menu contestuale la voce Properties. Nella finestra selezionare PHP Project Settings e nel campo Project URI: inserire la stringa seguente http://localhost/test e fare clic su OK. Server side um 34 di 243 La pagina PHP dev’essere eseguita con Apache, selezionare il nome del progetto e dal menu contestuale la voce Export…. Nella finestra selezionare General/File System e premere Next. I file vanno esportati all’intero della cartella HTDOCS di XAMPP. Server side um 35 di 243 Selezionare il file TEST.PHP e dal menu contestuale la voce PHP Browser. Al centro di Eclipse è aperta una scheda con un browser puntato sull’indirizzo della pagina. Server side um 36 di 243 TOMCAT INTRODUZIONE Procurarsi l’eseguibile d’installazione rilasciato da Apache dal sito del progetto Jakarta. Tomcat richiede che sia installato sul sistema il JRE (Java Runtime Enviroment). Indicare il pathname, fare clic su Start/Pannello di controllo/Prestazioni e manutenzione/Sistema. Nella finestra Proprietà del sistema fare clic sulla scheda Avanzate e Variabili d’ambiente. Tomcat rappresenta l’implementazione delle tecnologie Java Servlet e JSP. Crea l’ambiente all’interno del quale le servlet e le pagine JSP sono eseguite. Il server engine s’integra con IIS e Apache ed estende le loro funzionalità per l’esecuzione delle servlet. Il server Tomcat è associato alla porta 8080, questo significa che può funzionare anche se è attivo IIS o Apache. Per avviare il server, lanciare il comando seguente da console. C:\Tomcat\jakarta-tomcat\bin\startup.bat<INVIO> Server side um 37 di 243 Per controllare il funzionamento del server basta inserire nella barra degli indirizzi del browser l’indirizzo seguente. http://localhost:8080 o http://127.0.0.1:8080 Essendo il localhost, 127.0.0.1, il nome che identifica il server nel PC locale. È aperta la pagina iniziale del server Tomcat. 1. Administration 1.1. Status 1.2. Tomcat Manager: gestisce le applicazioni create tramite il browser. 2. Documentation 2.1. Release Notes 2.2. Change Log 2.3. Tomcat Documentation 3. Tomcat Online 4. Miscellaneous 4.1. Servlets Examples 4.2. JSP Examples Tomcat è soprattutto un AS (Application Server) che consente di utilizzare la tecnica delle servlet o delle JSP per creare applicazioni web con il linguaggio Java a fare da asse portante. È anche un server web ma quel che più conta è un container di applicazioni Java. Per arrestare il server, lanciare il comando seguente da console. C:\Tomcat\jakarta-tomcat\bin\shutdown.bat<INVIO> Per far sì che le applicazioni siano gestite correttamente da Tomcat, i file che le compongono devono essere correttamente collocati all’interno delle cartelle del server. La cartella che contiene le applicazioni è la seguente. C:\EASYPHP\APACHE\APACHE-TOMCAT-6.0.18\WEBAPPS\ROOT. All’interno della ROOT ci saranno tutti i file che compongono l’applicazione, file HTML, pagine JSP, immagini, suoni, opportunamente distribuiti in cartelle per una più facile Server side um 38 di 243 gestione. Particolare importanza ricopre la cartella WEB-INF contenuta nella ROOT. Qui si ha, dentro la cartella CLASSES, i file CLASS delle classi Java che comprendono il motore del sito web, tra cui le servlet e, dentro la cartella LIB, le opportune librerie in file di tipo JAR (Java ARchive). Le informazioni sulle applicazioni web del server sono memorizzate nel file WEB.XML che dev’essere posizionato nella sotto cartella WEB-INF. Il file WEB.XML si chiama deployment descriptor, descrittore della distribuzione ed è un descrittore dei componenti utilizzati nell’applicazione web, la sua struttura è la seguente. <?xml version="1.0" encoding="iso-8859-1"?> <!-Licensed to the Apache Software Foundation (ASF) --> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"version="2.5"> <display-name>Welcome to Tomcat</display-name> <description> Welcome to Tomcat </description> <servlet> <servlet-name> Esempio di servlet</servlet-name> <servlet-class> Prova </servlet-class> </servlet> <servlet-mapping> <servlet-name> Esempio di servlet</servlet-name> <url-pattern> Prova1 </url-pattern> </servlet-mapping> </web-app> Dove. <web-app> è il TAG radice che indica l’elenco delle informazioni sulle applicazioni web. <servlet-name> è il titolo identificativo della servlet. <servlet-class> è il nome fisico del file CLASS contenente la servlet compilata. <url-pattern> è l’URL nella casella degli indirizzi del browser per eseguire la servlet. Per ogni servlet bisogna inserire i due elementi: <servlet> e <servlet-mapping>. Supponendo di aver creato il file PROVA.CLASS e di averlo memorizzato nella sotto cartella seguente. C:\EASYPHP\APACHE\APACHE-TOMCAT-6.0.18\WEBAPPS\ROOT/ESEMPI/WEBINF/CLASSES Per eseguire la servlet basta inserire nella barra degli indirizzi del browser l’indirizzo. http://localhost:8080/esempi/Prova1 Server side um 39 di 243 Il server Tomcat mette a disposizione un’intefaccia grafica, all’interno del Tomcat Manager, per effettuare la distribuzione degli archivi WAR (Web ARchive). PYTHON Per aprire un server, dalla console digitare il comando seguente. Server side um 40 di 243 SICUREZZA DEI SERVER WEB INTRODUZIONE La sicurezza di un server web costituisce una buona parte della sicurezza di un’applicazione. Le informazioni sono uno dei beni più preziosi della società odierna e i DB rappresentano, in senso virtuale, il luogo nel quale tali informazioni sono custodite in attesa di essere utilizzate per le finalità più disparate. I DBMS costituiscono il fondamento di ogni sistema; tuttavia essi non sono tradizionalmente considerati alla stessa stregua dei SO e delle periferiche di rete almeno per quanto concerne l’adozione degli standard di sicurezza. Due sono le ragioni che rendono tali sistemi uno dei target più appetibili per gli hackers. 1. Presenza di dati che molto spesso assumono il carattere di estrema confidenzialità, numeri di carte di credito, dati finanziari e strategici. 2. La compromissione di un server di DB determina, non di rado, a fronte di una difficoltà di penetrazione talvolta irrisoria, la possibilità da parte dell’attaccante di acquisire il completo controllo della macchina se non addirittura della intera infrastruttura di rete. Cosa rende un server web una risorsa così appetibile ed esposta agli attacchi esterni: sicuramente la combinazione dei fattori seguenti. Rappresentano delle vere e proprie porte di accesso alla LAN (Local Area Network) nella quale sono custodite le informazioni più svariate. La sottovalutazione dei rischi oppure la mancanza di risorse economiche e umane da dedicare al potenziamento delle politiche di sicurezza e anche la scarsa progettazione e qualità del S/W possono determinare l’insorgere di una condizione d’intrinseca vulnerabilità dei servizi web resa ancora più grave dalla loro esposizione al pubblico. Condurre con successo un attacco sul web utilizzando le classiche porte del servizio HTTP 80, 81, 8000 è molto più facile dal momento che nella maggioranza dei casi il traffico veicolato in questo modo non è bloccato dai dispositivi di controllo degli accessi router e firewall. Sfortunatamente non esistono dei rimedi nè delle tecniche tali da poter rendere sicuro al 100% un server contro gli attacchi provenienti dall’esterno ma, ciò nonostante, si può ancora operare per tenere lontani molti problemi e vulnerabilità. Per fare ciò occorre comprendere la natura e la portata dei pericoli ai quali ci si espone e successivamente adottare delle precauzioni di carattere generale dirette a circoscrivere i rischi suddetti entro limiti accettabili in relazione alla natura degli interessi da proteggere. modello di sicurezza I DBMS adottano una architettura di sicurezza basata su tre differenti livelli di protezione. 1. Autenticazione: questa è la fase più delicata, contempla la verifica dell’identità dell’utente con una password, un sistema può autenticare un utente usando tre fattori. 1.1. Ciò che si conosce: la password. 1.2. Ciò che si possiede: la smart card. 1.3. Ciò che si è: password biometriche, impronte digitali, iride, retina e voce. 2. Autorizzazione: è la fase immediatamente seguente l’autenticazione nella quale il sistema deve determinare a quali risorse l’utente può avere accesso e con quali modalità operative, di regola le differenti attività che coinvolgono tale fase sono raggruppate sotto la terminologia di “amministrazione della sicurezza degli utenti”. 3. Auditing: questa fase si contraddistingue per l’adozione di mezzi idonei a identificare e riconoscere possibili abusi oltre che ad assicurare l’integrità delle informazioni. Server side um 41 di 243 INDIVIDUAZIONE DEI RISCHI Quando si parla di sicurezza dei DB s’intende fare riferimento alla necessità di garantire l’accessibilità dei dati, a prescindere dal loro valore intrinseco. Qualunque attività diretta ad implementare e/o rafforzare la sicurezza di questi sistemi presuppone il rispetto dei seguenti principi. 1. La definizione di pratiche e procedure di sicurezza chiare e di facile attuazione. 2. La predisposizione di livelli di sicurezza multipli. 3. L’applicazione costante del principio “dei privilegi minori”. È importante non sottovalutare l’efficacia di questi criteri e, in particolare, di quelli indicati nei punti due e tre. In effetti senza la presenza di “strati” di sicurezza multipli diventa estremamente facile connettersi ad un DB sfruttando la presenza di account noti e con password di default oppure bypassando la sicurezza del SO e dei vari dispositivi che compongono l’infrastruttura di rete. Analogamente il principio dei privilegi minori, in virtù del quale ogni utente deve avere assegnati soltanto i privilegi strettamente indispensabili al compimento delle sue attività, evita i danni che possono derivare da un uso improprio oppure da azioni compiute in modo accidentale. I pericoli derivanti dalla mancata adozione di adeguati criteri di sicurezza nell’allestimento e nel mantenimento di un sito web pubblico sono riconducibili alla possibilità di un abuso del servizio da parte di soggetti malintenzionati. Questo abuso può essere perpetrato in svariati modi ma molto spesso esso implica uno sfruttamento degli errori di configurazione o delle vulnerabilità esistenti ai vari livelli. 1. SO. 2. Servizio HTTP o altri servizi di rete SMTP, DB e FTP. 3. Programmi/interpreti e script utilizzati per la generazione del contenuto del sito. 4. Dispositivi di controllo degli accessi router e firewall. In linea generale il percorso che un aggressore tenta di seguire nell’attacco di un sistema può essere riassunto nel modo seguente. Accesso al sistema attraverso l’esecuzione di exploit, lo sfruttamento di condizioni di buffer overflow in script e applicazioni, la cattura o l’intercettazione del file delle password e gli attacchi a forza bruta. Scalata dei privilegi e/o impersonificazione degli utenti con privilegi amministrativi attraverso il crack delle password e/o l’esecuzione di exploit successivi. Occultamento delle tracce tramite la cancellazione dei log, l’uso di rootkits e lo sfruttamento di particolari caratteristiche del SO. Installazione di backdoor che permettono all’aggressore un ritorno e una ripresa del controllo del sistema in qualsiasi momento. Complessivamente, in conseguenza di queste azioni, l’aggressore può essere portato ad eseguire delle attività che rientrano in due aree principali. 1. Attività che comportano una manipolazione del server e/o un trafugamento di informazioni. 1.1. Atti di vandalismo come la modifica dei contenuti delle pagine web o la cancellazione del contenuto dell’intero sito. 1.2. Trafugamento d’informazioni sensibili concernenti l’organizzazione, la configurazione di rete oppure la clientela o gli utenti. 1.3. Uso dell’host come base per lanciare attacchi contro altri sistemi, attacchi DDOS (Distributed Denial of Service). 1.4. Installazione di strumenti per il monitoraggio del traffico di rete e la cattura di informazioni di autenticazione: sniffing. 2. Attività che producono una situazione d’indisponibiltà del servizio, in altre parole l’impossibilità per gli utenti di accedere alle risorse messe a disposizione dal server. Server side um 42 di 243 standard di sicurezza L’attuazione dei principi enunciati non può prescindere dalla considerazione di alcuni aspetti fondamentali tra i quali meritano di essere ricordati i seguenti. Segretezza e confidenzialità: i dati devono poter essere consultati e/o modificati soltanto da parte di chi sia debitamente autorizzato. Integrità e autenticità: i dati non devono poter essere manipolati dolosamente o accidentalmente e la loro provenienza dev’essere verificabile. Accessibilità: i dati devono essere sempre disponibili eventualmente anche attraverso il loro immediato ripristino. Gli aspetti coinvolti nel processo di hardening di un DBMS sono i seguenti. Configurazione iniziale. Autenticazione degli utenti. Autorizzazione all’uso degli oggetti del DB. Amministrazione e l’aggiornamento delle policies. Auditing. Strategie di backup e di ripristino dei dati. Il modo migliore di procedere, dopo aver identificato con esattezza i rischi, è quello di predisporre un piano che individui una serie di misure precauzionali tali da condurre ad un livello di sicurezza che dev’essere adeguato e proporzionato alla natura e all’importanza delle risorse da proteggere, un portale di commercio elettronico indubbiamente richiede livelli e politiche di sicurezza ben maggiori di un sito web di carattere statico. Si tratta di un processo lungo e ripetitivo che deve partire dalla considerazione degli aspetti più generali e che comporta una serie di attività e di valutazioni. DISABILITARE I SERVIZI NON NECESSARI Rimuovere FTP, disabilitare i linguaggi di scripting e gli script di esempio. Limitare i servizi di condivisone di file, a singole cartelle e non a tutto il file system, quindi creare una lista contenente gli indirizzi IP abilitati ad accedere al file sharing. auditing La maggior parte dei DB mette a disposizione funzionalità di auditing più o meno estese che tuttavia non sono attive di default a causa del loro impatto negativo sulle performance dell’intero sistema. Nonostante l’inevitabile carico di lavoro aggiuntivo tali funzionalità hanno una importanza fondamentale ai fini della individuazione di tutte le attività non autorizzate o dolosamente poste in essere. Pertanto, data la loro importanza, è sempre consigliabile ricorrere alle stesse cercando di adottare un giusto compromesso tra prestazioni complessive e quantità di dati raccolti per effetto del monitoraggio. autorizzazione all’uso degli oggetti del DB Generalmente i permessi assegnati agli utenti del DB racchiudono in se stessi il diritto di accedere ai vari oggetti, tabelle, viste, sinonimi e SP (Stored Procedure), con modalità operative differenti. È consigliabile individuare preventivamente, in fase progettuale, i vari utenti del DB e identificare per essi i ruoli, intesi in termini di raccolte di privilegi, più consoni al rispetto del principio dei privilegi minimi. autenticazione degli utenti L’utilizzo delle risorse di un DB dev’essere sempre subordinato alla preventiva autenticazione degli utenti che di regola avviene mediante la fornitura di una password. Data la delicatezza dell’intero processo è opportuno predisporre delle policies che Server side um 43 di 243 stabiliscano i seguenti parametri. La durata minima delle password. La lunghezza minima e il rispetto di determinati criteri quali ad esempio l’uso combinato di lettere, numeri e simboli. La predisposizione di meccanismi di lock degli account dopo un certo numero di tentativi falliti di login. La determinazione del ciclo di vita degli account. L’opportunità di verificare la facilità con la quale le password possono essere indovinate per mezzo di attacchi basati su un dizionario dati oppure condotti con il metodo della forza bruta. La necessità di verificare il fatto che le password siano memorizzate nel DB in forma crittografata. amministrazione e aggiornamento delle policies Nessun processo di hardening può dirsi veramente efficace senza che siano state sviluppate delle policies adeguate. Queste infatti rappresentano il framework utilizzato in tutte le attività ed i processi diretti a rafforzare la sicurezza ed a gestire i rischi ed il loro compito fondamentale è quello di garantire agli amministratori del DB che le attività compiute siano effettivamente appropriate per l’organizzazione. Vista la loro importanza è opportuno quindi che le policies regolamentino aspetti fondamentali quali i seguenti. La creazione di standard relativamente ad account utente, password, oggetti e ruoli. I requisiti minimi in termini di esecuzione di attività di auditing e logging. Le procedure per la gestione delle patch rilasciate dal produttore del S/W. Il controllo degli accessi agli oggetti del DB. L’utilizzo dei ruoli predefiniti e la predisposizione di nuovi ruoli. Ogni altro aspetto che assuma rilevanza per l’organizzazione ai fini della sicurezza. SCELTA DEL SO Non è possibile parlare delle tecniche di protezione dei server web senza fare una breve considerazione in merito ai criteri di scelta del SO poichè le caratteristiche di affidabilità e robustezza di quest’ultimo possono avere un impatto significativo nella predisposizione e nel mantenimento dei giusti livelli di sicurezza. 1. Assenza di vulnerabilità note nei confronti di tipologie conosciute di attacco. 2. Capacità di limitare determinati tipi di attività soltanto ad alcuni utenti. 3. Abilità nel rimuovere e disabilitare servizi e risorse non necessari. 4. Abilità nel controllare l’uso e l’accesso alle risorse e nel registrare la varie attività degli utenti. 5. Facilità di gestione ma non a discapito della sicurezza. SEPARARE I DATI PUBBLICI DA QUELLI PRIVATI Non memorizzare dati riservati su sistemi usati anche come server web, a meno che non sia assolutamente necessario. Per un’extranet, adottare una configurazione con un “agnello sacrificale”: ovvero un server web posto esternamente al firewall in modo da non mettere a rischio i dati aziendali dietro al firewall. CONFIGURAZIONE DI RETE Isolare il server web dalla LAN, nell’ipotesi di una compromissione dell’host questa pratica è infatti di ausilio nell’impedire il verificarsi di due conseguenze molto gravi. 1. L’accesso agli host della LAN da parte dell’aggressore. Server side um 44 di 243 2. Il monitoraggio del traffico proveniente dalla LAN con la conseguente cattura di eventuali informazioni riservate. Il raggiungimento di un tale livello d’isolamento diventa possibile attraverso la configurazione e l’uso di una zona DMZ (DeMilitarized Zone), in pratica di una topologia in grado di segmentare logicamente la LAN separando i sistemi interni da quelli esterni accessibili al pubblico che devono rimanere isolati, accompagnata anche dalla presenza di adeguati dispositivi di controllo degli accessi come router e firewall, i cui compiti sono. Bloccare tutto il traffico UDP, ICMP (Internet Control Message Protocol) e TCP non strettamente necessario. Bloccare tutte le connessioni TCP che traggono origine dallo stesso server web. Bloccare il traffico tra il server web e la LAN. Disabilitare il source routing e scartare i datagrammi IP che abbiano impostata questa opzione; il source routing è una funzione del meccanismo d’instradamento dei pacchetti IP che permette alla sorgente di influenzare il percorso che il pacchetto seguirà nel suo attraversamento delle reti. Il blocco delle connessioni in uscita dal server HTTP non può comunque essere adottato nel caso in cui quest’ultimo si avvalga di servizi esterni erogati da altri server, ad esempio un DB per la generazione dinamica dei contenuti, un server SMTP per la posta in uscita. Quando ciò accade è tuttavia conveniente collocare anche gli host dei servizi di supporto all’interno di una sottorete e separare quest’ultima sia dalla LAN che da quella esterna mediante l’utilizzo di dispositivi di controllo degli accessi. Bloccare qualsiasi tipo di traffico tra la rete Internet e questi host. Impedire ogni forma di traffico diretto tra gli utenti e i servizi di supporto. Disabilitare le funzioni di IP forwarding, inoltro del traffico IP, tra server web e server di supporto. Qualora non sia possibile servirsi di una zona DMZ, fermo restando i rischi di un possibile accesso agli host della LAN, è sempre possibile ricorrere a dispositivi come hub e switch in grado di suddividere il traffico della rete separando quello del web da quello interno ed, eventualmente, criptare quest’ultimo così da renderlo comunque incomprensibile all’aggressore anche in caso di avvenuta compromissione del server. Sniffing: lettura dei dati in transito perché le applicazioni basate sul TCP/IP, FTP, HTTP, SMNP, telnet offrono protezione scarsa per le password. Implementare un protocollo secondario dedicato alla sicurezza come SSH, per esempio HTTPS. Quando si amministrano i server e i router da remoto mai usare protocolli telnet, SMNP e FTP se non dopo aver attivato un protocollo di sicurezza. CONFIGURAZIONE DEL SERVER Il server web dovrebbe operare nell’ambito di una configurazione di rete e di sistema davvero minima. Il rispetto di questa semplice regola è effettivamente in grado di produrre come risultato un sensibile miglioramento dei livelli di sicurezza attraverso questi espedienti. 1. La disabilitazione di tutti i servizi di rete non essenziali ed, in particolar modo, di quelli affetti da vulnerabilità conosciute sotto il profilo della sicurezza. 2. La rimozione dal sistema dei file corrispondenti ai servizi disabilitati. 3. L’eliminazione delle porte TCP e UDP in ascolto superflue. 4. La rimozione o disabilitazione di tutte le risorse non richieste in relazione al ruolo dell’host, compilatori, interpreti, shell e scripts. 5. La corretta gestione degli utenti e dei loro privilegi. 6. La predisposizione di regole adeguate per l’accesso e l’uso delle risorse. Queste attività sono importanti non soltanto in un ottica generale di riduzione dei rischi di compromissione del sistema ma anche in vista di uno snellimento delle attività di amministrazione e, quindi, della minore probabilità di commettere errori di configurazione Server side um 45 di 243 che possono essere prontamente sfruttati da un aggressore. A tal fine, proprio per evitare di commettere dimenticanze, è conveniente adottare un approccio del tipo “deny all, then allow” che consiste prima nel disabilitare indistintamente tutti i servizi e le porte TCP/UDP e poi nel riabilitare, dopo un’attenta valutazione, soltanto quelle veramente essenziali. Anche per quanto concerne la gestione degli utenti e dei privilegi vanno prefissate delle regole improntate a criteri restrittivi. Impedire che il servizio HTTP sia lanciato da un utente con privilegi amministrativi perchè questo può comportare l’acquisizione del controllo completo del sistema in caso di exploit eseguito con successo. Disabilitare o rimuovere tutti gli account inutili, installati dal SO, per ridurre il rischio di una impersonificazione o scalata dei privilegi nel caso d’intrusione. Modificare il nome dell’account di amministratore. Eliminare gli account non necessari, per esempio guest; disabilitare automaticamente un account dopo un numero predefinito di tentativi di accesso falliti. Adottare criteri di robustezza delle password sotto il profilo della lunghezza, almeno otto caratteri, complessità, alfanumerica con un mix di caratteri maiuscoli e minuscoli e l’uso di caratteri non stampabili, riutilizzo da evitare e durata, cambiarla ogni sei settimane. Verificare direttamente le password, preferibilmente mediante gli stessi strumenti usati dagli hackers, per accertare che esse rispondano ai criteri voluti. Impostare il blocco degli utenti dopo un certo numero di tentativi falliti di login. Il blocco degli utenti è una misura da adottare con cautela poichè costituisce un’arma a doppio taglio che può spingere l’aggressore a provocare una situazione di DOS per l’utente attraverso una serie di tentativi di connessione falliti. I singoli processi coinvolti nella gestione del servizio HTTP devono comunque avere accesso soltanto ai file e alle cartelle necessarie al loro funzionamento per i quali occorre specificare delle regole di accesso ACL (Access Control List) che, oltre ad offrire una maggiore granularità nel controllo dell’uso delle risorse, sono in grado di scongiurare o mitigare gli effetti derivanti da un eventuale attacco DOS diretto a provocare una situazione d’indisponibilità dell’intero sistema proprio attraverso un esaurimento delle sue risorse. In questa prospettiva per ridurre significativamente gli effetti derivanti da attacchi di questo genere, è sempre consigliato il ricorso ai seguenti interventi correttivi. Creare una singola cartella radice e da essa far derivare una gerarchia di sotto cartelle nelle quali suddividere le risorse che costituiscono il contenuto del web. Limitare ad una sola cartella, opportunamente configurata e protetta, tutte le applicazioni esterne eseguite come parte integrante del servizio web. Limitare l’uso dei file temporanei da parte dei singoli processi all’interno di apposite cartelle opportunamente protette e consentirne l’accesso soltanto ai processi stessi. Impedire che file e risorse esterne alla gerarchia di cartelle del server possano essere forniti in risposta alle richieste degli utenti. Disabilitare l’uso dei link simbolici per evitare che risorse facenti parte del contenuto del web possano puntare a file di sistema o ad altre risorse all’interno della LAN. Aggiustare le priorità dei vari processi di sistema. USO DI APPLICAZIONI E DI SCRIPT L’installazione e l’uso di applicazioni esterne, plugin e script può aprire una breccia nei livelli di protezione di qualsiasi server web. Anche gli host più inviolabili possono infatti cadere a causa di un banale exploit che sfrutta un semplice script CGI per eseguire localmente sul server comandi diretti ad ottenere l’accesso al sistema. La preoccupazione principale deve sempre rimanere quella di ridurre i rischi. Server side um 46 di 243 Evitare, se possibile, l’uso di script di terze parti oppure accertarne l’esatta provenienza e autenticità del codice. Fare uso soltanto di applicazioni e degli script indispensabili disabilitando tutti gli altri. Impiegare tecniche di programmazione nella scrittura del codice e prestare la massima attenzione ad aspetti quali la lunghezza e la complessità finale, la presenza di opportuni controlli per la validazione dell’input e l’interazione con altre applicazioni esterne o l’accesso in lettura e/o scrittura al file system. Valutare la presenza di queste stesse caratteristiche anche negli script di terze parti. Usare una macchina di test per verificare il funzionamento di tutte le applicazioni e degli script prima ancora di impiegarli in una macchina di produzione. Evitare di collocare le applicazioni e gli interpreti all’interno della stessa cartella dove risiedono gli script, ad esempio la CGI-BIN e posizionarli invece in una cartella separata opportunamente protetta e accessibile soltanto agli utenti amministratori. Circoscrivere l’accesso di applicazioni e interpreti ai soli file e cartelle indispensabili al loro funzionamento e comunque soltanto a quelli all’interno del contenuto pubblico del web. Verificare costantemente l’integrità degli eseguibili relativi ad applicazioni ed interpreti nonchè degli script. PROGETTAZIONE WEB DI QUALITÀ L’uso di script può diminuire il livello di sicurezza di un server web poichè introduce molteplici aspetti e variabili nel flusso di esecuzione del codice che possono essere sfruttate da un aggressore per tentare di ottenere un accesso non autorizzato. Non a caso una delle tipologie più note di aggressione si basa sull’invio al server di dati inattesi mediante l’URL. Generalmente questo avviene inviando una quantità eccessiva di dati oppure combinando tecniche che consistono in un abuso di metacaratteri e nell’encoding della stringa al fine di provocare tre effetti differenti. 1. Buffer overflow: si verifica quando un buffer d’input, ad esempio una variabile all’interno di un’applicazione, è saturata con un valore più grande di quello che essa riesce a gestire, di regola il verificarsi di questa condizione è sfruttato per tentare di eseguire una porzione di codice arbitrario forzandolo all’interno dello stack di esecuzione della CPU. 2. Alterazione della logica applicativa: può consistere nell’alterazione dei meccanismi di autenticazione o nell’accesso a funzionalità riservate dell’applicativo. 3. Invocazione di funzioni di sistema o di altre applicazioni oppure accesso a file e risorse che non fanno parte del contenuto del web. L’approccio migliore per cercare di scongiurare questi effetti è rappresentato dall’adozione di tecniche incentrate sulla qualità della programmazione, sull’implementazione di una logica applicativa robusta e sul controllo del flusso. Usare meccanismi di controllo e di filtro dei dati d’input. Utilizzare tutte le caratteristiche di sicurezza messe a disposizione dai linguaggi. Evitare l’utilizzo di meccanismi come SSI (Server Side Include). Usare con attenzione e cautela i TAG nascosti all’interno delle pagine HTML. Quando non si ha certezza sulla natura dell’input ricevuto è necessario ricorrere al filtraggio dei dati attenendosi ad una regola elementare: identificare con esattezza la natura e il tipo delle informazioni da gestire ed eliminare tutti i caratteri non necessari e inutili avvalendosi di funzioni proprie o di quelle messe a disposizione dai linguaggi di scripting. Per quanto concerne l’utilizzo delle caratteristiche di sicurezza va ricordato che sia il PERL sia il PHP implementano un meccanismo che evita il verificarsi di condizioni di overflow del buffer d’input aggiustando automaticamente la dimensione di questo per supportare un’allocazione di memoria consona alla quantità di dati effettivamente ricevuta. Server side um 47 di 243 Inoltre, i linguaggi offrono anche altre funzionalità. PERL può girare in una modalità detta “taint” abilitata da linea di comando mediante l’opzione -T che avvisa nell’eventualità in cui i dati d’input siano passati ad alcune funzioni di sistema tra le quali chmod, chown, exec, connect. CFML mette a disposizione una vera e propria sandbox che può essere sfruttata per limitare l’invocazione delle funzioni di sistema oppure l’uso di certi TAG proprietari. PHP racchiude una modalità detta “safe_mode” che limita l’accesso di alcune funzioni fopen, link, chmod ai soli file di cui è proprietario l’utente di PHP che generalmente coincide con l’utente del server web. ASP consente di disabilitare le funzioni di sistema de-registrando l’oggetto file system del motore di scripting, SCRRUN.DLL. La disabilitazione degli SSI è un’altra misura precauzionale opportuna perchè questi meccanismi possono rivelarsi un’arma a doppio taglio particolarmente insidiosa in quanto se da un lato mettono a disposizione funzionalità interattive utili dall’altro hanno un funzionamento fin troppo elementare da sovvertire per un aggressore poichè si basano sull’impiego di TAG tra i quali “cmd” ed “email” che possono essere inseriti ovunque all’interno di un documento HTML per forzare l’esecuzione di comandi in locale sul server. Considerazioni analoghe spingono a evitare, se possibile, l’uso di TAG nascosti, specie all’interno di form, per raccogliere informazioni sensibili che possono essere alterate con estrema facilità. IDENTIFICAZIONE E USO DI MECCANISMI DI LOGGING E AUDITING La raccolta dei dati relativi al sistema, al traffico di rete, alle attività degli utenti e al server web è essenziale poichè collezionando e analizzando queste informazioni diventa possibile non soltanto scoprire i segni di eventuali intrusioni e determinare la portata delle azioni compiute dall’aggressore ma anche individuare tutte quelle operazioni di “probing” che, pur non rappresentando dei veri e propri attacchi, sono sintomi di un sicuro interesse di qualcuno verso il sistema e in particolare verso le sue vulnerabilità. L’approccio ideale per riconoscere in qualsiasi momento i segni di probabili intrusioni o di altre attività anomale consiste nel paragonare le informazioni relative al funzionamento del sistema in quel dato momento con le analoghe informazioni catturate in un momento anteriore generalmente in fase di prima installazione od operatività che, in quanto tali, costituiscono un’impronta affidabile e caratteristica del funzionamento del sistema stesso. Di regola questo genere d’indicazioni è fornito dal SO, dal S/W che gestisce il server web e da altri meccanismi di terze parti. 1. Monitorare e analizzare la natura e la portata del traffico di rete attraverso la raccolta di questi dati. 1.1. Il numero dei pacchetti, il numero dei byte e delle connessioni in entrata e uscita suddivisi per protocollo, indirizzi IP sorgenti e destinazione e porte. 1.2. Le intestazioni e il contenuto dei pacchetti ricevuti. 1.3. Il numero dei socket aperti. 1.4. Gli errori che si verificano sulle interfacce di rete e lo stato di queste ultime. 1.5. Le connessioni effettuate e i tentativi di connessione falliti suddivisi per protocollo, nome dell’host/utente, porte, indirizzi, data e ora. 1.6. La durata e il flusso di ciascuna delle connessioni. 2. Monitorare l’utilizzo del sistema attraverso la raccolta di questi dati. 2.1. L’ammontare totale delle risorse in uso CPU, memoria e dischi. 2.2. Lo stato del file system per ciascuna partizione in termini di spazio libero, numero dei file aperti e statistiche d’I/O. 2.3. I cambiamenti nello stato del sistema shutdown e riavvii. 2.4. La data e l’ora di avvio dei vari processi, la loro durata nonchè la percentuale di risorse, le periferiche e il numero dei file che ciascuno di essi adopera. 2.5. Gli errori e i warning segnalati dal sistema. Server side um 48 di 243 3. Monitorare gli utenti e le attività di questi attraverso la raccolta di dati. 3.1. Login data e ora, numero di tentativi falliti, tentativi di accesso come utenti privilegiati e logout. 3.2. I cambiamenti e le modifiche riguardanti i privilegi degli utenti e l’uso dei privilegi stessi. 3.3. L’esecuzione di azioni che richiedono particolari privilegi. 3.4. Il numero dei processi avviato. 4. Monitorare e verificare l’integrità dei file attraverso la raccolta di questi dati. 4.1. Il numero e il timestamp di file e/o cartelle e i loro attributi. 4.2. Il checksum per tutti i file e le cartelle più importanti, file di sistema e di configurazione, applicazioni e strumenti per la sicurezza, file contenenti dati rilevanti. 4.3. I cambiamenti nelle dimensioni, nel contenuto e nella protezione dei file. 4.4. L’aggiunta, la cancellazione e la modifica di file e/o cartelle. 4.5. La presenza di eventuali “alternate data streaming” all’interno dei file. 4.6. I risultati delle scansioni antivirus. 5. Raccogliere e analizzare i file di log per i vari servizi di rete web, mail e FTP. Naturalmente la mole d’informazioni che deriva da simili operazioni di monitoraggio può essere veramente considerevole per cui è sempre opportuno effettuare un giusto bilanciamento tra l’importanza della attività di raccolta dei dati e le risorse disponibili per memorizzarli. Qualunque sia la quantità d’informazioni che si decide di catturare è essenziale che questa fotografia del funzionamento del sistema sia ripetuta in relazione ai vari aggiornamenti che sono apportati al sistema stesso, installazione di nuovo S/W, aggiornamento dei file del SO, applicazioni delle patches di sicurezza, in modo da garantire l’attendibilità dei risultati che scaturiscono dal raffronto tra i dati. Per non rischiare di vanificare inutilmente i meccanismi di logging e auditing è conveniente adottare le seguenti precauzioni. Posizionare i log su un host separato dedicato esclusivamente a questa finalità e posto all’interno di una sottorete protetta da firewall. Configurare questo host in modo da scongiurare o limitare fortemente gli effetti di un possibile attacco di tipo DOS che può verificarsi se l’aggressore cerca di saturare le risorse impiegate per il salvataggio dei dati in modo da far cessare il logging. Proteggere i log in modo tale da consentire l’accesso a essi e agli strumenti utilizzati per la loro configurazione soltanto agli utenti debitamente autorizzati. Criptare i log contenenti informazioni sensibili nel momento stesso della loro registrazione. Eliminare la possibilità che i dati una volta scritti possano essere modificati, usando per la registrazione supporti di tipo DVD (Digital Versatile Disk). Predisporre regole per l’archiviazione dei log e la loro dislocazione in luoghi e su supporti fisicamente sicuri. Analizzare i log con cadenza periodica avvalendosi degli strumenti esistenti a tal fine. Per diminuire il volume dei dati da analizzare periodicamente e quindi favorire un riscontro più puntuale dei dati possono essere impiegate tecniche cosiddette di rotazione che consistono nel creare copie dei log online a intervalli regolari, rinominare queste copie e analizzare i dati in esse contenuti in modo da poter contare sempre su una collezione di log relativi a ben precisi momenti temporali. MANTENIMENTO DELL’INTEGRITÀ DELLE RISORSE È determinante per scongiurare gli effetti delle azioni che l’aggressore può compiere in caso di penetrazione e che generalmente consistono nei seguenti modi. Nella sostituzione dei file di sistema con analoghi file, modificati e alterati nelle loro funzionalità e nella modifica dei file di log per cercare di cancellare le proprie tracce. Server side um 49 di 243 Nell’installazione di strumenti, backdoor che consentono di rientrare nel sistema in qualsiasi momento per riprenderne il controllo. Nella modifica delle pagine web in modo che riproducano informazioni fuorvianti o offensive e/o addirittura nella cancellazione dell’intero sito. Per tutelarsi nei confronti di questi abusi è innanzitutto necessario predisporre una serie di meccanismi idonei a monitorare lo stato, le modifiche e l’integrità del file system e individuare i seguenti fattori. Le modifiche del file system come la creazione di nuovi file/cartelle e/o la modifica e cancellazione di file esistenti. I cambiamenti nella dimensione, nel contenuto, negli attributi e nelle regole di accesso ai file. Le alterazioni della consistenza dei log, variazioni nella dimensione e buchi temporali nella registrazione degli eventi. La presenza di virus, backdoor e cavalli di troia. Le alterazioni del file delle password. Inoltre, è importante predisporre delle misure idonee a consentire il ripristino di tutti i file e applicazioni in caso di compromissione dell’integrità degli stessi. La creazione di copie di backup. Il salvataggio di queste copie in forma criptata su supporti di memorizzazione a sola lettura o su un altro host posto in una sottorete protetta attraverso l’uso di firewall. La restrizione dell’accesso e dell’uso delle copie ai soli utenti debitamente autorizzati. La predisposizione di procedure da seguire per il ripristino dello stato dei file alterati o modificati. VULNERABILITÀ D’INTERPRETI E SCRIPT Una delle caratteristiche più interessanti dei server web è rappresentata dalla loro capacità d’interagire con gli utenti attraverso l’elaborazione dell’input per mezzo di appositi script CGI, ASP, PHP che, a loro volta, sono interpretati da altre applicazioni esterne. Purtroppo il prezzo da pagare a fronte dell’esistenza di queste funzionalità aggiuntive è una diminuzione intrinseca dei livelli di sicurezza del server derivante dall’introduzione di meccanismi il cui funzionamento s’inserisce all’interno dei normali processi che gestiscono il servizio HTTP. Infatti, la presenza di malfunzionamenti, generalmente imputabile a imperfezioni del codice, introduce dei punti deboli nel funzionamento di questi meccanismi che possono essere sfruttati da un aggressore per tentare di eseguire localmente sul server determinati comandi. Il risultato di queste azioni è in genere molto variabile da caso a caso ma in alcuni ipotesi è addirittura costituito dalla fuoriuscita all’esterno d’informazioni sensibili, dall’invio tramite email del file delle password, dalla modifica o dalla cancellazione d’informazioni del registro di Windows, fino ad arrivare, nei casi più gravi, all’installazione di vere e proprie backdoor e all’acquisizione del controllo completo dell’host da parte dell’aggressore. Di regola la gravità delle conseguenze che scaturiscono da queste anomalie è tanto più grave quanto più elevati sono i privilegi nel contesto dei quali avviene l’esecuzione di questi script o dei processi S/W che l’interpretano. In ogni caso non è corretto pensare che questo tipo di attacchi possa essere evitato dalla semplice circostanza che i parametri per l’invocazione degli script sono normalmente veicolati da una form HTML sotto forma di una sequenza del tipo seguente. parametro1=valore1¶metro2=valore2 Poiché questa rappresenta un’ipotesi di corretto utilizzo che, peraltro, non esclude affatto la possibilità di abusi. Nulla vieta, infatti, ad un aggressore di costruirsi manualmente una stringa URL che tenti Server side um 50 di 243 di alterare il normale flusso di esecuzione dello script o dell’interprete sfruttando alcune tecniche particolari. L’abuso dei metacaratteri e/o delle sequenze di escape con conseguente encoding della URL, peraltro questa tecnica è anche utile per bypassare quei meccanismi di scoperta delle intrusioni basati sulle cosiddette “signatures”. L’overflow del buffer d’input e l’induzione all’esecuzione di porzioni di codice arbitrario attraverso tecniche cosiddette di “code injection”. L’esecuzione e/o l’invocazione non controllata di particolari funzioni di sistema. Gli exploit si possono suddividere in due categorie fondamentali. 1. Exploit severi: espongono il sistema ad un elevato rischio di compromissione. 2. Exploit minori: producono conseguenze molto meno negative dei primi, come ad esempio una eccessiva fuga d’informazioni all’esterno. Vulnerabilità Presuppone l’esistenza di uno o più punti deboli che rappresentano un potenziale fattore di rischio per qualsiasi sistema poiché consentono a chiunque d’introdursi all’interno dello stesso e danneggiarlo in qualche modo. Exploit È una tecnica che, facendo leva sulle vulnerabilità di un sistema, di un protocollo, cerca di causare un comportamento anomalo e imprevisto, di regola, anche se non sempre, gli exploit hanno un campo di applicazione limitato poiché sono strettamente legati alla presenza di un determinato SO e/o servizio di rete o di particolari configurazioni. DDOS Produce come risultato la completa paralisi dell’attività del server. Per provocare una situazione di questo genere è sufficiente inoltrare in modo ripetitivo un numero considerevole di richieste di connessione HTTP ciascuna seguita dall’invio all’host di un buffer di 200 caratteri. L’elevato numero delle richieste può causare sul server un incremento della memoria di sistema impegnata con conseguente saturazione delle risorse. Non è facile eseguire con successo questo tipo di attacco, specie se proviene da una fonte soltanto, poichè spesso esiste una considerevole differenza tra le risorse del sistema attaccante e quelle del target che potrebbe benissimo essere in grado di far fronte a centinaia di richieste senza per questo vedere degradare le proprie prestazioni. Backup e ripristino dei dati La cancellazione o l’alterazione delle informazioni a causa di malfunzionamenti di carattere generale o per l’esecuzione di operazioni dannose può comportare delle ingenti perdite, non soltanto di carattere economico, qualora non siano predisposte delle adeguate strategie di backup periodico dei dati. Inoltre, queste procedure non soltanto devono essere previste ma devono altresì essere sottoposte a continui test in modo da assicurare una elevata probabilità di ripristino dei dati in qualsiasi momento. Server side um 51 di 243 DB CENTRALE E DB REMOTI INTRODUZIONE Dato un sistema composto da un DB centrale, localizzato su un server e uno o più DB remoti che non sono sempre connessi con quello centrale, nasce il problema di sincronizzazione dei dati. Il DB centrale conterrà i dati di tutti i clienti, di tutti i prodotti, gli ordini e i fornitori, mentre quelli remoti, installati su smartphone o notebook degli agenti, conterranno solo una parte dei dati: quelli che servono all’agente per il suo lavoro. I dati dovranno essere sincronizzati: quelli nuovi presenti sul DB centrale, come nuovi prodotti o aggiornamenti di prezzi, dovranno essere scaricati sui DB locali e gli ordini o eventuali modifiche alle anagrafiche, dovranno essere inviati dal client al server centrale. S’individuano due importanti problematiche. 1. L’accesso al DB centrale da parte del dispositivo, la sicurezza e i conflitti sui dati; infatti è buona norma non esporre il DB centrale su Internet, non è infatti detto che i client sincronizzino i dati dall’interno della LAN. 2. L’integrità dei dati, quello che può accadere, infatti, è che due o più sistemi remoti, quando sono disconnessi, modifichino lo stesso dato. Due rappresentanti, la mattina, sincronizzano i loro client scaricando l’anagrafica clienti. Il rappresentante RA si reca dal suo cliente CA per prendere un ordine e, mentre è dal cliente, quest’ultimo gli comunica che il numero di telefono è errato. RA aggiorna l’anagrafica di CA sul suo client e fa l’ordine. Per un errore di organizzazione, il rappresentante RB si reca da CA il quale, dopo avergli confermato che è già passato il suo collega, si ricorda di aver dato a RA un numero di telefono errato e comunica ad RB quello corretto. RB aggiorna l’anagrafica di CA sul suo client. La sera, quando RA e RB rientrano in sede e aggiornano i dati sul server centrale, i dati di CA saranno stati modificati su ambedue i client. Qual è il dato che si troverà sul server? Per la corretta gestione di questa problematica, esistono diverse strade. 1. Parzializzazione dei dati: la più sicura ed economica, consiste nel fare in modo che due client non possano modificare lo stesso dato, parzializzando appunto i dati che ogni client gestisce, nel caso dell’esempio, ogni agente dovrebbe avere sul proprio client solo i clienti di competenza; in questo modo, RB non potrà mai aggiornare i dati di CA perchè il dato non è presente sul proprio dispositivo. 2. Sincronizzazione dei dati: si usa Service Broker di SQL Server. 3. Gestione autonoma dei conflitti sui dati: si usano opportuni campi delle tabelle, si può sapere quando un record è stato modificato e se ci si trova di fronte ad un conflitto ci si comporta di conseguenza, è evidente che tale sistema presuppone un grosso lavoro di progettazione e d’implementazione. SINCRONIZZAZIONE SQL Server Mobile Edition permette di stabilire una connessione e scambiare dati con un DB di SQL Server in tre modi. Merge replication È la metodologia più adatta da utilizzare nei client poiché permette di aggiornare i dati in modo autonomo e indipendente sul dispositivo portatile e sul server. Quando il dispositivo è connesso al server, è possibile sincronizzare i dati su entrambi, sia per inviare le modifiche dal client al server sia per ricevere le nuove modifiche dal server. Server side um 52 di 243 Le merge replication sono molto potenti e consentono una dettagliata gestione dei conflitti, cosa frequente quando si lavora con utenti disconnessi. Hanno però lo svantaggio di essere complesse da configurare e, in alcuni casi, da gestire. WS (Web Service) Lascia ai programmatori il compito di gestire l’intero processo di sincronizzazione nonché di gestione dei conflitti ma ha il vantaggio di non dipendere dal DB. RDA (Remote Data Access) È un sistema che consente di accedere e scambiare dati tra un dispositivo mobile e un server centrale, per la connessione al server si usa IIS. È evidente come solo il server web può essere esposto su Internet attraverso il firewall, lasciando il server DB in una sotto rete separata e non esposta. Internamente, RDA è strutturato in due moduli. 1. Uno sul client, per gestire la comunicazione con il DB locale. 2. Un modulo server il cui scopo è quello di gestire le richieste del client. Lato client, l’agente locale si occupa di tracciare le modifiche ai dati delle tabelle che partecipano alla sincronizzazione. Quando uno dei metodi di RDA è richiamato, l’agente locale contatta, attraverso IIS del Server side um 53 di 243 server web, l’agente server che si occupa, attrraverso il data provider, di comunicare con il server DB. La funzionalità RDA permette ad un’applicazione, di accedere ai dati da una tabella di DB remota di SQL Server e di archiviarli in una tabella di DB locale di SQL Server Mobile. Dopo che i dati sono stati archiviati nella tabella locale di SQL Server Mobile, l’applicazione potrà accedere alla tabella locale e quindi leggere e aggiornare i dati in essa contenuti. Una volta che l’applicazione termina di aggiornare i dati locali, può aggiornare nuovamente i record modificati dalla tabella locale nella tabella remota di SQL Server. La funzionalità RDA, si può utilizzare quando non è necessario impiegare tutte le funzionalità messe a disposizione dalla replica di tipo merge come, ad esempio, la risoluzione di conflitti. RDA supporta tre tipi di comandi. 1. Pull: le tabelle e i dati da sincronizzare sono scaricati dal server, i dati da scaricare sul client possono essere filtrati attraverso la clausola WHERE della query da inviare al server, si possono così parzializzare i dati e fare in modo che ogni client ottenga solo quelli di sua competenza. 2. Push: i dati modificati sul client sono inviati al server e inseriti nella tabella corretta. 3. SubmitSql: dà la possibilità di eseguire query SQL direttamente sul server DB, usando l’architettura RDA. Non è possibile richiamare il comando Push su una tabella di cui non è stato fatto prima il Pull e questo perchè, senza aver eseguito il Pull, l’agente client non potrà tracciare le modifiche eseguite su una tabella. L’ordine di esecuzione da seguire quando si usa RDA è il seguente. Se l’ordine non è rispettato, si avranno degli errori nel momento in cui si cercherà di sincronizzare i dati con il server. Se si hanno delle tabelle locali non presenti sul server, per cui non si ha la possibilità di eseguirne il Pull, torna comodo il comando SubmitSql. Prima d’iniziare ad usare RDA si deve installarne la parte server e configurarla. Esempio, progettare un’applicazione per la creazione di ordini usata dai rappresentanti. L’applicazione fa uso di un DB SQL Server Mobile Edition locale per la memorizzazione dei dati che poi dovrà essere sincronizzato con un DB centrale. Come prima operazione, si deve aggiungere al progetto, un riferimento alla libreria SYSTEM.DATA.SQLSERVERCE. Nella finestra del codice scrivere, come prima istruzione. using System.Data.SqlServerCe; Per aggiungere una connessione dati al DB, selezionare il menu Dati/Aggiungi nuova origine dei dati… scegliere il DB Microsoft SQL Server Mobile Edition. Server side um 54 di 243 La prima operazione da fare è il Pull dei dati dal server verso il DB del dispositivo mobile. Essendo un’operazione che potrebbe richiedere del tempo, per evitare di lasciare l’interfaccia bloccata, è meglio far eseguire la sincronizzazione in un thread separato e notificare, mediante dei messaggi in una casella di testo, lo stato di avanzamento della sincronizzazione. private void PullData() { // Pull delle Tabelle InvokeDelegate del = new InvokeDelegate(InvokeMethod); SqlCeRemoteDataAccess rda = null; try { foreach (String table in _tablesName) { this.Invoke(del, new object[]{"Inizio drop tabella " + table}); DBHelper.DropTable(table); this.Invoke(del, new object[]{"Drop tabella " + table + " eseguito"}); rda = new SqlCeRemoteDataAccess(_ServerUrl, DBHelper.ConnString); this.Invoke(del, new object[] { "Inizio Pull tabella " + table }); rda.Pull(table, "SELECT * FROM " + table, _rdaOleDbConnectString, RdaTrackOption.TrackingOn); this.Invoke(del, new object[] { "Pull tabella " + table + " eseguito" }); } this.Invoke(del, new object[] { "Procedura di Pull completata" }); } catch (SqlCeException sqlCeEx) { MessageBox.Show("Si è verificato un errore durante la sincronizzazione dei dati. Ripetere l'operazione", "Mobile Order", MessageBoxButtons.OK, Server side um 55 di 243 MessageBoxIcon.Exclamation,MessageBoxDefaultButton.Button1); } finally { rda.Dispose(); } } _tableName è un array di stringhe contenente i nomi delle tabelle da sincronizzare. foreach richiama per primo il metodo DBHelper che esegue il Drop della tabella. È successivamente creata un’istanza dell’oggetto RD cui si passa l’URL del sito web e la stringa di connessione del DB locale. L’ultima operazione è quella di eseguire il Pull vero e proprio dei dati specificando i seguenti elementi. 1. Il nome della tabella di cui eseguire il Pull. 2. La stringa SQL per decidere quali dati della tabella scaricare sul client, in questa fase è possibile far scaricare sul PDA (Personal Digital Assistant) solo i dati di competenza di uno specifico agente. 3. La stringa di connessione al DB remoto. 4. Il tipo di tracking che si desidera eseguire sulla tabella. Il primo passo è completo, il device può essere scollegato e i dati possono essere modificati. L’operazione opposta al Pull dei dati è il Push degli stessi verso il server centrale. private void PushData() { // Push delle tabelle InvokeDelegate del = new InvokeDelegate(InvokeMethod); SqlCeRemoteDataAccess rda = null; try { foreach (String table in _tablesName) { this.Invoke(del, new object[] { "Inizio Push tabella " + table }); rda = new SqlCeRemoteDataAccess(_ServerUrl,DBHelper.ConnString); rda.Push(table, _rdaOleDbConnectString); this.Invoke(del, new object[] { "Push tabella " + table + " eseguito" }); } this.Invoke(del, new object[] { "Procedura di Push completata" }); } catch (SqlCeException sqlCeex) { MessageBox.Show("Si è verificato un errore durante la sincronizzazione dei dati. Ripetere l'operazione", "Mobile Order", MessageBoxButtons.OK, MessageBoxIcon.Exclamation,MessageBoxDefaultButton.Button1); } finally { rda.Dispose(); } } Dopo aver creato un’istanza di RDA, si richiama il metodo Push passando come argomenti il nome della tabella e la stringa di connessione del server DB. L’agente client di RDA si occuperà d’inviare al server solo i record modificati dall’ultimo Pull della stessa tabella. Esempio, sul notebook dell’agente è installato SQL Server Express, sul server remoto è installato un SQL Server. Quando l’agente salverà i dati, in realtà li “ingloberà” in un messaggio XML che sarà conservato in una coda nel DB interno. Alla prima disponibilità di connessione, il messaggio contenente il dato da salvare sarà inviato al server centrale che lo riceverà e lo elaborerà inserendo il dato. Tutte le modalità per lo scambio di messaggi sono implementate nel Service Broker. Server side um 56 di 243 RIA (RICH INTERNET APPLICATION) INTRODUZIONE In Internet c’è sempre più bisogno di servizi applicativi di tipo classico, come i gestionali, la logistica e la produzione, rivisti però in un’ottica web. Per rispondere a queste esigenze si sono sviluppate nel tempo due metodologie. 1. Applicazioni basate sulle architetture web. 2. Tecnologie AJAX. Non si è riusciti, però, a cambiare la percezione degli utenti finali: l’usabilità e l’interattività era migliore quando si usavano le versioni client/server. Oggi si parla del nuovo modello applicativo: RIA, applicazioni web che possiedono usabilità e interattività pari o migliori alle tradizionali applicazioni desktop. Sono disponibili due tecnologie e ambienti di sviluppo. 1. Adobe Flex. 2. Microsoft Silverlight. Sono punti di riferimento per realizzare RIA attraverso VM (Virtual Machine) grafiche che dovrebbero sostituire i browser, ritenuti datati e incompatibili fra loro. La difficoltà nell’utilizzo di questi strumenti non è solo quella di dover apprendere nuovi linguaggi e nuove architetture ma soprattutto quella di dover suddividere la logica applicativa in due contesti separati. 1. Server side: tanti servizi applicativi che svolgono operazioni atomiche. 2. Client side: la logica di controllo dell’UI che utilizzerà questi servizi. Questo rende più difficile la realizzazione di applicazioni sicure e il controllo dell’applicazione stessa, perché nel lato client è semplice alterare il funzionamento modificando la coordinazione e l’uso dei servizi server; inoltre anche la distribuzione delle versioni successive è più complessa e costosa. Infine, un grave problema è che realizzare applicazioni così frammentate richiede molto tempo e quindi costi elevati per cui la realizzazione di RIA resta un’attività decisamente più onerosa in confronto allo sviluppo di applicazioni tradizionali. Proprio per superare queste difficoltà e rendere possibile a tutti la creazione di RIA, si stanno progettando sistemi che non sono semplicemente dei CASE (Computer Aided Software Engineering) o framework ma veri e propri sistemi di sviluppo che contengono gli strumenti necessari alla creazione di applicazioni RIA di qualunque grado di complessità. PROGRAMMAZIONE RELAZIONALE I componenti del S/W non sono più descritti in tanti file di testo, le classi, senza legami di coerenza intrinseca ma attraverso una struttura relazionale, un grafo che nativamente scopre e memorizza le relazioni fra i vari componenti dell’applicazione. Il tutto avviene automaticamente mentre i programmatori continuano a lavorare con le stesse dinamiche cui sono abituati e con il vantaggio ulteriore di poter utilizzare un solo strumento per tutto il ciclo di vita del S/W, dal disegno del DB, al disegno dell’UI, alla scrittura della business logic, alle definizioni delle stampe e alla creazione di componenti aggiuntivi quali ad esempio i WS. Le comunicazioni fra browser e server non sono più basate su HTML ma su XML compresso e questo rende possibile ottenere l’interattività richiesta dalle RIA. In conclusione, è un unico strumento per gestire tutto il ciclo di vita del S/W. La progettazione e la creazione degli schemi E/R (Entity/Relationship) nel DB. L’implementazione degli oggetti di business tramite la DO (Document Orientation). Server side um 57 di 243 La creazione del presentation manager di tipo RIA, dai form ai report. Il debug e il test delle applicazioni. La personalizzazione a run-time delle applicazioni, compreso il multilingua. La creazione della documentazione utente e tecnica. La gestione delle problematiche di assistenza agli utenti dell’applicazione. La gestione del versioning e dei gruppi di lavoro. SILVERLIGHT Silverlight è un’implementazione ridotta del .NET Framework e del suo motore grafico. Le capacità di WPF (Windows Presentation Foundation) sbarcano anche sul web. Tutto ciò con la stessa possibilità di utilizzo di codice gestito e linguaggi dedicati e che quindi offrono la loro produttività notevolmente maggiore rispetto ad un linguaggio per esempio di scripting, possibilmente dedicato al solo web. Caratteristiche a disposizione del programmatore di applicazioni web. Applicazioni cross-browser e cross-platform, senza alcuna differenza di visualizzazione ed esecuzione, con caratteristiche di sicurezza e affidabilità proprie della piattaforma. L’applicazione può sfruttare l’IS (Isolated Storage) per memorizzare dati locali e consentirne l’utilizzo anche in scenari disconnessi. Applicazioni RIA, in altre parole applicazioni utilizzabili attraverso la rete e un browser. Fornisce la possibilità di audio e video streaming, scalandoli in base alla piattaforma H/W di destinazione, passando per esempio da uno schermo di smartphone a monitor di grandi dimensioni, mantenendo al massimo la qualità. Permette lo zoom, la rotazione, il drag & drop di elementi grafici all’interno del browser. Aggiorna una parte della pagina web senza necessità di refresh completo e quindi d’interruzione dell’UX (User eXperience). È supportato tramite un plugin. Server side um 58 di 243 Architettura È pervasa di componenti .NET classici: CLR (Common Language Runtime), LINQ (Language INtegratedQuery) e XAML. Ci sono diverse differenze tecniche rispetto a Flash. Testi e UI sono codificati secondo lo standard XAML e poiché i file XAML sono file di testo, i contenuti e servizi Silverlight sono analizzabili e indicizzabili dai motori di ricerca molto più velocemente di quelli binari di Flash. Programmazione delle animazioni: Flash si basa su sequenze di fotogrammi che l’autore dell’applicazione, deve definire una per una proprio come quando si disegna un cartone animato; in Silverlight, si possono creare sequenze temporali in cui è sufficiente definire le posizioni iniziali e finali di un oggetto, al resto pensa il compilatore. Silverlight è destinato soprattutto all’interazione dell’utente con l’applicazione. Ad esempio, è possibile interagire con la webcam, stampare contenuti, sentire la pressione del tasto destro del mouse, eseguire operazioni di drag & drop dal file system, il pieno supporto alla tastiera durante la visualizzazione in full-screen e la possibilità di firmare digitalmente il file XAP di distribuzione. Una delle caratteristiche più interessanti riguarda le Trusted Application, in altre parole applicazioni out-of-browser che ricevono maggiori diritti per l’esecuzione di operazioni quali l’interoperabilità con il mondo COM, per esempio Office. Esiste anche la possibilità di visualizzare notifiche “toast” e ospitare il controllo web browser all’interno di applicazioni out-of-browser. Struttura di un’applicazione Il contenuto di una pagina che contiene elementi Silverlight può essere creato utilizzando differenti tecniche. 1. Inline XAML e codice JavaScript. 2. File XAML esterni e file JavaScript. 3. Package compressi che contengono XAML, codice gestito, immagini e media. Per creare un nuovo progetto, fare clic su File/Nuovo Progetto… (CTRL+MAIUSC+N)/Modelli/Visual C#/Silverlight/Applicazione Silverlight. Server side um 59 di 243 I template contengono una configurazione di base di un’applicazione Silverlight, inclusa un’applicazione web già corredata dei file necessari per l’integrazione del plugin nelle pagine web, sia HTML sia ASP.NET. Il primo passo del wizard propone di selezionare il CheckBox Ospita l’applicazione Silverlight in un nuovo sito Web, l’opzione, abilitata di default, consente di creare, contestualmente al progetto Silverlight, una applicazione ASP.NET con l’infrastruttura necessaria per ospitare l’applicazione Silverlight stessa. Quindi, chiede se per ospitare l’applicazione Silverlight si desideri utilizzare un Progetto Applicazione Web ASP.NET o un Sito Web ASP.NET. Server side um 60 di 243 Scegliere la versione del run-time di Silverlight da utilizzare. Non occorre selezionare Abilita servizi RIA WCF, in quanto non si utilizzano le caratteristiche di questo componente. Scegliendo la prima opzione, la Soluzione creata è composta di due progetti. 1. Il primo è un’applicazione web che contiene due file XAML e rappresenta il progetto Silverlight. 2. Il secondo, con il suffisso web, è il progetto server che propone due pagine che consentono di testare la soluzione, una pagina ha l’estensione ASPX, mentre l’altra, indipendentemente dalla piattaforma server, è una pagina HTML. Fare clic su Visualizza/Esplora soluzioni (CTRL+ALT+L), presenta l’organizzazione gerarchica dei componenti del progetto con nome, finestre, codice e moduli. Compilare la Soluzione per capire l’interazione fra i due progetti: la compilazione del progetto Silverlight produce un file con estensione XAP che sarà copiato automaticamente nella cartella CLIENTBIN del progetto web per permettere di testare immediatamente il componente Silverlight nell’applicazione che lo ospita. File SILVERLIGHTAPPLICATION1TESTPAGE.ASPX Il plugin Silverlight è eseguito all’interno del browser come un modulo aggiuntivo ed è istanziato nel codice di una pagina ASPX o HTML attraverso un elemento di tipo object. <body> <form id="form1" runat="server" style="height:100%"> <div id="silverlightControlHost"> <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%"> <param name="source" value="ClientBin/SilverlightApplication1.xap"/> <param name="onError" value="onSilverlightError" /> <param name="background" value="white" /> <param name="minRuntimeVersion" value="5.0.61118.0" /> <param name="autoUpgrade" value="true" /> <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=5.0.61118.0" style="text-decoration:none"> <img src="http://go.microsoft.com/fwlink/?LinkId=161376" alt="Scarica Microsoft Silverlight" style="border-style:none"/> Server side um 61 di 243 </a> </object><iframe id="_sl_historyFrame" style="visibility:hidden;height:0px;width:0px;border:0px"></iframe></div> </form> </body> File SILVERLIGHTAPPLICATION1TESTPAGE.HTML <object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%"> <param name="source" value="ClientBin/SilverlightApplication1.xap"/> <param name="onError" value="onSilverlightError" /> <param name="background" value="white" /> <param name="minRuntimeVersion" value="5.0.61118.0" /> <param name="autoUpgrade" value="true" /> <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=5.0.61118.0" style="text-decoration:none"> <img src="http://go.microsoft.com/fwlink/?LinkId=161376" alt="Scarica Microsoft Silverlight" style="border-style:none"/> </a> </object> Il TAG object specifica il tipo di oggetto Silverlight definendo le proprietà data e type rispettivamente con i valori data:application/x-silverlight-2 e application/x-silverlight-2; attraverso la collezione di oggetti param è possibile specificare una serie d’informazioni utili all’applicazione stessa. Il param source specifica il path del file XAP contenente l’applicazione Silverlight, tale file è il risultato della compilazione del progetto e per consuetudine è posizionato all’interno della cartella CLIENTBIN. L’oggetto accetta molti parametri tra i più utili c’è il param OnError e la sua funzione onSilverlightError. Attraverso questo parametro si specifica il metodo che dev’essere eseguito qualora occorrano errori imprevisti nell’esecuzione del codice. Questo evento è proprio utile durante la fase di sviluppo per identificare quelle eccezioni che possono verificarsi a run-time. Il codice intercetta il tipo di errore e più informazioni possibili sull’eccezione, per poi visualizzarle al programmatore in una finestra di alert. Il param InitParameters consente di passare all’applicazione una serie d’informazioni che possono provenire dalla pagina HTML. Tali informazioni sono definite nella proprietà value attraverso una stringa di coppie nome=valore separata da virgole. <param name="initParameters" value="parameter1=value1,parameter2=value2 " /> Con questa definizione, tali variabili entrano a far parte dell’applicazione e possono essere recuperate subito dopo lo startup. Il metodo d’inserimento di un oggetto Silverlight può essere utilizzato su qualunque tipo di server web, trattandosi di codice HTML. Usando IIS e il supporto ASP.NET, è possibile inglobare un progetto Silverlight nella pagina utilizzando il webcontrol Silverlight. Selezionare con il tasto destro del mouse il progetto Silverlight e fare clic sulla voce Proprietà. Visual Studio permette di gestire gli aspetti principali dell’applicazione; oltre al nome dell’assembly nel quale saranno compilati i file si può specificare il nome del file XAP nel quale sono “impacchettate” in un file unico tutte le risorse da cui l’applicazione dipende per Server side um 62 di 243 funzionare. Il file XAP è un comune file ZIP con diversa estensione nel quale sono compressi tutti gli assembly referenziati nel progetto e tutte le risorse che si desiderano distribuire insieme al progetto stesso. Il file XAP è quello che dev’essere specificato nel param source del TAG object ed è quello che dev’essere distribuito nel sito web. Utilizzando Visual Studio il processo di pubblicazione è automatizzato insieme alla compilazione attraverso la configurazione del pannello Silverlight Applicazione, accessibile dalla voce Proprietà dell’applicazione web. In questa maschera si specificano le applicazioni Silverlight che sono utilizzate nel sito web; a seguito della compilazione della soluzione, il file XAP di ciascun progetto è copiato all’interno della cartella specificata nella voce Percorso nel Web, il suo valore predefinito è CLIENTBIN. File APPMANIFEST.XML Specifica l’elenco delle risorse contenute nel file XAP stesso, la versione del run-time di Silverlight richiesto per l’esecuzione e, soprattutto, l’assembly e la classe di EntryPoint. <Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" > EntryPointAssembly="SilverlightItalia" EntryPointType="SilverlightItalia.App" RuntimeVersion="5.0.31005.0"> <Deployment.Parts> </Deployment.Parts> <AssemblyPart x:Name="Silverlight" Source="Silverlight.dll" /> </Deployment> Questo file è utile al plugin per l’esecuzione del progetto ma in particolare è importante per la definizione della classe di startup come punto d’inizio nell’esecuzione del codice. Questa classe dev’essere di tipo Application e Visual Studio, nel template predispone proprio due file per lo scopo: APP.XAML e APP.XAML.CS. I suddetti file contengono proprio il codice di una classe di tipo Application: seguendo il pattern di code behind, i due file contengono separatamente markup XAML e codice applicativo, per poi essere riuniti in un’unica classe in fase di compil-time. Attraverso essi è possibile gestire il ciclo di vita dell’applicazione, nella fattispecie dell’evento Application_Startup è istanziata e utilizzata quella classe che rappresenta concretamente il vero progetto: la classe MainPage. Il progetto Silverlight contiene due file. Server side um 63 di 243 File APP.XAML Definisce l’applicazione. <Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="SilverlightApplication1.App" > <Application.Resources> </Application.Resources> </Application> File MAINPAGE.XAML È la pagina di default che è caricata all’avvio dell’applicazione. La classe MainPage è l’UserControl principale che contiene la grafica del menu e la logica che gestisce l’UI. In Silverlight le UI si definiscono attraverso gli oggetti di tipo UserControl che possono contenere sia l’interfaccia grafica sia la logica applicativa. Uno UserControl può essere definito come un raggruppamento di più controlli in modo da creare una funzionalità complessa e specifica per l’applicazione. L’attributo x:Class deve corrispondere al nome della classe del code behind associato alla pagina. <UserControl x:Class="SilverlightApplication1.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> <Grid x:Name="LayoutRoot" Background="White"> </Grid> </UserControl> La definizione di DesignHeight e DesignWidth imposta la grandezza dell’area occupata dal plugin al 100% dell’area disponibile nel browser, tuttavia la zona utilizzabile per la presentazione dell’UI è definita proprio con le dimensioni dell’UserControl principale. Esempio, utilizzare come controllo principale un pannello Grid, capace di sistemare gli elementi figli in righe e colonne e che occuperà il 100% dello spazio disponibile. <UserControl x:Class="SilverlightApplication1.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400"> <Grid x:Name="LayoutRoot" Background="Aquamarine"> <Grid.RowDefinitions> <RowDefinition Height="50"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!-- Header Area--> Server side um 64 di 243 <Border Grid.Row="0" Height="50" VerticalAlignment="Top" BorderBrush="White" BorderThickness="0,0,0,4"> <TextBlock Text="Esempio Silverlight" FontSize="24" Foreground="Black" VerticalAlignment="Bottom" Margin="5,0,0,5" /> </Border> <Grid Grid.Row="1" > <!-- Master/Details Area --> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition Width="300"/> </Grid.ColumnDefinitions> <Border Grid.Column="0" x:Name="MasterControl" Background="Yellow"/> <Border Grid.Column="1" x:Name="DetailsControl" /> </Grid> </Grid> </Grid> </UserControl> La griglia definisce due righe, la prima delle quali ha un’altezza fissa di DIP (Device Independent Pixels), mentre la seconda occuperà lo spazio rimanente. Immediatamente sotto la definizione delle righe, si trova il TAG Border che è posizionato nella prima riga grazie all’impostazione Grid.Row="0". Questa tipologia di proprietà è chiamata attached property, in altre parole è una proprietà esposta dal contenitore, in questo caso Grid che è agganciata (attaccata) ai controlli figli per semplicità di sviluppo. Border è un controllo figlio e non una proprietà, infatti, è un oggetto che rappresenta una cornice e propone una serie di proprietà, per esempio BorderThickness rappresenta la dimensione di ogni lato della cornice partendo dal lato sinistro in senso orario. Nell’esempio, quindi, l’unico lato a essere riempito di bianco, tramite la proprietà BorderBrush, è il lato inferiore. Il controllo Border è un controllo che può contenere un solo elemento figlio: nell’esempio è rappresentato da un’etichetta testuale identificato da un controllo TextBlock che propone una serie di proprietà, per esempio Margin che rispetta le stesse regole della proprietà BorderThickness. Grid.Row="1", definisce nella seconda riga, un’ulteriore griglia che consente di suddividere il contenuto in due colonne per creare un’interfaccia di tipo Master/Detail. Eseguire l’applicazione e provare a modificare la dimensione della finestra del browser per apprezzare che la colonna si adatta alla dimensione della finestra, mentre il titolo e la parte di destra restano fisse. Server side um 65 di 243 Il programmatore deve dare la possibilità all’utente di decidere la dimensione delle colonne. Per questo scopo c’è il controllo GridSplitter, per utilizzarlo occorre aggiungere un riferimento, nel progetto Silverlight, alla libreria SYSTEM.WINDOWS.CONTROLS. Il parser XAML di Silverlight conosce solo i controlli prestabiliti, quindi per utilizzare elementi esterni come il controllo GridSplitter, si deve dichiarare il namespace SYSTEM.WINDOWS.CONTROLS, definito nell’assembly omonimo e assegnare un alias al namespace XML. Nel codice seguente, nel TAG UserControl si definisce come xmlns:controls il mapping verso il namespace .NET che contiene il controllo GridSplitter. xmlns:controls="clr-namespace:System.Windows.Controls;assembly = System.Windows.Controls" Dopo l’operazione di mapping si può referenziare il controllo nella pagina tramite la sintassi seguente. <controls:GridSplitter Grid.Column="0" HorizontalAlignment="Right" Width="5" /> Il controllo è stato poi posizionato all’interno della griglia allineato a destra nella prima colonna. Oggetto di avvio Le applicazioni Silverlight hanno un’unica “pagina” di partenza nella quale dovranno essere presenti tutti i controlli di cui l’applicazione necessita. Infatti, per cambiare questa pagina iniziale, non è disponibile un metodo strutturato ma si dovrà modificare manualmente una proprietà del file APP.XAML che rappresenta l’applicazione Silverlight, è un po’ come il form di partenza delle WinForms che è unico. La proprietà da modificare per cambiare pagina iniziale si chiama RootVisual ed è esposta dalla classe base Application da cui deriva la classe App del file APP.XAML. Affinché al prossimo avvio dell’applicazione sia mostrato il nuovo file al posto del precedente PAGE.XAML, si deve aprire il file APP.XAML.CS e individuare il metodo Server side um 66 di 243 Application_Startup nel quale è assegnata la proprietà RootVisual. private void Application_Startup(object sender, StartupEventArgs e) { //this.RootVisual = new DeepZoomDemo(); this.RootVisual = new ProgressBarDemo(); } Deep Zoom Composer Permette di visualizzare sul web immagini a risoluzione molto alta senza che questo rallenti la navigazione. In pratica, l’immagine ad alta risoluzione di partenza è spezzettata in immagini di dimensioni inferiori, in modo da creare una specie di “piramide” costituita dalla stessa immagine a risoluzioni differenti via via sempre più alte. In questo modo è possibile caricare immediatamente l’immagine alla sua risoluzione più bassa, per passare poi progressivamente e solo in seguito alla richiesta dell’utente, a visualizzare le risoluzioni più alte che saranno caricate in maniera sequenziale. Permette anche di comporre una vera e propria “scena” formata da diverse immagini opportunamente sovrapposte o allineate tra loro in modo da creare un’immagine più grande che sarà poi possibile “navigare” grazie alla funzionalità DeepZoom. Esempio, creare le immagini con Deep Zoom Composer per poi passare al codice Silverlight per ottenere l’effetto desiderato. Al termine della procedura di composizione delle immagini, copiare la cartella generata dal tool, all’interno della cartella CLIENTBIN dell’applicazione web che si utilizza per richiamare l’applicazione Silverlight. Il controllo MultiScaleImage contiene l’immagine da visualizzare, espone la proprietà Source che va impostata con il percorso del file generato dal tool. Oltre a questo, si devono gestire i vari eventi necessari affinché l’immagine si possa navigare con il mouse oltre a permetterne lo zoom attraverso la rotellina. File DEEPZOOMDEMO.XAML <UserControl x:Class="SilverlightTipsAndTricks.DeepZoomDemo" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="400" Height="300"> <Grid x:Name="LayoutRoot" Background="White"> <MultiScaleImage x:Name="dzImage" Source="F:/Esercizi/Silverlight/SilverlightTipsAndTricks/SilverlightTipsAndTricks.Web/Clie ntBin/fiore/dzc_output.xml" MouseLeftButtonDown="dzImage_MouseLeftButtonDown" MouseLeftButtonUp="dzImage_MouseLeftButtonUp" MouseMove="dzImage_MouseMove"></MultiScaleImage> </Grid> </UserControl> File DEEPZOOMDEMO.XAML.CS using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; Server side um 67 di 243 using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; namespace SilverlightTipsAndTricks { public partial class DeepZoomDemo : UserControl { Point currentMousePos = new Point(); Point mousePos = new Point(); Point mousePoint = new Point(); bool dragging = false; double zoomFactor = 1; public DeepZoomDemo() { InitializeComponent(); MouseMove += new MouseEventHandler(DeepZoomDemo_MouseMove); new MouseWheelHelper(this).Moved += new EventHandler<MouseWheelEventArgs>(DeepZoomDemo_Moved); } private void DeepZoomDemo_Moved(object sender, MouseWheelEventArgs e) { double newzoom = zoomFactor; newzoom = ((e.Delta > 0) ? newzoom /= 1.2 : newzoom *= 1.2); Point logicalPoint = dzImage.ElementToLogicalPoint(currentMousePos); dzImage.ZoomAboutLogicalPoint(zoomFactor/newzoom,logicalPoint.X,logicalPoint.Y); zoomFactor = newzoom; } private void DeepZoomDemo_MouseMove(object sender, MouseEventArgs e) { currentMousePos = e.GetPosition(dzImage); } private void dzImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { mousePos = e.GetPosition(dzImage); mousePoint = dzImage.ViewportOrigin; dragging = true; } private void dzImage_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { dragging = false; } private void dzImage_MouseMove(object sender, MouseEventArgs e) { if (dragging) { Point newPoint = mousePoint; Point mousePosition = e.GetPosition(dzImage); newPoint.X += (mousePos.X - mousePosition.X) / dzImage.ActualWidth * dzImage.ViewportWidth; newPoint.Y += (mousePos.Y - mousePosition.Y) / dzImage.ActualWidth * dzImage.ViewportWidth; dzImage.ViewportOrigin = newPoint; } } } } Anche in Silverlight è possibile eseguire Unit Test utilizzando due specifiche librerie. MICROSOFT.SILVERLIGHT.TESTING.DLL. MICROSOFT.VISUALSTUDIO.QUALITYTOOLS.UNITTESTING.SILVERLIGHT.DLL. Server side um 68 di 243 LIGHTSWITCH INTRODUZIONE È una piattaforma di sviluppo che permette di creare applicazioni data-driven sia per il cloud sia per il desktop secondo il modello three-tier, in altre parole un’applicazione LOB (Line Of Business), basate sui dati, con pochi clic e in modo semplice come premere un interruttore della luce: da cui il nome LightSwitch. C’è anche la possibilità d’installare un’applicazione in Windows Azure, con l’automatica migrazione dei dati in un DB SQL Azure. Si creano le tabelle, si creano le maschere e LightSwitch prepara l’infrastruttura. Si esegue l’applicazione funzionante senza scrivere codice. Le applicazioni gestionali, anche se si tratta di applicazioni “verticali”, fanno un certo numero di attività che sono comuni a tutti gli altri gestionali. I programmatori progettano applicazioni gestionali seguendo una sequenza di azioni sempre uguali. Creare un DB o connettersi ad un DB esistente, creare l’interfaccia utente con varie modalità griglia, master-detail, scheda singola, modulo di ricerca con selezione dati, associare i controlli dell’interfaccia all’origine dati, eseguire le operazioni CRUD, esportare una tabella di dati verso Excel, sono tutte abilità che devono essere fornite all’applicazione. Non può sostituire strumenti professionali come Visual Studio ma può essere utile in vari contesti, nascondendo all’utente molti dettagli dell’architettura, evitandogli di dover conoscere il linguaggio di programmazione, almeno per i progetti più semplici. Le applicazioni LightSwitch sono però completamente configurabili e si può accedere all’editor di codice per aggiungere il codice che rappresenta le regole di business che si vogliono introdurre nell’applicazione. STRUMENTI Le applicazioni LightSwitch utilizzano tecnologie e pattern d’avanguardia. Silverlight e WCF. RIA Services e l’Office Automation per l’integrazione con Office. SQL Server e SQL Azure. SharePoint. Pattern MV-VM (Model View-View Model). TIPI DI DATI Ci sono due nuovi tipi di dato specifici per EmailAddress e PhoneNumber, sono importanti perché incorporano funzionalità di validazione dei dati. Binary Boolean Date, DateTime Decimal, Money Double EmailAddress, PhoneNumber, String Int16 Server side byte[] bool DateTime decimal double string short um 69 di 243 Int32 Int64 Sbyte Single TimeSpan Guid int long SByte float TimeSpan Guid ARCHITETTURA Una LOB è organizzata in tre livelli. 1. Livello di presentazione: Client Tier si occupa dell’interfaccia utente, recuperando i dati dagli altri livelli e inviando le modifiche. 2. Livello relativo alla logica di business: Middle Tier è il livello che smista le richieste del livello di presentazione al livello dati, gestisce la sicurezza per garantire che l’utente abbia i permessi per visualizzare e/o modificare determinati dati e effettua le elaborazioni secondo le regole di business. 3. Livello dati: Data Access è costituito dal DBMS o da un’applicazione che si occupa di gestire la persistenza dei dati e dei documenti archiviati. Questi tre livelli sono tra loro interconnessi, perché ciascuno dei livelli fornisce servizi che servono agli altri livelli. Creare un’applicazione gestionale a tre livelli non è un compito semplice, perché per ogni livello ci sono innumerevoli scelte da fare, sia dal punto di vista della tecnologia da utilizzare, sia da quello delle tecniche da adottare, per esempio il problema della sicurezza. TIPOLOGIE DI APPLICAZIONE Sta iniziando una nuova “era di rottura” sulla divisione tra applicazioni desktop e web, perché ormai è possibile trasformare un’applicazione desktop in un’applicazione web o per il cloud e viceversa con un semplice clic. Questo era impensabile, fino a oggi, per le nette differenze delle piattaforme. In LightSwitch si può passare da un’applicazione desktop ad un’applicazione web, con un clic del mouse; è sufficiente aprire la finestra delle proprietà dell’applicazione, doppio clic sulla voce Properties, nella finestra Esplora Soluzioni e spostarsi alla scheda Application Type. In questa scheda è possibile scegliere tre tipi di applicazione ma più precisamente tre architetture diverse. Server side um 70 di 243 La scheda Access Control permette d’impostare il livello di sicurezza dell’applicazione. Inoltre, è possibile definire i ruoli per esempio Utente, Superutente, Amministratore, gli utenti appartenenti a ciascun ruolo e i permessi da assegnare a ciascuno di essi o a ciascun ruolo. APPLICAZIONE DESKTOP L’ambiente di sviluppo s’integra in Visual Studio, aggiungendo ai template di progetto disponibili l’applicazione LightSwitch, per la quale è possibile utilizzare sia il linguaggio Visual C# sia Visual Basic. I programmatori si trovano di fronte ad un problema che è costituito dalla necessità di collegare un DB di SQL Server ad un’istanza di SQL Server già installata sul PC. Se l’applicazione non è installata da programmatore ma dall’utente, come fa a collegare il DB a SQL Server, se esiste già un DB con lo stesso nome, se SQL Server non è installato nel PC di destinazione o se ha un nome d’istanza diverso. Con Visual Studio LightSwitch si ha la possibilità di creare un DB locale, basato su file di SQL Server, definendo le tabelle e le relazioni che servono all’applicazione. In questo modo si risolve il problema dell’installazione, pur mantenendo alcune importanti caratteristiche dei file di DB di SQL Server: l’accesso protetto ai dati, la robustezza e la Server side um 71 di 243 stabilità della struttura dei file. Esempio, progettare un DB e definire tabelle e relazioni per una struttura master-detail come può essere quella delle fatture. La parte master corrisponde all’intestazione della fattura: numero e data fattura, dati del cliente, condizioni di pagamento. La parte detail è formata dalle singole righe di ciascun prodotto o servizio fatturato: codice, descrizione, prezzo unitario, quantità, importo totale, aliquota IVA. Creare un nuovo progetto, fare clic su File/Nuovo progetto… (CTRL+N), selezionare nella finestra Nuovo progetto e in Modelli installati LightSwitch. Usare il template per Visual Basic e chiamarlo LS_masterdetail. Creazione delle tabelle Nella prima schermata, si può scegliere di creare una nuova tabella Create new table oppure di aprire una connessione ad un DB esterno Attach to external database. S’inizia da Create new table per creare una nuova tabella, selezionare l’intestazione Table1ItemSet e modificarla in Fatture_. Quando si sposta il cursore dall’intestazione, si vede che nella finestra Esplora Soluzioni sarà modificato il nome della fonte dati in Fatture_Set. A questo punto, inserire i campi della tabella. Server side um 72 di 243 In ogni tabella è inserito di default un campo ID di tipo Int32, è la PK. Creare la seconda tabella, con i dati delle righe di dettaglio della fattura. Per avviare la creazione di una nuova tabella, in Esplora Soluzioni fare clic con il pulsante destro del mouse sul nodo ApplicationData e selezionate Add Table. Cambiare il nome della tabella in Dettagli_. A questo punto, inserire i campi della tabella. Server side um 73 di 243 Creare la tabella relativa ai dati dei clienti, cambiare il nome della tabella in Clienti_. A questo punto, inserire i campi della tabella. Creazione delle relazioni tra tabelle Nella parte superiore della finestra di definizione delle tabelle ci sono alcuni pulsanti. Per definire una nuova relazione, fare clic sul secondo pulsante Relationship…. Le relazioni da creare sono due. 1. Clienti e Fatture. 2. Fatture e Dettagli. In entrambi i casi, alla prima tabella è assegnata la proprietà Multiplicity pari a One, mentre la seconda tabella ha la proprietà impostata a Many, in altre parole la classica relazione uno-a-molti, indicato anche più sinteticamente con (1:N). La proprietà On Delete Behavior è la proprietà che definisce il comportamento da adottare in caso di cancellazione di un record: l’opzione preimpostata è Restricted ma se Server side um 74 di 243 si vuole è possibile impostarla con l’opzione Cascade delete (cancellazione in cascata). In pratica, con questa ultima opzione, se si cancella un record cliente, sono cancellate tutte le fatture associate a tale cliente e, ancora in cascata, tutti i record di dettaglio di ciascuna fattura. Nel caso di un gestionale, in genere è meglio controllare in modo puntuale la cancellazione, prima di effettuare una cancellazione a cascata. Nella figura si può osservare la struttura delle relazioni di tabella, focalizzata sulla tabella Fatture. Per navigare tra le tabelle, è sufficiente fare un doppio clic sulla tabella desiderata e subito si vede cambiare la visualizzazione, con il focus su tale tabella. Notare che le tabelle, dopo la definizione delle relazioni, sono state modificate: infatti, sono stati aggiunti automaticamente i campi necessari per inserire il riferimento al record associato nella tabella correlata: chiave esterna. Le chiavi esterne sono implicite, poiché non sono mai visualizzate nelle schermate ma sono gestite in modo totalmente trasparente all’utente. Creazione delle schermate per la gestione dei dati Per definire un nuovo screen, una visualizzazione, una schermata, fare clic sul quinto pulsante Screen…, è possibile selezionare cinque tipi di schermate. 1. New Data Screen per l’inserimento di nuovi record. 2. Search Data Screen per la ricerca di dati. 3. Details Screen di tipo master-detail. 4. Editable Grid Screen una griglia per la visualizzazione e modifica di dati in forma tabellare, utilizzata per la visualizzazione di un cliente, nella casella Screen Name: inserire Clienti. 5. List and Details Screen, di tipo master-detail espresso però come una lista, per la visualizzazione di una fattura. Server side um 75 di 243 Anche se l’applicazione non è terminata, se si esegue si può vedere che questa parte è già funzionante, dato che ora è possibile inserire nuove righe nella tabella Clienti, modificarle e cancellarle. Dopo aver inserito le righe di dati, ricordarsi sempre di cliccare sul pulsante Save, per memorizzare i dati nel DB locale. Durante lo sviluppo dell’applicazione, si può controllare se uno screen potrà essere visualizzato nel menu laterale e quindi essere disponibile nell’applicazione in run-time, evitando così di avviare più volte l’applicazione per verificarne il corretto funzionamento. È sufficiente aprire la finestra delle proprietà dell’applicazione, doppio clic sulla voce Properties, nella finestra Esplora Soluzioni e spostarsi alla scheda Screen Navigation, nel nodo Tasks, sono presenti entrambi gli Screen. Server side um 76 di 243 In ogni screen ci sono dei pulsanti già predefiniti e funzionanti, tra cui anche un pulsante per l’esportazione del set di dati verso un foglio Excel. Il designer mostra la composizione della maschera, elencando i controlli di comando e quelli data-bound, fare doppio su Fatture. DB locale Nella finestra Esplora Soluzioni, i dati sono definiti all’interno del nodo Data Sources, nel DB chiamato ApplicationData. In quest’ultimo nodo sono contenute le tabelle, se si va a vedere all’interno dei file contenuti nel progetto, si scopre che gli oggetti di DB sono mappati nel file APPLICATIONDEFINITION.LSML (LightSwitch Markup Language), contenuto nella cartella Data. Questo però non è il file che contiene i dati inseriti dall’utente ma è solamente il file che contiene la definizione della struttura dei dati: tabelle, campi, relazioni, vincoli. I dati veri e propri si trovano nella sotto cartella BIN\DATA dove ci sono due file, rispettivamente con estensione MDF e LDF. Il DB locale non è altro che un file di DB di SQL Server, scollegato da qualsiasi istanza di Server side um 77 di 243 SQL Server eventualmente installata sul PC. Esaminare la struttura del DB: creare una nuova connessione al DB, dalla finestra Esplora Server, fare clic sulla terza icona Connetti al database. Usare il provider dati File di database Microsoft SQL Server. Una volta aperta la connessione, si vede che il DB contiene le seguenti tabelle. Server side um 78 di 243 Il DB include non soltanto le tabelle create nel corso dello sviluppo dell’applicazione ma anche una serie di altre tabelle accessorie per la definizione di profili, ruoli, utenti, permessi. Inoltre, c’è la presenza di numerose Visualizzazioni e Stored procedure, definite automaticamente durante la creazione del DB. Creare il diagramma delle relazioni tra le tre tabelle, fare clic nella finestra Esplora Server sul pulsante Diagrammi di database. Aprire la finestra delle proprietà dell’applicazione, doppio clic sulla voce Properties, nella finestra Esplora Soluzioni e spostarsi alla scheda General. Questa scheda è dedicata alle proprietà generali dell’applicazione, si può dare il nome all’applicazione, assegnare un’immagine per il logo e un’icona, indicare la “cultura” da utilizzare, per esempio per le impostazioni relative ai numeri, alle valute e alle date, il numero di versione distinto in major version e minor version. Server side um 79 di 243 Esecuzione Salvare, compilare e eseguire l’applicazione. Nella finestra si notano i seguenti elementi. Un controllo ribbon con i comandi principali. Una scheda Task con i comandi per mostrare le maschere. La validazione automatica dei dati, senza scrivere codice. Sul lato sinistro c’è l’elenco degli screen inseriti nell’applicazione. Nella parte alta è presente un controllo ribbon, con alcuni pulsanti per salvare le modifiche e per aggiornare la visualizzazione. Infine, all’interno della finestra di visualizzazione, sono inseriti i pulsanti per aggiungere, modificare o cancellare i record, la casella di ricerca e perfino un pulsante già predisposto per esportare i dati dell’intera griglia in un foglio Excel. Per esempio, se si vuole aggiungere un record alla tabella, basta cliccare sul pulsante Add…. Appare una finestra di dialogo con tutti i controlli già predisposti per l’inserimento dei dati. L’applicazione include anche già delle regole per la validazione dei dati, anche se non può certo prevedere tutte le regole di validazione che possono essere definite in contesti specifici. L’applicazione, dopo la sua creazione attraverso i wizard, può essere completamente personalizzata e modificata, sia nell’aspetto grafico, sia nelle regole di business che devono essere definite dietro le quinte, sia aggiungendo funzionalità che possono essere implementate solamente attraverso il codice Visual Basic o Visual C#. Server side um 80 di 243 APPLICAZIONE WEB Aprire la finestra delle proprietà dell’applicazione, doppio clic sulla voce Properties, nella finestra Esplora Soluzioni e spostarsi alla scheda Application Type. Fare clic sulla terza opzione, per selezionare il tipo di applicazione Browser client, 3-tier deployment. Dopo aver salvato le modifiche, eseguire l’applicazione. Si apre il browser con l’applicazione nella pagina web che ha lo stesso aspetto e le stesse funzionalità dell’applicazione per desktop. Server side um 81 di 243 CONNESSIONE A UN DB ESISTENTE Fare clic sulla voce Attach to external database, apparirà una finestra di dialogo relativa ad un wizard, un’autocomposizione, per la definizione della connessione all’origine dati. È possibile creare una connessione a un Database, ad un sito SharePoint o anche a un WCF RIA Service, selezionare Database e fare clic sul pulsante Next. Server side um 82 di 243 Il passo successivo definisce le proprietà della connessione, con la finestra di dialogo seguente. Lasciare invariato il provider dati Microsoft SQL Server (SqlClient), perché si ha a disposizione proprio un DB di questo tipo. Dopo aver selezionato il nome del server, del tipo di autenticazione e il nome del DB che interessa, fare clic sul pulsante Test connessione. Server side um 83 di 243 Avuta conferma della correttezza di tutte le proprietà di connessione, il passaggio successivo richiede la scelta delle tabelle che si vogliono inserire nel Data Source. Selezionare la tabella dati e modificare, se si desidera, il nome del Data Source, da quello proposto automaticamente C_APPO_DATI_MDFData dovuto a una ridenominazione del DB collegato all’istanza di SQL Server e fare clic sul pulsante Finish. Sono visualizzate le tabelle e loro relazione, nell’esempio è una sola. Definire uno screen per la visualizzazione della tabella. Server side um 84 di 243 Salvare, compilare e eseguire l’applicazione. QUERY Per avviare la creazione di una nuova query, in Esplora Soluzioni fare clic con il pulsante destro del mouse all’interno del nodo Data Sources/C_APPO_DATI_MDFData/dati/Add Query. Definire uno screen per la visualizzazione della query. Server side um 85 di 243 Salvare, compilare e eseguire l’applicazione. La localizzazione è la possibilità di creare delle versioni dell’applicazione con lingue diverse ma con una limitazione: bisogna creare distribuzioni distinte, una per ogni lingua. Per il cambiamento della lingua nell’interfaccia utente, per gli elementi standard, è sufficiente cambiare la selezione della casella a discesa Culture, nelle proprietà dell’applicazione. A questo punto, riavviando l’applicazione, si vede che le voci standard saranno visualizzate nella lingua selezionata. Tutte le parti non standard dell’applicazione dovranno essere tradotte manualmente. Server side um 86 di 243 MODULO 5 ASP.NET Introduzione Visual Studio Web Form Passaggio d’informazioni Master Page Temi Web User Control Web Control Library Convalida Applicazioni multilingua Rilevazione del browser SEO Server side um 87 di 243 INTRODUZIONE ASP Le pagine ASP utilizzano un modello di threading chiamato STA (Single Threaded Apartment) che stabilisce che tra tutti gli oggetti dati in un determinato thread, solo uno può essere eseguito in un determinato momento; se due o tre oggetti sono pronti per l’esecuzione, prima che il secondo possa iniziare deve terminare il primo e così via. Le pagine ASP.NET usano il modello di threading MTA (Multiple TA), se due o più oggetti sono pronti per l’esecuzione, ASP.NET inizia simultaneamente l’esecuzione di entrambi, per default, dispone di 25 thread per ogni CPU multicore per processare tutte le richieste HTTP relative alle applicazioni ospitate nello stesso processo, questo numero si può alzare da configurazione. Questa capacità migliora le prestazioni in generale ma richiede particolari funzionalità nell’implementazione di ogni oggetto. In STA, un oggetto non deve mai preoccuparsi che un altro possa assumere il controllo esclusivo su una risorsa o che possa aggiornare informazioni in modo inaspettato. In MTA, ogni oggetto deve prendere le precauzioni del caso, altrimenti uno degli oggetti produrrà risultati non corretti. Il .NET Framework fornisce i seguenti strumenti. ASP.NET: permette di scrivere pagine web dinamiche, compilate, eseguite sul server. IIS Express: server web su cui pubblicare le applicazioni web. SQL (Structured Query Language) Server Express LocalDB: è il RDBMS (Relational DataBase Management System) locale su cui ospitare i DB utilizzati dalle applicazioni web. ASP.NET supporta due modalità per creare l’UI. 1. ASP.NET WebForms: è il modello classico, dove ogni pagina è chiamata Web Form, offre un approccio basato su controlli ed eventi, simile alle applicazioni Windows. 2. ASP.NET MVC: è l’implementazione del pattern MVC (Model View Controller) per applicazioni ASP.NET, consente un controllo maggiore in fase di definizione del markup e garantisce la possibilità di testare il codice. ASP.NET CORE È il run-time di ASP.NET, condiviso sia da ASP.NET WebForms sia da ASP.NET MVC. Supporto all’async sia negli HttpHandler sia negli HttpModule. La compilazione e il JIT (Just In Time) del .NET Framework sfruttano al meglio le architetture H/W, parallelizzando il carico, con evidenti benefici in termini di performance. Server side um 88 di 243 Le funzionalità di compilazione multicore sono attivate automaticamente, senza che debba essere fatto nulla da parte del programmatore. PROFILE API ASP.NET ha un supporto fisico per aree protette, fornito attraverso l’uso di alcune funzionalità. Per quanto riguarda la protezione, sono supportati due scenari. 1. Autenticazione integrata di Windows: a carico del server web. 2. Autenticazione tramite form: a carico del programmatore via codice. Per evitare tutto questo, ASP.NET supporta il meccanismo di Membership API che consente di definire un provider che esegue tutto il lavoro. Per quanto riguarda i ruoli sono sfruttate le Roles API. In entrambi i casi si tratta di funzionalità che si basano sul provider Model Design Pattern che prevede la definizione di un provider che implementa concretamente la strategia e di una serie di provider che possono essere definiti nel file WEB.CONFIG. I provider, basati su una classe astratta in comune, possono essere scambiati tra loro, modificando il funzionamento interno di queste due funzionalità. Il provider Model rappresenta un vantaggio in termini di flessibilità, specie nella sua applicazione principale con Membership, Roles e Profile API: consente di scrivere codice usando un’API che, poi, attraverso la dependency injection, attiva un provider opportuno che contiene l’implementazione concreta. Questo consente di scrivere una volta il codice, ad esempio per gestire l’autenticazione e decidere, dal WEB.CONFIG, quale sia l’implementazione e quindi il DB da utilizzare. ASP.NET supporta, inoltre, una serie di controlli chiamati security control che offrono un supporto nativo alle funzionalità di login, creazione utente e recupero password. Con gli Universal Provider, inclusi in ASP.NET e contenuti all’interno del namespace System.Web.Providers, è offerta maggior flessibilità, funzionano anche con SQL Server Compact e SQL Azure. Creando un nuovo progetto, saranno registrati di default. Esempio, Membership provider. <membership defaultprovider="DefaultMembershipProvider"> <providers> <add type="System.Web.Providers.DefaultMembershipProvider" name="DefaultMembershipProvider" ...="" /> </providers> </membership> WEBSOCKET ASP.NET aggiunge una nuova proprietà IsWebSocketRequest all’HttpContext, oltre ad una serie di oggetti tramite i quali gestire questo tipo di comunicazioni. Per sfruttare WebSocket, si deve realizzare un HttpHandler all’interno del quale verificare se si è in presenza di una richiesta di tipo WebSocket. public class WebSocketHandler : IhttpHandler { public void ProcessRequest(HttpContext context) { if (context.IsWebSocketRequest) context.AcceptWebSocketRequest(x => HandleRequest(x)); } private async Task HandleRequest(AspNetWebSocketContext context) { WebSocket socket = context.WebSocket; // ... codice Server side um 89 di 243 } } In caso positivo, è invocato il metodo HandleRequest passando come parametro un AspNetWebSocketContext che si può considerare come l’analogo di HttpContext per questo protocollo e che, nello specifico, si può utilizzare per recuperare un’istanza dell’oggetto WebSocket, tramite il quale si possono ricevere e inviare dati al browser. Esempio, il server risponde con un pingback a ogni messaggio ricevuto dal browser. private async Task HandleRequest(AspNetWebSocketContext context) { WebSocket socket = context.WebSocket; while (true) { var buffer = new ArraySegment<byte>(new byte[1024]); var result = await socket.ReceiveAsync(buffer, CancellationToken.None); if (socket.State == WebSocketState.Open) { string message = Encoding.UTF8.GetString(buffer.Array, 0, buffer.Array.Length); var reply = "Pingback: " + message; buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(reply)); await socket.SendAsync(buffer, WebSocketMessageType.Text,true, CancellationToken.None); } else break; } } Il primo passo è attendere un messaggio da parte del WebSocket remoto con il metodo ReceiveAsync che, alla ricezione, ne memorizza il contenuto binario all’interno di un buffer di byte. A questo punto, se il socket è ancora aperto, si può costruire il messaggio di risposta e inviarlo tramite SendAsync. Nell’esempio si è implementata una comunicazione di tipo richiesta/risposta ma questo non è necessario e quindi si possono inviare messaggi in push dal server al browser in maniera del tutto autonoma. Dal punto di vista del client si può interagire con questo handler creando da JavaScript un oggetto di tipo WebSocket. <script type="text/ecmascript"> $(function () { var socket = new WebSocket("ws://localhost:19534/websocketdemo.ashx"); socket.onmessage = function (msg) { alert(msg.data); }; $('#btnSend').click(function () { socket.send($('#message').val()); }); }); </script> Il protocollo non è più http:// ma ws://; se si sta utilizzando IIS 8 (Internet Information Services) il supporto a questo protocollo è nativo. L’oggetto WebSocket espone un membro onmessage al quale si può agganciare un handler che sarà eseguito alla ricezione di ogni messaggio. Server side um 90 di 243 Nell’esempio, si visualizza il contenuto tramite un alert. L’invio, invece, avviene tramite la funzione send che accetta l’oggetto da inviare al server. SERVIZI L’architettura più innovativa nell’ambito delle applicazioni web è sicuramente quella delle SPA (Single Page Application). Questa tipologia di applicazioni e, ovviamente, anche quelle più “tradizionali” che fanno però un uso massiccio di AJAX si basano sulla possibilità d’invocare una serie di servizi esposti dal server per manipolare i dati e aggiornare i contenuti delle pagine. In un tale contesto, avere a disposizione un’infrastruttura che renda agevole e veloce la realizzazione di questi servizi server side costituisce indubbiamente un enorme valore aggiunto ed è proprio questa la direzione in cui si muove ASP.NET Web API. Esempio, realizzare una serie di servizi per gestire la classe seguente. public class Customer { public int Id { get; set; } public string Name { get; set; } public string Region { get; set; } } Per sfruttare ASP.NET Web API tutto ciò che si deve fare è aggiungere un nuovo controller chiamato CustomersController, è possibile sfruttare anche uno dei template di API Controller e che per esempio conterrà una coppia di action, GetAllCustomers e GetCustomerById. public class CustomersController : ApiController { public IEnumerable<Customer> GetAllCustomers() { return _customers; } public Customer GetCustomerById(int id) { var result = _customers.SingleOrDefault(x => x.Id == id); if (result == null) throw new HttpResponseException(HttpStatusCode.NotFound); return result; } } Questo oggetto presenta alcune differenze rispetto ad un controller tradizionale. La classe base è ApiController, invece che Controller. Le action restituiscono oggetti, invece che istanze di ActionResult. Eventuali errori sono gestiti con status code HTML. Con queste semplicissime righe di codice, il servizio è già pronto e funzionante. Per testarlo, è sufficiente aprire dal browser l’URL (Uniform Resource Locator) seguente. http://localhost:.../api/customers E ottenere in questo modo il risultato di GetAllCustomers o accedere all’URL seguente. http://localhost:.../api/customers/1 Server side um 91 di 243 Per recuperare il customer di il cui ID è 1. È ASP.NET Web API a occuparsi di tutto il resto, per esempio serializzando il risultato in XML o JSON (JavaScript Object Notation) in base alla particolare request che è pervenuta al server e, soprattutto, integrando questo controller con l’infrastruttura di routing in base a delle semplici convenzioni sui nomi. Nelle impostazioni di default, il pattern di base dell’indirizzo è /api/{controller}/{id};. L’action è cercata in base al verbo HTML della request. Se s’invoca il servizio in GET, saranno prese in considerazione le action che iniziano per Get- come GetAllCustomers e GetCustomerById. Se si usa POST sarà cercata un’action con il prefisso Post- e così via. Tra queste action, sarà poi selezionata quella i cui parametri corrispondono all’URL della richiesta: /api/customers non ha alcun parametro, quindi sarà invocata GetCustomers, mentre se si specifica un ID sarà utilizzata GetCustomerById, visto che il metodo ha un argomento che si chiama proprio in questo modo. ASYNC/AWAIT Si hanno a disposizione delle estensioni per i controller che consentono di sfruttare queste parole chiave per l’invocazione di metodi asincroni. Per realizzare un’action asincrona è sufficiente marcarla come async e restituire un oggetto di tipo Task. public async Task<ActionResult> Index() { using (var client = new WebClient()) { var result = await client.DownloadStringTaskAsync("http://www.miosito.com"); ViewBag.Content = result; } return View(); } Il codice dell’action è analogo a quello dell’implementazione sincrona, sebbene questa forma consenta di sfruttare al massimo il server e migliorare quindi le prestazioni e la scalabilità dell’applicazione: durante la chiamata al server remoto, infatti, il thread assegnato da IIS alla gestione della REQUEST non resterà in attesa della risposta ma ritornerà a disposizione del thread pool del server e potrà essere sfruttato per processare un’ulteriore richiesta. BUNDLING È la capacità di unire insieme una serie di file, per formarne uno solo: il vantaggio di questa tecnica è che i browser, per via di come funziona il meccanismo di richiesta di una risorsa tra resolve dei DNS, latenza e numero massimo di richieste contemporanee preferiscono scaricare un solo file di dimensioni maggiori che un numero elevato di file di piccole dimensioni. MINIFICATION È la tecnica di ridurre al minimo possibile un file, per risparmiare banda e rendere più veloce ed efficiente il trasferimento dei file, per esempio CSS e JavaScript sono pieni di caratteri non necessari, spazi, TAB e commenti. In entrambi i casi, queste tecniche sono applicate a run-time con meccanismi di cache, per evitare d’impattare sul server, senza essere obbligati a scrivere file senza commenti o senza spazi o ritorni a capo. In questo caso le istruzioni per avere questo comportamento sono molto semplici e si trovano nel file APP_START\BUNDLECONFIG.CS, aggiunto in automatico ai progetti. Server side um 92 di 243 File APP_START\BUNDLECONFIG.CS using System.Web.Optimization; namespace WebApplication1 { public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { bundles.Add(new ScriptBundle("~/bundles/WebFormsJs").Include( "~/Scripts/WebForms/WebForms.js", "~/Scripts/WebForms/WebUIValidation.js", "~/Scripts/WebForms/MenuStandards.js", "~/Scripts/WebForms/Focus.js", "~/Scripts/WebForms/GridView.js", "~/Scripts/WebForms/DetailsView.js", "~/Scripts/WebForms/TreeView.js", "~/Scripts/WebForms/WebParts.js")); bundles.Add(new ScriptBundle("~/bundles/MsAjaxJs").Include( "~/Scripts/WebForms/MsAjax/MicrosoftAjax.js", "~/Scripts/WebForms/MsAjax/MicrosoftAjaxApplicationServices.js", "~/Scripts/WebForms/MsAjax/MicrosoftAjaxTimer.js", "~/Scripts/WebForms/MsAjax/MicrosoftAjaxWebForms.js")); bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( "~/Scripts/modernizr-*")); } } } Esistono opzioni che consentono di personalizzare diversi aspetti, ma, di default, i bundle funzionano prendendo tutti i file all’interno di un dato percorso, escludendo quelli con suffisso VSDOC o INTELLISENSE, necessari solo all’editor di Visual Studio. Server side um 93 di 243 In una WebForms basterà riferirsi al bundle in questo modo. <%: Scripts.Render("~/bundles/scripts") %> In ASP.NET MVC basterà riferirsi al bundle in questo modo. @Scripts.Render("~/bundles/scripts") Nel caso il bundle riguardi i CSS, bisogna utilizzare la classe StyleBundle e il metodo Styles.Render. Tutto il lavoro, dietro le quinte, è fatto da ASP.NET: fintanto che si è in modalità Debug, l’engine si limiterà a includere i singoli file del bundle, in modo da semplificare l’investigazione di eventuali errori. Una volta messo il sito in produzione, è sufficiente impostare su WEB.CONFIG la modalità di compilazione in release, per attivare l’engine di minification e far sì che tutti i riferimenti siano automaticamente condensati all’interno di un unico link e rielaborati in modo da limitarne la dimensione. SICUREZZA applicativa Si usa il Provider Model per autenticare gli utenti e autorizzarli all’utilizzo dell’applicazione. In pratica, si sfrutta un DB SQL Server e per configurarlo si usa il seguente tool sia da linea di comando, se si passano i relativi parametri sia come wizard, nel caso in cui non si passa alcun parametro. C:\Windows\Microsoft.NET\Framework64\v4.0.30319>aspnet_regsql.exe /? Microsoft (R) ASP.NET SQL Registration Tool versione 4.0.30319.18408 Utilità amministrativa per l'installazione e la disinstallazione delle funzional ità ASP.NET in un'istanza di SQL Server. Copyright (C) Microsoft Corporation. Tutti i diritti sono riservati. -- OPZIONI GENERALI --? Visualizza questo argomento della Guida. -W Modalità guidata. Impostazione predefinita utilizzata se non sono specificati altri parametri. -- OPZIONI DI CONNESSIONE SQL --S <server> Istanza di SQL Server (SQL Server 7.0 o versione successiva) da utilizzare. -U <ID accesso> Nome utente di SQL Server per l'autenticazione. È richiesta l'opzione -P. -P <password> Password di SQL Server per l'autenticazione. È richiesta l'opzione -U. -E Esecuzione dell'autenticazione con le credenziali di Windows correnti. -C <stringa di connessione> Stringa di connessione. Anziché specificare nome utente, password e nome del server, è possibile specificare una stringa di connessione di SQL Server. Tale stringa non deve contenere un nome di database, a meno che non venga specificato diversamente. -sqlexportonly <nomefile> Generazione del file script SQL per l'aggiunta o la rimozione delle funzionalità specificate, senza eseguire l'operazione effettiva. Sono supportate le opzioni seguenti: -A, -R, -ssadd e -ssremove. -- OPZIONI DEI SERVIZI APPLICATIVI -Server side um 94 di 243 -A all|m|r|p|c|w Aggiunge il supporto per una funzionalità. È possibile specificare più valori contemporaneamente, ad esempio: -A mp -A m -A p all: tutte le funzionalità m: appartenenza r: Gestione ruoli p: profili c: personalizzazione w: provider di eventi Web SQL -R all|m|r|p|c|w Rimozione del supporto per una funzionalità. È possibile specificare più valori contemporaneamente, ad esempio: -R mp -R m -R p all : tutte le funzionalità, oltre alle tabelle comuni e alle stored procedure condivise dalle funzionalità m: appartenenza r: Gestione ruoli p: profili c: personalizzazione w: provider di eventi Web SQL -d <database> Nome del database da utilizzare con i servizi applicativi. Se non si specifica il nome del database, verrà utilizzato il database predefinito "aspnetdb". -Q Modalità non interattiva. Non chiede di confermare la rimozione delle funzionalità. -- OPZIONI RELATIVE ALLA DIPENDENZA DELLA CACHE SQL (PER SQL SERVER 7.0 E 2000) --d <database> Nome del database da utilizzare con la dipendenza della cache SQL in SQL 7.0 e SQL 2000. In alternativa, il nome del database può essere specificato utilizzando la stringa di connessione con l'opzione -C. (Richiesto) -ed Abilitazione di un database per la dipendenza della cache SQL. -dd Disabilitazione di un database per la dipendenza della cache SQL. -et Abilitazione di una tabella per la dipendenza della cache SQL. È richiesta l'opzione -t. -dt Disabilitazione di una tabella per la dipendenza della cache SQL. È richiesta l'opzione -t. -t <tabella> Nome della tabella da abilitare o disabilitare per la dipendenza della cache SQL. È richiesta l'opzione -et o -dt. -lt Elenco di tutte le tabelle abilitate per la dipendenza della cache SQL. -- OPZIONI STATO SESSIONE --ssadd Aggiunta del supporto per lo stato sessione in modalità SQLServer. -ssremove Rimozione del supporto per lo stato sessione in modalità SQLServer. -sstype t|p|c Tipo di supporto dello stato sessione: t: temporaneo. I dati dello stato sessione vengono archiviati nel database "tempdb", mentre le stored procedure per la gestione della sessione vengono installate nel database "ASPState". Se si riavvia SQL Server, i dati non vengono mantenuti. (Impostazione predefinita) Server side um 95 di 243 p: persistente. Sia le stored procedure che i dati dello stato sessione vengono archiviati nel database "ASPState". c: personalizzazione. Sia le stored procedure che i dati dello stato sessione vengono archiviati in un database personalizzato. È necessario specificare il nome del database. -d <database> Nome del database personalizzato da utilizzare se -sstype è "c". Si crea un nuovo DB chiamato ASPNETDB contenente le tabelle e le SP necessarie per la corretta gestione delle credenziali. Server side um 96 di 243 VISUAL STUDIO INTRODUZIONE Prima di avviare un qualsiasi progetto web, è importante la scelta degli strumenti più adatti per raggiungere i propri obiettivi. La soluzione di scrivere da zero l’intero codice è inefficiente e richiede un notevole dispendio di tempo e risorse. Per fortuna, è possibile evitare di “reinventare la ruota”, ricorrendo a strumenti che forniscano il codice comune e indispensabile alla maggior parte dei progetti. È possibile creare un sito web oppure un progetto web, in base a questa scelta cambia il modello di compilazione e di distribuzione. Il progetto web è da preferire perché gestisce in maniera più rigorosa il ciclo di sviluppo. Server side um 97 di 243 SITO WEB Per creare un nuovo sito web, fare clic su File/Nuovo/Sito web… (MAIUSC+ALT+N). Nella finestra di dialogo Nuovo sito Web selezionare Modelli/Visual C#/Sito Web ASP.NET vuoto, scegliere il Percorso Web: Filesystem C\INETPUB\WWWROOT. Per distribuire il sito web, è necessario copiare tutti i file all’interno del percorso, in questo caso, è possibile fare una modifica ad un singolo file senza la necessità di caricare l’intera applicazione ma applicando le sole modifiche. Il progetto web necessita, viceversa, di una compilazione per ogni modifica che è fatta, inoltre è necessario copiare tutti i file, a esclusione di quelli con estensione CS il cui contenuto è compilato automaticamente. In entrambi i casi, la cartella BIN contiene tutti gli assembly generati da Visual Studio che sono utilizzati all’interno dell’applicazione. APP_CODE Memorizza classi in formato sorgente, da compilare al volo insieme all’applicazione, supporta un solo linguaggio per volta. APP_DATA Contiene file di dati, per esempio TXT, DB o XML, in pratica è una cartella pensata per contenere file che sono protetti dal download ma che si possono sfruttare nelle pagine. APP_THEMES Include i file legati ai temi. APP_WEBREFERENCES Include i file generati per l’utilizzo delle reference di WS. APP_LOCALRESOURCE Contiene i file di risorse che sono locali alle singole pagine web. APP_GLOBALRESOURCE Contiene le risorse globali cui hanno accesso tutte le componenti dell’applicazione. Server side um 98 di 243 PROGETTO WEB Per creare un nuovo progetto, fare clic su File/Nuovo/Progetto… (CTRL+MAIUSC+N). Nella finestra di dialogo Nuovo Progetto selezionare Altri linguaggi/Visual C#/Web, quindi selezionare il tipo di applicazione, Applicazione Web ASP.NET vuota. Immettere il nome del progetto nel campo Nome:, per esempio WebForm, quindi per creare il progetto, premere OK. Fare clic destro in Esplora soluzioni sul nome del progetto, dal menu contestuale selezionare Aggiungi/Nuovo elemento… (CTRL+MAIUSC+A). Alla voce Visual C# selezionare Web/Web Form. File WEBFORM1.ASPX Selezionare il menu Visualizza/Finestra di progettazione (MAIUSC+F7), si apre l’editor visuale della pagina web in modo Progettazione. Dalla Casella degli strumenti trascinare nella finestra Progettazione un’etichetta e un pulsante, la pagina di markup è la seguente. Server side um 99 di 243 Nella finestra Proprietà sono visualizzate le proprietà dell’oggetto. Selezionare il modo Origine per visualizzare il codice HTML. L’attributo runat="server" indica che la pagina è eseguita server side: s’istruisce il server che, alla pressione del pulsante, quando sarà eseguito il PostBack della pagina, dev’essere richiamato il metodo btnPremi_Click contenuto nel code-behind. L’etichetta è rappresentata dal TAG Label, ha una proprietà ID che serve a identificare univocamente il controllo all’interno della pagina corrente e la proprietà Text che assume per default lo stesso valore del TAG ID. Il pulsante è rappresentato dal TAG Button, ha una proprietà ID. Per l’etichetta, assegnarle l’ID lblVisualizza, cancellare il valore della proprietà Text lasciando una stringa vuota. Per il pulsante, assegnargli l’ID btnPremi, cancellare il valore della proprietà Text e inserire la stringa "Premi". Fare doppio clic sul pulsante, nella pagina di markup, all’interno del TAG Button è aggiunto il comando seguente. <asp:Button ID="btnPremi" runat="server" Text="Premi" OnClick="btnPremi_Click" /> File WEBFORM1.ASPX.DESIGNER.CS Il controllo è inserito automaticamente. protected global::System.Web.UI.WebControls.Button btnPremi; File WEBFORM1.ASPX.CS Contiene il code-behind della pagina, questa caratteristica è molto importante: tutto il codice degli script della pagina che in genere è immerso all'interno dell'HTML, ora può risiedere in un file separato, consentendo una separazione tra grafica e codice. Nella finestra del code-behind dove c’è il codice relativo all’evento associato al pulsante. protected void btnPremi_Click(object sender, EventArgs e) { lblVisualizza.Text = "Ciao, mondo!"; } Server side um 100 di 243 Quando l’utente clicca sul pulsante è valorizzata l’etichetta. Compilare il progetto, fare clic su Debug/Avvia senza eseguire debug (CTRL+F5). È avviato il server web integrato in Visual Studio e l’applicazione ASP.NET sarà visualizzata all'interno del browser web predefinito. File WEB.CONFIG Il comportamento di un’applicazione ASP.NET può essere personalizzato mediante l’uso di questo file di configurazione, in formato XML. È possibile avere un file WEB.CONFIG diverso in ciascuna cartella del sito: ognuno di essi applica le impostazioni di configurazione sulla propria cartella e su tutte le sotto cartelle. I file di configurazione nelle sotto cartelle possono stabilire configurazioni aggiuntive a quelle ereditate dalla cartella padre oppure possono sostituire o modificare quelle definite nei livelli superiori. Un file WEB.CONFIG può contenere vari tipi di informazioni. Variabili comuni a tutte le pagine. Configurazioni per le sessioni. Opzioni per la gestione degli errori. Informazioni per la sicurezza. In fase di progettazione, la proprietà debug dev’essere true. In fase di pubblicazione, la proprietà debug dev’essere false. <?xml version="1.0" encoding="utf-8"?> <configuration> <system.web> <compilation debug="true" targetFramework="4.5.1" /> <httpRuntime targetFramework="4.5.1" /> </system.web> </configuration> Sostanzialmente se la proprietà debug è impostata a true. La compilazione è più lenta in quanto le ottimizzazioni sono disabilitate. Tutti gli script e le immagini non sono inserite nella cache del client. Si verifica un maggior consumo di memoria. Il codice gira più lentamente in quanto sono utilizzati i file di debug. Su un server di produzione, sul quale non si effettuerà mai debug con Visual Studio, potrebbe risultare scomodo verificare manualmente se i WEB.CONFIG di ciascuna applicazione web ospitata hanno la proprietà debug impostata a false. Per fare in modo, invece che la modalità debug sia disabilitata indipendentemente dalla specifica impostazione presente eventualmente in ciascun WEB.CONFIG, si può agire globalmente utilizzando la proprietà retail dell’elemento deployment presente nel file di configurazione globale MACHINE.CONFIG. <configuration> <system.web> <deployment retail="true"/> </system.web> Server side um 101 di 243 </configuration> L’impostazione a true di questa proprietà, oltre a disabilitare la modalità debug per tutte le applicazioni del server indipendentemente dalle specifiche impostazioni di ciascuna, disabilita anche tutti gli eventuali output di tracing presenti nelle pagine di tutte le applicazioni e, inoltre, forza la visualizzazione degli errori custom al posto di quelli generici. Componente dinamica Per aggiungere una componente dinamica, fare doppio clic su un punto vuoto della pagina all’interno dell’editor, il cursore si posiziona automaticamente sul gestore dell’evento Load che è inserito nel code-behind della pagina. protected void Page_Load(object sender, EventArgs e) {} La firma dell’evento è esattamente la stessa dell’evento Load di una Windows Form. Anche il significato è lo stesso: il codice inserito in questo metodo sarà eseguito ogni volta che la pagina sarà caricata. Si desidera che ogni volta che si apre o si aggiorna la pagina, l’etichetta definita in precedenza contenga la data e l’ora dell’operazione, per fare questo si deve scrivere il seguente codice. protected void Page_Load(object sender, EventArgs e) { lblVisualizza.Text = "Ora di connessione: " + DateTime.Now; Page.Title = "Progetto web"; } Esecuzione dell’applicazione nel server web IIS Copiare il file WEBFORM1.ASPX che si trova nella cartella seguente. F:\ESERCIZI\C#\WEB\WEBFORM\WEBFORM\ WEBFORM1.ASPX Nella cartella del server web. C:\INETPUB\WWWROOT. Copiare la cartella BIN che si trova nella cartella seguente. F:\ESERCIZI\C#\WEB\WEBFORM\WEBFORM\BIN Nella cartella BIN del server web. C:\INETPUB\WWWROOT\BIN. Server side um 102 di 243 File GLOBAL.ASAX Oltre a WEB.CONFIG, ad un sito ASP.NET può essere aggiunto questo file che risiede nella cartella principale del sito ed è un contenitore di codice comune all’applicazione. È impiegato per eseguire operazioni all’avvio, al termine delle sessioni e dell’applicazione, inoltre, esegue del codice al verificarsi di una condizione di errore. Per inserire il file GLOBAL.ASAX, fare clic con il tasto destro del mouse sul nome del progetto in Esplora soluzioni e selezionare la voce Aggiungi/Nuovo elemento…. Si apre una finestra di dialogo in cui sono mostrati tutti i tipi di file che possono essere aggiunti, selezionare Classe di applicazione globale. Ha il codice seguente già inserito e include già le dichiarazioni per gli event handler che possono essere definiti. using System; namespace WebForm { public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { } protected void Session_Start(object sender, EventArgs e) { } protected void Application_BeginRequest(object sender, EventArgs e) { } protected void Application_AuthenticateRequest(object sender, EventArgs e) { } protected void Application_Error(object sender, EventArgs e) { } protected void Session_End(object sender, EventArgs e) { } protected void Application_End(object sender, EventArgs e) { } } } Navigazione Si usa l’oggetto seguente con la proprietà NavigateUrl. <asp:HyperLink ID="hplPagina" runat="server" NavigateUrl="~/Pagina2.aspx">Vai alla pagina numero 2</asp:HyperLink> Server side um 103 di 243 WEB FORM PAGINA Una Web Form differisce da una Windows Form perché utilizza controlli differenti, chiamati Web Controls e perché, a differenza di quest’ultima, produce HTML e codice eseguito server side. I Web Controls sono raggruppati in categorie nella Casella degli strumenti. BulletedList Uno degli elementi più comuni delle pagine web è l’elenco puntato. È un controllo server che permette di ottenere elenchi puntati o ordinati, sfruttando in fase di rendering i TAG <ul> e <ol>. Può essere costruito sia specificando un insieme di elementi ListItem sia sfruttando il databinding. Tramite la proprietà BulletStyle è possibile settare lo stile dell’elemento grafico posto davanti al testo, cerchietti, dischetti, quadratini, immagini custom, lettere e numeri. <asp:BulletedList Runat="server" ID="Sample" BulletStyle="Square"> <asp:ListItem>Primo elemento</asp:ListItem> <asp:ListItem>Secondo elemento</asp:ListItem> <asp:ListItem>Terzo elemento</asp:ListItem> </asp:BulletedList> FileUpload Rappresenta una valida alternativa al controllo HTML pre esistente. Internamente il controllo utilizza la collection Request.Files di tipo HttpFileCollection e da essa recupera il file caricato sul server di tipo HttpPostedFile. HiddenField È renderizzato come un TAG <input type="hidden"> e la sua utilità è quella di memorizzare informazioni di stato, in alternativa, per esempio, al ViewState all’atto di eseguire un post. HiddenField fornisce l’evento ValueChanged che è scatenato quando il valore associato al controllo è ritornato al server modificato. Server side um 104 di 243 ImageMap Permette di trasformare un’immagine in un menu di navigazione cliccabile, definendo tramite coordinate geometriche alcune aree sensibili, HotSpot, alle quali può essere associato un diverso comportamento all’atto del clic. Principalmente i comportamenti possono essere due. 1. L’area funge da link ad un URL modalità HotSpotMode.Navigate. 2. Il clic sull’area produce un PostBack modalità HotSpotMode.PostBack. Le aree sensibili di un controllo ImageMap possono essere rettangolari, circolari o poligonali. In fase di rendering il controllo è trasformato in una mappa client TAG <map>, per la quale sono definite le aree cliccabili tramite altrettanti TAG <area>. <asp:ImageMap Runat="server" ID="Sample" ImageUrl="Sample.jpg" Width="300" OnClick="Sample_Click" HotSpotMode="PostBack"> <asp:RectangleHotSpot Top="0" Bottom="225" Left="0" Right="150" AlternateText="Area 1" PostBackValue="Area1"></asp:RectangleHotSpot> <asp:RectangleHotSpot Top="0" Bottom="225" Left="151" Right="300" AlternateText="Area 2" PostBackValue="Area2"></asp:RectangleHotSpot> </asp:ImageMap> MultiView e View MultiView definisce un insieme di viste rappresentate da una collezione di tipo ViewCollection, di queste viste solamente una è riconosciuta come attiva per il controllo ed è renderizzata sul client. Il controllo ingloba tutti i meccanismi per poter commutare da una vista ad un’altra, grazie alla proprietà ActiveViewIndex e all’evento SetActiveView. <asp:MultiView Runat="server" ID="Sample"> <asp:View Runat="Server" ID="View1">...</asp:View> <asp:View Runat="Server" ID="View2">...</asp:View> <asp:View Runat="Server" ID="View3">...</asp:View> </asp:MultiView> Wizard Permette di costruire una sequenza di schermate da mostrare in successione all’utente finale. In realtà è un controllo composito che al suo interno utilizza un controllo MultiView per mostrare i vari passi della sequenza che quindi non sono altro che controlli View in quanto WizardStepBase deriva dal tipo View. Il controllo mette a disposizione i pulsanti di avanzamento, Avanti, Indietro, Fine, Annulla e sfrutta eventi server side per gestire la navigazione ogni volta che l’utente decide di cambiare schermata. È possibile navigare sia in modo lineare sia in modo casuale, saltando da un passo ad un altro. <asp:Wizard Runat="server" ID="Sample" SideBarEnabled="True" ActiveStepIndex="0"> <WizardSteps> <asp:WizardStep Runat="server" Title="Primo passo">Primo passo</asp:WizardStep> <asp:WizardStep Runat="server" Title="Secondo passo">Secondo passo</asp:WizardStep> <asp:WizardStep Runat="server" Title="Terzo passo">Terzo passo</asp:WizardStep> </WizardSteps> Server side um 105 di 243 </asp:Wizard> La pagina ASP.NET ha estensione ASPX e quando si richiede parte il worker process ASPNET_WP.EXE. È suddivisa in due parti. 1. Markup. 2. Codice: con estensione ASPX.CS. La prima richiesta ad una pagina è leggermente più lenta delle successive, perché c’è un overhead aggiunto dalla fase di compile-time. Durante questo momento, infatti, è richiamato il compilatore specifico per il linguaggio utilizzato e tutto il codice presente nella pagina, sia sotto forma di codice sia di HTML, è convertito in una classe ed è creato un assembly DLL (Dynamic Link Library) corrispondente alla pagina che è chiamato con il nome della stessa. Fino ad un’eventuale modifica o riavvio del processo, ASP.NET continuerà ad utilizzare la versione già compilata delle pagina, con un accesso alla stessa istantaneo. Il meccanismo di compilazione incrementa le performance dell’applicazione web, perché le richieste sono tenute, già compilate in assembly, all’interno di un percorso ben definito sul disco eseguite in MSIL (MicroSoft Intermediate Language) dal JIT. Nella compilazione nell’assembly corrispondente sono però racchiusi più passaggi. 1. Sono individuate tutte le dipendenze con eventuali classi esterne. 2. È invocato il compilatore specifico per il linguaggio utilizzato nella pagina. 3. È generato un assembly e salvato su disco, in una cartella temporanea, per essere riutilizzato. Le pagine non sono altro che classi ed ereditano le funzionalità di base dalla classe Page come una Windows Form eredita dalla classe Form. Page si trova nel namespace System.Web.UI.Page e contiene al proprio interno la logica necessaria a far funzionare una pagina HTML, per esempio l’invio del codice al client e il recupero delle informazioni. La modalità code-behind che separa codice da contenuto, sfrutta proprio questa caratteristica per creare la pagina, in un file separato, una classe a tutti gli effetti da cui poi la pagina vera e propria eredita. Metodi di base Sono agganciati attraverso HttpContext, una classe istanziata da HttpRuntime che espone verso la pagina l’accesso a querystring e form, i principali metodi sono i seguenti. Response Fornisce un ponte verso la classe HttpResponse che implementa le funzionalità di output verso il client. Request Si aggancia alla classe HttpRequest, utilizzata per recuperare informazioni dal client. Server Sfrutta HttpServerUtility per fornire funzionalità di uso comune alla pagina. Session Fornisce un ponte verso la classe HttpSessionState che contiene le funzionalità necessarie alla gestione dello stato della sessione. Application Si aggancia a HttpApplicationState, per fornire la logica di accesso allo stato Server side um 106 di 243 dell’applicazione. EVENTI Come tutti gli oggetti, anche la classe Page può avere degli eventi che si verificano in seguito ad un comportamento del client e che possono essere di natura automatica o associati a particolari condizioni. Come è possibile avere eventi in un’applicazione web che è senza stato? Dopo tutto una pagina web comincia la sua vita non appena il client la richiede, per morire quando il suo risultato è inviato al client. ASP.NET sfrutta questo concetto: tutti gli eventi si verificano sul server ma sono invocati dal client, in pratica non fa altro che rendere più semplice, per chi scrive codice, pensare ad eventi, sfruttando però del normale codice JavaScript per fare il submit di un form, passando i dati necessari a far eseguire il codice server side. Da parte del programamtore non è necessario scrivere codice JavaScript, perché la pagina è in grado di fornire la struttura di base sulla quale operare e che si occuperà di fornire il substrato necessario all’invocazione degli eventi. Sono scatenati all’interno di una pagina, nell’ordine in cui sono intercettati. Page_PreInit Si verifica prima dell’inizializzazione della pagina, serve per impostare Master Page e theme. Page_Init Si verifica all’inizializzazione della classe rappresentata dalla pagina ed è, di fatto, il primo vero evento. Page_InitComplete Si verifica subito dopo l’evento INIT. Page_LoadState Segna il caricamento dello stato della pagina e dei controlli dal ViewState. Page_PreLoad Si verifica prima del caricamento della pagina. Page_Load Si verifica al caricamento della pagina, successivamente all’inizializzazione. Page_LoadComplete Si verifica subito dopo l’evento LOAD. Page_PreRender Si verifica subito prima del rendering della pagina. Page_Render Si verifica al rendering della pagina e segna la generazione del codice HTML associato. Page_SaveState Salva lo stato dei controlli all’interno del ViewState. Page_UnLoad Si verifica allo scaricamento dell’istanza della pagina, non corrisponde al metodo Dispose perché questo è gestito dal GC (Garbage Collector). Server side um 107 di 243 Page_Error Si verifica in caso di errore non gestito all’interno della pagina e può essere sfruttato per visualizzare un avviso all’utente o registrare la causa del fallimento dell'esecuzione. POSTBACK Nella programmazione web si ragiona con questa logica Form->Post->Action. L’idea è che un form posizionato in una pagina ASP passa dei dati ad una seconda pagina tramite un POST generato dalla pressione di un pulsante. La seconda pagina preleva poi i dati e li utilizza per i propri scopi. ASP.NET fornisce all’utente un frameWork che consente di astrarsi da queste conoscenze e utilizzare direttamente controlli e programmazione ad eventi. Progettare un’applicazione web in modo che l’utente possa digitare il proprio nome e, premendo un pulsante, riceva un messaggio di saluto. Impostare le proprietà degli oggetti. lblNome txtNome btnConferma lblSaluto Label TextBox Button Label Text = Nome: Text = Conferma Text = Ciao, mondo File WEBFORM1.ASPX <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="WebApplication1.WebForm1" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>PostBack & ViewState</title> </head> <body> <form id="form1" runat="server"> <div> <asp:Label ID="lblNome" runat="server" Text="Nome:"></asp:Label> <asp:TextBox ID="txtNome" runat="server"></asp:TextBox> <asp:Button ID="btnConferma" runat="server" OnClick="btnConferma_Click" Text="Conferma" /> <br /> <br /> <asp:Label ID="lblSaluto" runat="server" Text="Ciao, mondo!"></asp:Label> </div> </form> Server side um 108 di 243 </body> </html> Quando la pagina è caricata all’interno del browser, è generato l’evento Page_Load. Nel codice del delegate per Page_Load è possibile verificare se l’utente sta effettuando il primo accesso alla pagina oppure sta effettuando un PostBack. La tecnica del PostBack è quella che consente ad una pagina web di trasmettere a se stessa delle informazioni di stato. In pratica, in una pagina web dinamica è creato un form che ha come campo action l’indirizzo della pagina stessa e al suo interno dei campi nascosti che mantengono informazioni sullo stato della pagina. Per esempio si può avere un campo che contiene il testo di una casella di testo. Sono usati campi nascosti per ricordare se includere una porzione di codice o meno. Queste informazioni una volta generato un evento submit che non necessariamente scaturisce da un pulsante, sono rispedite indietro alla pagina che le usa per modificare il suo aspetto. La proprietà IsPostBack restituisce il valore true quando il modulo presente nella pagina è stato eseguito, false quando si tratta del primo accesso alla pagina. File WEBFORM1.ASPX.CS using System; using System.Web.UI; namespace WebApplication1 { public partial class WebForm1 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (Page.IsPostBack) { // la pagina è stata già processata // è stato sollevato qualche evento che ha provocato un PostBack } else { // primo caricamento della pagina } } Sono continui scambi d’informazioni tra il browser e il server, per esempio un doppio clic su un pulsante genera due chiamate al server. In caso di transazioni complesse una continua pressione del pulsante potrebbe mandare in timeout il server. Allora bisogna disabilitare il pulsante dopo che l’utente lo ha cliccato tramite la proprietà Attributes dell’oggetto Button e il relativo metodo Add che accetta due parametri. 1. Il nome dell’attributo che sarà aggiunto al TAG HTML. 2. Il nome della funzione o il codice client script che sarà eseguito dal pulsante. btnConferma.Attributes.Add("onclick", "this.disabled = true;"); Dopo aver chiamato la pagina, quest’istruzione è convertita nel seguente codice HTML. <input type="submit" name="btnConferma" value="Ricerca"id=" btnConferma" onclick="this.disabled = true;" /> Premendo il pulsante sulla pagina web il suo stato passa in disabilitato e non è più eseguito il PostBack verso il server. Quindi non è più eseguito lo script lato server contenuto nell’evento clic del pulsante. Per ovviare a questo malfunzionamento occorre aggiungere una chiamata al metodo Server side um 109 di 243 GetPostBackEventReference all’interno della classe Page. Questo metodo restituisce il codice JavaScript necessario a fare il submit della pagina, ovvero a contattare il server per fornirgli i dati contenuti nella pagina. Si possono utilizzare i metodi e le proprietà di questa classe in quanto ogni pagina ASP.NET crea una classe derivandola da Page. String strScript = "this.disabled = true;"; strScript += Page.GetPostBackEventReference(btnConferma); btnConferma.Attributes.Add("onclick", strScript); Nel metodo bisogna utilizzare un parametro per specificare il controllo al quale associare la chiamata PostBack verso il server. Ecco come sarà convertito il nuovo codice in HTML completo delle funzioni di PostBack. <input type="submit" name=" btnConferma " value="Ricerca" id=" btnConferma " onclick="this.disabled = true; __doPostBack('btnRicerca', '');" /> Quando si preme sul pulsante di conferma, in automatico, è fatto un invio del form. Quest’azione si chiama PostBack, perché il form è nuovamente inviato a se stesso. In altre parole, il PostBack si verifica esattamente ogni volta che il client invia il controllo al server al seguito di un’azione compiuta dall’utente sulla pagina. È questo il motivo per cui con ASP.NET si fa pochissimo uso dell’oggetto Request, dato che si può direttamente accedere alla proprietà che contiene il testo del controllo. Per scatenare il PostBack, ASP.NET utilizza due campi nascosti all’interno del form che automaticamente aggiunge alle pagine che indicano rispettivamente il controllo che ha scatenato il PostBack e i parametri da passare. Queste informazioni serviranno ad ASP.NET per ricostruire l’evento scatenato dall’utente associato al controllo. __EVENTTARGET __EVENTARGUMENT Con quest’operazione, ASP.NET riceve nuovamente la pagina e capisce che c’è da intercettare l’evento clic, richiamando l’event handler specificato nel markup, definito con l’attributo OnClick. protected void btnConferma_Click(object sender, EventArgs e) { lblSaluto.Text = "Il tuo nome è " + txtNome.Text; } } } Eseguito nel browser, questo codice genera la schermata seguente. Server side um 110 di 243 Visualizzare il sorgente della pagina all’interno del browser web, ad esempio, in IExplorer, con il comando Visualizza/Origine. Si vede che tutto il codice ASP.NET è stato tradotto in HTML standard. Non c’è però traccia del codice dello script, esso, in modo del tutto invisibile, è stato compilato in una libreria che risiede sul server e che è richiamata ogni volta che si effettua il post della pagina. <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>PostBack & ViewState</title> </head> <body> <form method="post" action="WebForm1.aspx" id="form1"> <div class="aspNetHidden"> <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="vjQaB82JZxbWmzxEDPGVWhTKqamnYE4KgzSux/jxrWm5CxRNT8a3aKuURHKc mAeF4Z7veDIU6Fzo14RRn5QbMgcItUjf5yLdy1oWdiUlPruIyUYTgasMaYmoya++VVfFWs QVMFvHW1Ayxsfj4J9Kw+vwYPikFa1YmVn1r0OYUg8=" /> </div> <div> <span id="lblNome">Nome:</span> <input name="txtNome" type="text" value="Andrea" id="txtNome" /> <input type="submit" name="btnConferma" value="Conferma" id="btnConferma" /> <br /> <br /> <span id="lblSaluto">Il tuo nome è Andrea</span> </div> <div class="aspNetHidden"> <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="9Y1S/98Im9RB7OHBBrAG7hrH9Oq3n3AX3/DExCUT8yG9IBkqnIqfTcyIQ1YIJ7Rt3 jm8UJh7VZeUn/t5NxWfV0U0rAqIpNXY3zWoLp0+Y+3NPyahiKw1VVf41d1v0fKEuJX3YIO oBKHKei7CB46kvw==" /> </div></form> <!-- Visual Studio Browser Link --> <script type="application/json" id="__browserLink_initializationData"> {"appName":"Internet Explorer","requestId":"bf0cdea20c904977b4e6f09aa80ce557"} </script> <script type="text/javascript" src="http://localhost:49216/759a4a15f8834677b4d38141d5a000d6/browserLink" async="async"></script> <!-- End Browser Link --> </body> </html> La TextBox ha mantenuto lo stato senza che il programmatore dovesse effettivamente gestirlo manualmente, questa caratteristica fa parte dell’infrastruttura ASP.NET e si chiama ViewState. ACCESSO AI META TAG ASP.NET mette a disposizione due proprietà della classe Page che consentono di avere Server side um 111 di 243 un controllo maggiore sui meta TAG inseriti nella pagina quando questa è convertita in HTML. Le due proprietà sono MetaKeywords e MetaDescription e dal nome, consentono rispettivamente di valorizzare il meta TAG Keywords e il meta TAG Description della pagina. Queste due proprietà sono di tipo String e possono essere impostate sia dichiarativamente. <%@ Page MetaKeywords="key1,key2, key3" %> <%@ Page MetaDescription="Descrizione della pagina" %> Sia da code-behind. Page.MetaKeywords ="key1, key2, key3"; Page.MetaDescription ="Descrizione della pagina"; CONTROLLI Se la Web Form è il contenitore, gli oggetti contenuti all’interno di quest’ultima sono chiamati controlli, producono, come output, codice HTML, anche se la loro dichiarazione all’interno della pagina ha una sintassi differente da quello che potrebbe essere un TAG. Ci sono due grandi famiglie di controlli, divisi in base alle funzionalità che implementano. Web controls Hanno il prefisso <asp> e la sintassi che li contraddistingue è di tipo XML. Questo prefisso è, in genere, riservato ai controlli disponibili all’interno di ASP.NET, mentre il suffisso è il nome vero e proprio della classe che sarà istanziata. <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label> HTML controls Appartengono tutti i TAG HTML cui è aggiunto l’attributo runat = "server", ASP.NET li usa per mappare gli oggetti della pagina sulle istanze di classi che poi andrà a creare, in fase di compile-time. Questa caratteristica, applicata ai frammenti di codice HTML, li rende controlli server che producono un markup e sono in grado di fornire un comportamento. <form id="form1" runat="server"> I web control hanno un modello a oggetti che rende controlli molto diversi nel markup ma, in realtà, molto simili nel modello a oggetti. Gli HTML control hanno un modello a oggetti che ricalca quello dei TAG HTML cui si riferiscono. La differenza tra queste due famiglie è che i primi ricordano, nel nome, i controlli per Windows e s’inseriscono all’interno delle pagine con una sintassi diversa dal corrispondente codice HTML generato, mentre i secondi permettono di utilizzare un TAG HTML sfruttandone la possibilità di essere trattato come oggetto e quindi di accedere in maniera programmatica alle sue funzionalità. I web control offrono un insieme coerente di proprietà, in modo che la stessa funzionalità implementata su controlli differenti mantenga lo stesso nome all’interno di tutto il framework. Tanto i web control, quanto gli HTML control, producono alla fine codice HTML grazie al componente page parser. Il page parser garantisce che i programmatori possono scrivere il markup nel modo Server side um 112 di 243 classico ma che, contestualmente, a run-time i frammenti sono automaticamente tradotti in codice. Il vantaggio è di poter scrivere l’interfaccia in modo semplice e veloce sfruttando le stesse funzionalità delle applicazioni Windows, con il risultato di poter programmare i controlli posizionati nella pagina, impostandone le proprietà e, soprattutto, potendone gestire gli eventi associati agli oggetti della pagina. La Casella degli strumenti contiene gli oggetti attraverso i quali il programmatore costruisce le Web Form e l’utente interagisce con la pagina. Dati: sono controlli con funzionalità avanzate. Convalida: sono controlli per la validazione dell’input, hanno una forte componente client. HTML: sono elementi HTML esposti lato server. Controllo Wizard: consente di strutturare una serie di passaggi da far compiere all’utente. Controlli container: funzionano da contenitore, in pratica possono raggruppare altri controlli e, se implementano l’interfaccia INamingContainer, assicurano agli stessi che il loro ID sarà generato univocamente. Un controllo contenuto in un container non è direttamente accessibile utilizzando l’ID, come si fa con un controllo contenuto in una pagina ma va ricercato all’interno dell’albero dei controlli che contiene tutta la struttura della pagina, usando il metodo FindControl sul contenitore. Il controllo PlaceHolder permette di raggruppare e nascondere o visualizzare un gruppo di controlli, senza agire singolarmente sugli stessi. Esempio, subito dopo il clic dell’utente nascondere, contestualmente il form d’immissione dati. File FORM2.ASPX <%@ Page Language="C#" CodeFile="Form2.aspx.cs" Inherits="Form2" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Esempio di contenitore</title> </head> <body> <form runat="server"> <div> <asp:PlaceHolder ID="EntryForm" runat="server">Inserisci il tuo nome: Server side um 113 di 243 <asp:TextBox ID="txtNome" runat="server" /> <br /> <asp:Button ID="btnConferma" runat="server" Text="Conferma" OnClick="VisualizzaNome" /> </asp:PlaceHolder> <asp:PlaceHolder ID="Results" runat="server" Visible="false"> Il tuo nome è <asp:Literal ID="lblNome" runat="server" /> </asp:PlaceHolder> </div> </form> </body> </html> File FORM2.ASPX.CS using System; using System.Web.UI; public partial class Form2 : Page { protected void VisualizzaNome(object sender, EventArgs e) { EntryForm.Visible = false; Results.Visible = true; lblNome.Text = txtNome.Text; } } Server side um 114 di 243 PASSAGGIO D’INFORMAZIONI CICLO DI VITA Un utente richiede una pagina ASPX tramite il browser, in tre modi. 1. Digita l’URL nel browser. 2. Fa clic su un link: è una richiesta HTTP di tipo GET. 3. Fa clic su un pulsante: è una richiesta HTTP di tipo POST. La pagina esegue un’elaborazione, compilazione ed esecuzione sul server web che termina con la restituzione di una risposta HTTP al browser. All’interno della risposta HTTP si trova la pagina in formato HTML con i dati frutto dell’elaborazione. Ogni volta che è fatta una nuova richiesta HTTP su una pagina sono perse tutte le informazioni in essa contenute. Per ovviare a questo inconveniente, ASP.NET mette a disposizione diversi meccanismi per preservare i dati tra un PostBack e l’altro, divisi in due gruppi. 1. MECCANISMI DI CONSERVAZIONE DEI DATI LATO CLIENT I meccanismi 1, 2 e 4 permettono di riutilizzare i dati solo se si rimane nella stessa pagina ma se si va su altre pagine si perde il loro contenuto. 1.1 ViewState È una tabella associativa che contiene lo stato degli oggetti presenti nella pagina. In pratica, consente alle pagine di mantenere lo stato dei controlli attraverso i vari PostBack e concorre al supporto degli eventi all’interno del sistema. In questo modo tra un PostBack e un altro i dati sono salvati e recuperati da questo contenitore che è implementato come campo nascosto dalla Web Form. __VIEWSTATE Se ad esempio s’implementa un sistema di validazione dell’input dell’utente, non ci si deve preoccupare di mantenere il valore dei campi presenti sulla pagina, perché il ViewState se ne occupa direttamente per noi. HTTP è un protocollo stateless, senza stato, ovvero non mantiene le informazioni sullo stato di una pagina tra una visita e l’altra. Per sopperire a tale mancanza, ASP.NET utilizza il campo ViewState. Ogni volta che si ricarica una pagina, i controlli in essa presenti sono inizializzati sulla base del contenuto del ViewState che è generato automaticamente da ASP.NET. Questa soluzione consente di trasferire lo stato dei controlli dal server al client e viceversa. Ciò significa che quando il server leggerà il ViewState di una pagina sarà in grado di ripristinare, ad esempio, il valore corrente di tutti i campi input, senza bisogno che sia il programmatore a farlo via codice. Trattandosi di una tabella associativa, inoltre, nel ViewState è possibile inserire anche dei valori personalizzati. Esempio, memorizzare il numero di volte in cui il pulsante btnConferma è stato premuto. protected void btnConferma_Click(object sender, EventArgs e) { int click; if (ViewState["click"] == null) click = 1; else Server side um 115 di 243 click = (int)ViewState["click"] + 1; lblSaluto.Text="Il tuo nome è " + txtNome.Text + ", hai premuto " + click + " volte"; ViewState["click"] = click; } Si legge il valore associato alla chiave click nel ViewState: se non è stato ancora inizializzato, ovvero vale null, significa che questa è la prima pressione del pulsante, altrimenti s’incrementa il valore in esso contenuto. Dopo aver stampato il numero di clic, si aggiorna il contenuto del ViewState, ad ogni pressione del pulsante il numero stampato aumenterà. Il ViewState è mantenuto solo se sono eseguite richieste consecutive della stessa pagina, nel caso in cui sia caricata una nuova pagina, il ViewState sarà perso. Per mantenere le informazioni durante la navigazione, si usa l’oggetto Session. È possibile abilitare/disabilitare il ViewState di un controllo impostando la sua proprietà EnableViewState oppure intervenire a livello di pagina con la direttiva seguente. <%@ Page EnableViewState = "False" %> È possibile salvare/prelevare variabili di ogni tipo del ViewState a livello di code-behind, per questo motivo è necessario fare il cast con il tipo corrispondente quando si prelevano. // salvo i dati nel ViewState: contenuto della proprietà Text di una Label this.ViewState["ora_iniziale"] = lblOra.Text; // prelevo i dati dal ViewState string ora = (string) this.ViewState["ora_iniziale"]; 1.2 Hidden fields Sono come caselle di testo ma non sono visibili sulla pagina, ASP.NET li usa per memorizzare il ViewState, il programmatore li usa tramite code-behind. // definizione nella pagina di markup <asp:HiddenField ID =" HiddenField1" runat "server" Value "pluto" /> // utilizzo lato code-behind HiddenField1.Valure = "pippo"; 1.3 Querystring È un’informazione appesa alla fine di un URL, comincia con un (?) ed è costituita da coppie attributo/valore separate da (&), utile per passare informazioni tra pagine; è buona regola crittografare il contenuto perché viaggia in chiaro. È il caso di più pagine web che si richiamano tra di loro e che si passano informazioni. Il richiamo di una pagina avviene utilizzando il metodo Redirect della classe Response. Server side um 116 di 243 Response.Redirect ("pagina.aspx"); In ASP.NET ci sono diverse possibilità. 1. Impostazione di una stringa di query. 2. Cookie. 3. Sessione. 4. Applicazione. L’impostazione di una stringa di query, è il metodo più semplice e prevede l’inserimento delle informazioni all’interno dell’URL della pagina cui si è reindirizzati. Esempio, costruire una pagina in cui dopo aver richiesto il nome e il cognome di un utente richiama un’altra pagina che visualizza un messaggio. File RICHIESTA.ASPX <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="richiesta.aspx.cs" Inherits="query.richiesta" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>Richiesta</title> </head> <body> <form id="form1" runat="server"> <div> <asp:Label ID="lblnome" runat="server" Text="Inserisci il nome:"></asp:Label> <asp:TextBox ID="txtNome" runat="server"></asp:TextBox> <br /> <br /> <asp:Label ID="lblCognome" runat="server" Text="Inserisci il cognome:"></asp:Label> <asp:TextBox ID="txtCognome" runat="server"></asp:TextBox> <br /> <br /> <asp:Button ID="btnConferma" runat="server" Text="Invia informazioni" OnClick="btnConferma_Click" /> <br /> Server side um 117 di 243 </div> </form> </body> </html> File RICHIESTA.ASPX.CS using System; using System.Web; namespace query { public partial class richiesta : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void btnConferma_Click(object sender, EventArgs e) { string query; if (txtNome.Text != "" || txtCognome.Text != "") { query = txtNome.Text + " " + txtCognome.Text; Response.Redirect("visualizza.aspx?nome=" + query); } } } } File VISUALIZZA.ASPX <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="visualizza.aspx.cs" Inherits="query.visualizza" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>Visualizza</title> </head> <body> <form id="form1" runat="server"> <div> <asp:Label ID="lblMessaggio" runat="server"></asp:Label> </div> </form> </body> </html> Server side um 118 di 243 File VISUALIZZA.ASPX.CS using System; namespace query { public partial class visualizza : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { lblMessaggio.Text = "Benvenuto in questa pagina: "+ Request.QueryString["nome"]; } } } 1.4 Cookies Sono file di testo che risiedono sul PC dell’utente, nei quali si possono memorizzare i dati, permettono di riutilizzare i dati su più pagine. È salvato automaticamente e in modo trasparente per l’utente nella memoria del browser se le informazioni in esso contenute sono temporanee. È salvato automaticamente e in modo trasparente per l’utente sul disco se le informazioni in esso contenute sono di più lunga durata che è stabilita con il metodo Expires. Esempio, costruire una pagina in cui dopo aver richiesto il nome e il cognome di un utente richiama un’altra pagina che visualizza un messaggio. File RICHIESTA.ASPX.CS using System; using System.Web; namespace query { public partial class richiesta : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void btnConferma_Click(object sender, EventArgs e) { HttpCookie prova; if (txtNome.Text != "" || txtCognome.Text != "") { prova = new HttpCookie("ASP.NET"); // inserire una variabile con un dato valore Response.Cookies["nome"].Value = txtNome.Text + " " + txtCognome.Text; /* aggiungere il cookie creato alla collezione dei cookies della pagina web con il * metodo Add */ Response.Cookies.Add(prova); Response.Redirect("visualizza.aspx"); } } } } Server side um 119 di 243 File VISUALIZZA.ASPX.CS using System; namespace query { public partial class visualizza : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { lblMessaggio.Text = "Benvenuto in questa pagina: " + Request.Cookies["nome"].Value; } } } 2. MECCANISMI DI CONSERVAZIONE DEI DATI LATO SERVER 2.1 Session È una tabella associativa e funziona esattamente come il ViewState, tuttavia le variabili archiviate al suo interno non sono cancellate quando l’utente passa da una pagina ad un’altra dell’applicazione ma sono mantenute per tutta la sessione che inizia quando un browser punta al sito e finisce quando è chiusa. È l’intera fase di lavoro dell’utente sull’applicazione web, comincia quando l’utente effettua la prima richiesta ad una pagina dell’applicazione web e termina quando l’utente esce dall’applicazione web o quando rimane inattivo per diverso tempo. // in una pagina salviamo il dato della sessione Session["nome_variabile"] = "pippo"; // in un'altra pagina o nella pagina stessa lo preleviamo string nome = (string)Session["nome_variabile"]; // è buona regola rilasciare le variabili dalla sessione non appena sono obsolete Session.Remove("nome_variabile"); Session.RemoveAll(); Prima differenza con il ViewState: le variabili che sono messe in sessione sono estraibili da tutte le pagine dell’applicazione e non solo dalla pagina dalla quale sono state portate in sessione. Seconda differenza con il ViewState: le variabili di sessione risiedono sul server e non sul client. 2.2 Cache Permette la gestione dello stato della pagina lato server, l’utilizzo è simile alla sessione, le variabili sono visibili contemporaneamente a tutti gli utenti connessi all’applicazione web. Server side um 120 di 243 MASTER PAGE INTRODUZIONE Le Windows Form permettono di sfruttare l’ereditarietà per usare l’UI, dato che un form è una classe che può contenere controlli e da essa è possibile derivare. Non si può dire la stessa cosa per le Web Form, per le quali non è possibile parlare di ereditarietà, a causa della presenza del codice di markup. Pertanto nella realizzazione di applicazioni web si sono sempre cercate strade alternative all’ereditarietà, cercando di sfruttare al meglio la possibilità di modularizzare le pagine, al fine di minimizzare il lavoro svolto e favorire il riuso del codice. Con ASP il meccanismo utilizzato per ottenere lo scopo è stato l'inclusione di file sfruttando SSI (Server Side Includes). Questo approccio è stato in seguito soppiantato dagli Web User Control, tramite cui è stato possibile trattare ogni singola porzione di una pagina ASPX come un controllo custom definito dal programmatore. Per superare i limiti della soluzione basata su Web User Controls, è stato introdotto un nuovo meccanismo basato su Master Page e Content Page. Anche se non risolvono il problema dell’ereditarietà, le Master Page forniscono una struttura comune e condivisa tra tutte le pagine che da esse dipendono, sia da un punto di vista grafico sia logico. Nello sviluppo di applicazioni web è essenziale che tutte le pagine mantengano un layout comune. L’uso di questa tecnica è potente e consente di annidare anche Master Page tra loro, questo permette di poter disporre di template diversificati per ciascuna sezione del sito. È possibile assegnare una Master Page anche da codice all’interno dell’evento Pre_Init. MASTER PAGE È un template che, può contenere al suo interno qualsiasi elemento HTML e qualsiasi elemento ASP.NET, in pratica sia contenuti dinamici sia statici, contiene alcune aree rappresentate da altrettanti controlli di tipo ContentPaceHolder che saranno riempiti con i contenuti definiti nelle varie sezioni delle Content Page associate. In altre parole, permette di centralizzare le funzionalità comuni, sia in termini di layout sia di code-behind, a tutte le pagine del sito web mettendo a disposizione del programmatore un unico file in cui fare le modifiche. In questo modo le pagine ereditano l’apparenza e il comportamento implementato nella Master Page. Una Master Page è un file con estensione MASTER che ha una sintassi del tutto analoga ad una normale pagina ASPX, identificata dalla direttiva seguente. <%@Master Sostituisce la direttiva @Page delle pagine web. Una Master Page contiene una serie di controlli ContentPlaceHolder che identificano alcune regioni personalizzabili il cui contenuto è caricato a run-time a partire da una Content Page. Una Master Page priva dei controlli ContentPlaceHolder non ha senso di esistere. Esempio, progettare una Master Page con tre aree che saranno poi specializzate nella Content Page. Fare clic destro in Esplora soluzioni sul nome del progetto, dal menu contestuale Server side um 121 di 243 selezionare Aggiungi/Nuovo elemento… (CTRL+MAIUSC+A). Alla voce Visual C# selezionare Web/Web Form/Pagina Master Web Form. Nel progetto, il template inserisce automaticamente i seguenti file. File SITE.MASTER Contiene solamente un ContentPlaceHolder posizionato a centro pagina, pronto per essere personalizzato. È possibile aggiungerne più di uno, basta trascinare il controllo dalla Casella degli strumenti, oppure aggiungerlo tramite codice. <%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.master.cs" Inherits="MasterPage.Site" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>Esempio di Master Page</title> <asp:ContentPlaceHolder ID="head" runat="server"> </asp:ContentPlaceHolder> </head> <body> <form id="form1" runat="server"> <div class="left"> <asp:ContentPlaceHolder ID="Left" runat="server"> <div>Sinistra</div> </asp:ContentPlaceHolder> </div> <div class="body"> <asp:ContentPlaceHolder ID="Body" runat="server" > <p>Centro</p> </asp:ContentPlaceHolder> </div> <div class="right"> Server side um 122 di 243 <asp:ContentPlaceHolder ID="Right" runat="server"> <div>Destra</div> </asp:ContentPlaceHolder> </div> </form> </body> </html> File SITE.MASTER.CS using System; namespace MasterPage { public partial class Site : System.Web.UI.MasterPage { protected void Page_Load(object sender, EventArgs e) { } } } File SITE.MASTER.DESIGNER.CS Il codice è generato automaticamente dal template. CONTENT PAGE È una Web Form che contiene unicamente controlli di tipo Content e alla quale è associata una Master Page. Inoltre, per ogni controllo di tipo Content, deve esistere un controllo di tipo ContentPaceHolder nella Master Page. Ciascun controllo Content include il contenuto effettivo per ogni ContentPaceHolder presente nella Master Page. La corrispondenza tra i due tipi di controllo si basa sul valore della proprietà ID e ContentPlacerHolderID. Questo è necessario perché in una Master Page si possono avere tanti ContentPlaceHolderID e si deve avere il modo di distinguerli in modo da associare i contenuti correttamente. Adesso bisogna associare la Master Page appena progettata alle pagine web del sito. Fare clic destro in Esplora soluzioni sul nome del progetto, dal menu contestuale selezionare Aggiungi/Nuovo elemento… (CTRL+MAIUSC+A). Alla voce Visual C# selezionare Web/Web Form mediante pagina master. Si apre una finestra per selezionare la Master Page da associare alla pagina web. Non è sempre obbligatorio sovrascrivere il placeholder, per questo motivo, nell’esempio, sono soltanto definite la parte centrale e quella destra. L’uso dell’attributo MasterPageFile sulla direttiva @Page indica il percorso virtuale del file Server side um 123 di 243 MASTER. File CONTENTPAGE.ASPX <%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="ContentPage.aspx.cs" Inherits="MasterPage.ContentPage" %> <asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server"> </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="Left" runat="server"> </asp:Content> <asp:Content ID="Content3" ContentPlaceHolderID="Body" runat="server"> <div>Parte centrale della pagina</div> </asp:Content> <asp:Content ID="Content4" ContentPlaceHolderID="Right" runat="server"> <div>Parte destra personalizzata</div> </asp:Content> File CONTENTPAGE.ASPX.CS using System; namespace MasterPage { public partial class ContentPage : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } } } Il codice HTML e i possibili script non sono inseriti nell’HTML della pagina ma nel codice della MasterPage; questo permette al designer del sito di modificarne l’aspetto senza dover ritoccare ogni singola pagina: basterà modificare la Master Page. È possibile associare una specifica Master Page ad una Content Page tramite l’attributo MasterPageFile nell’ambito della direttiva @ Page, specificando il percorso del file .MASTER. In alternativa, è possibile definire la Master Page per le pagine anche a livello di singola cartella o a livello di applicazione direttamente nel WEB.CONFIG tramite l'attributo masterpagefile del TAG <pages/>. <configuration> <system.web> <pages masterpagefile="MasterPage.master" /> </system.web> </configuration> È inoltre possibile associare una Master Page ad una pagina anche dinamicamente via Server side um 124 di 243 codice. L’associazione dinamica può essere fatta solamente nell’ambito dell’event handler dell’evento di pagina PreInit, dato che il run-time di ASP.NET carica le informazioni relative alle impostazioni della pagina subito dopo questo evento. void Page_PreInit(object sender, EventArgs e) { string master = "MasterPage.master"; if(Page.Request["Master"] != null) master = Page.Request["Master"].ToString(); this.MasterPageFile = master; } È possibile annidare le Master Page, in questo caso ciascuna pagina si comporta nei confronti della propria Master Page come una normale pagina di contenuto, indipendentemente dal fatto che la Content Page sia a sua volta Master Page. Una Master Page collegata ad un’altra Master Page presenta qualsiasi possibile combinazione di oggetti Content e ContentPlaceHolder. Una Master Page può essere associata ad una pagina di contenuto in relazione al tipo di browser utilizzato. Aggiungendo un prefisso all’attributo MasterPageFile nella direttiva @ Page, è possibile indicare per quale particolare tipologia di browser la Master Page dev’essere caricata a run-time. Per esempio, il prefisso ie identifica Internet Explorer. <%@ Page Language="c#" ie:MasterPageFile="IE_MasterPage.master" MasterPageFile="MasterPage.master" %> Oltre che da template, la Master Page può fungere da Page Controller per le pagine di contenuto cui è associata. Data una particolare Master Page è possibile definire nell’ambito della sua interfaccia una serie di membri pubblici custom. Per poter utilizzare questi membri aggiuntivi nell’ambito della pagina di contenuto, si possono seguire due strade. È possibile castare in modo esplicito la proprietà Master di Page al tipo specifico di MasterPage che si sta usando nell’ambito del codice. ((MyMaster)this.Master).MyMethod(); In alternativa è possibile utilizzare la direttiva @ MasterType nell’ambito della Content Page. In questo caso non è più necessario fare il cast al tipo specifico di Master Page utilizzata. <%@ MasterType VirtualPath="MasterPage.master" %> Server side um 125 di 243 Esempio, progettare un sito web composto da pagine che hanno la stessa struttura. File DEFAULT.ASPX.CS L’istruzione IsPostBack restituisce un valore booleano che indica se il PostBack si è verificato o meno, di solito si usa nell’evento Page_Load. Quando restituisce false significa che è la prima volta che la pagina è caricata nel browser. Nell’esempio, si valorizza con l’orario corrente la proprietà Text della lblOra. using System; namespace WebApplication1 { public partial class _default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) lblOra.Text = "Sono le ore: " + System.DateTime.Now.TimeOfDay.ToString(); } protected void btnAggiorna_Click(object sender, EventArgs e) { if (!string.IsNullOrWhiteSpace(txtNominativo.Text)) lblOra.Text = "Ciao " + txtNominativo.Text + " sono le ore: " + System.DateTime.Now.TimeOfDay.ToString(); } } } L’orario visualizzato nella pagina, è aggiornato solo se l’utente digita il nome nella casella di testo. Quindi ASP.NET preserva l’orario già presente sulla pagina, grazie al ViewState perché aggiunge nella pagina un campo nascosto, hidden fields, chiamato _VIEWSTATE. In questo campo sono aggiunte tutte le proprietà dei controlli presenti nella pagina che sono state modificate durante il roundtrip, in altre parole il processo di gestione del PostBack. Eseguire l’applicazione, nel browser, fare clic su Strumenti/Strumenti di sviluppo F12. Nel campo value c’è il contenuto del ViewState della pagina, serializzato con una codifica Server side um 126 di 243 a base 64 bit. Quando la pagina è caricata nel browser, evento Page_Load, il ViewState è codificato e le proprietà in essa contenute sono associate ai controlli corrispondenti. <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title> </title><link href="Site.css" rel="stylesheet" /></head> <body> <form method="post" action="default.aspx" id="form1"> <div class="aspNetHidden"> <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="kV/ZvwlahzV65fC6pTJTrxFp5X8qZ0+Al9roCnaqVl+QnGckXn5Hf3OB0akGW mxuegcvd5ttCDP3cLg5E7PyYeDiUUNhtfL0W3p9EYXNqbg6XRTHg6L8GcgwJxYxvm VKS7lgrLugX1TmK3fFw/33Vw/cmQ26484IV0OmriKc6kRXD8vTBI8to4rAWuNmK3/o" /> </div> <div class="aspNetHidden"> <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="DlUDOlyNm7vagb53ueTN41D7/oeHNQxI5Q9XEilwbSExUolLEl1Pg4EoYD4bgPW BfARVmvz0W3RM1nYKuUO/SBdICZbmXZAD3P0aAGe2E0sqG/iQdJ9dlbUHSoo0DVh9A yaFjIdIViSXtBFPWBP0aA==" /> </div> <div id="container"> <header> <h1>Biciclette</h1> <nav> <ul> <li><a href="default.aspx">Home</a></li> <li><a href="webform1.aspx">Chi Siamo</a></li> <li><a href="webform2.aspx">Prodotti</a></li> </ul> </nav> </header> <div id="body"> <h2>Home</h2><br /> <span id="ContentPlaceHolder1_lblNominativo">Nominativo: </span><input name="ctl00$ContentPlaceHolder1$txtNominativo" type="text" id="ContentPlaceHolder1_txtNominativo" /> <br /> <input type="submit" name="ctl00$ContentPlaceHolder1$btnAggiorna" value="Aggiorna" id="ContentPlaceHolder1_btnAggiorna" /> <br /><br /> <span id="ContentPlaceHolder1_lblOra">Sono le ore: 11:07:58.9355468</span> </div> <footer>© 2023 - Biciclette</footer> </div> </form> </body> </html> File SITE.MASTER Le pagine devono avere un layout monolitico fisso a colonna singola, una testata che Server side um 127 di 243 contiene il menu di navigazioen del sito. L’intestazione è rappresentata dal TAG <header>, all’interno si usa il TAG <nav>. Il corpo è rappresentato dal TAG <body>, all’interno si usa il ContentPlaceHolder. Il piè di pagina è rappresentato dal TAG <footer>. <%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.master.cs" Inherits="WebApplication1.Site" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> <link href="Site.css" rel="stylesheet" /> </head> <body> <form id="form1" runat="server"> <div id="container"> <header> <h1>Biciclette</h1> <nav> <ul> <li><a href="default.aspx">Home</a></li> <li><a href="webform1.aspx">Chi Siamo</a></li> <li><a href="webform2.aspx">Prodotti</a></li> </ul> </nav> </header> <div id="body"> <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server"> </asp:ContentPlaceHolder> </div> <footer>© 2023 - Biciclette</footer> </div> </form> </body> </html> File SITE.CSS Definisce il layout del sito: margini, dimensioni, colore, font e allineamenti del testo e degli sfondi. html { margin: 0px; padding: 0px;} body { margin: 0px; padding: 0px;} #container { margin: 0px auto; width: 1060px; text-align: left; border-right-color: black; border-left-color: black; border-right-width: 2px; border-left-width: 2px; border-right-style: solid; border-left-style: solid; } header { color: red; background-color: grey; } footer { padding: 0.1em; text-align: center; color: red; background-color: grey; } nav { background-color: blue; } #body { padding: 1em; } nav ul { margin: 0px; padding: 0px; list-style-type: none; } nav li { margin: 0px 0px 0px 1em; padding: 0px; display: inline; } Server side um 128 di 243 nav a { font: bold arial, sans-serif; font-size:18pt;color: red; text-decoration: none; } nav a:hover { color: white; text-decoration: underline; } File WEBFORM1.ASPX Inserire, quindi, il contenuto della pagina: due Label, una TextBox e un Button. File DEFAULT.ASPX <%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="default.aspx.cs" Inherits="WebApplication1._default" %> <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server"> <h2>Home</h2><br /> <asp:Label ID="lblNominativo" runat="server" Text="Nominativo: "></asp:Label><asp:TextBox ID="txtNominativo" runat="server"></asp:TextBox> <br /> <asp:Button ID="btnAggiorna" runat="server" Text="Aggiorna" OnClick="btnAggiorna_Click" /> <br /><br /> <asp:Label ID="lblOra" runat="server"></asp:Label> </asp:Content> Server side um 129 di 243 TEMI INTRODUZIONE L’aspetto grafico di un sito può dipendere dalla definizione di stili, come colori e font, all’interno di un file CSS. ASP.NET prevede la possibilità di applicare temi al proprio sito anche a run-time. Realizzando vari modelli grafici per il proprio sito sarà possibile far scegliere all’utente quello a lui più gradevole e cambiarlo in tempo reale. Rappresentano il principale meccanismo per personalizzare la resa grafica delle applicazioni web. Un tema definisce un insieme di stili e di attributi grafici e li associa ai vari elementi e controlli utilizzati nell’ambito dell’applicazione. Ciascun tema è caratterizzato da un nome che lo identifica univocamente ed è composto dai file CSS contenenti gli stili dei TAG, dagli skin dei controlli e dalle immagini. Se da una parte un CSS contiene le impostazioni di rappresentazione dei TAG HTML e definisce le classi CSS associate agli elementi delle pagine, dall’altra lo skin è un file di testo contenente i valori predefiniti per le proprietà grafiche relative ai controlli web. Di fatto si può pensare ad uno skin come ad una specie di stylesheet server side riferito ai controlli di ASP.NET. Rispetto ad un file CSS, lo skin può contenere l’impostazione di proprietà grafiche che non hanno una corrispondenza con un attributo CSS. Anche se è possibile farlo, non sarebbe in ogni caso indicato utilizzare lo skin per impostare valori che possono essere settati utilizzando i CSS. Risulta più corretto associare allo skin di un controllo una classe CSS in cui siano indicati tutti gli attributi utili alla definizione della resa grafica. Un file .SKIN contiene l’insieme dei TAG relativi ai controlli da personalizzare. Ogni skin può essere identificato tramite un ID oppure l’identificazione può essere omessa. Lo skin senza ID rappresenta lo skin predefinito per un particolare controllo. In uno stesso file .SKIN possono coesistere più skin relativi allo stesso tipo di controllo contrassegnati con ID diversi. <asp:TextBox ID="TextBox1" runat="server" SkinID="LongText" CssClass="InputText" Columns="100" /> <asp:TextBox ID="TextBox2" runat="server" SkinID="ShortText" CssClass="InputText" Columns="10" /> <asp:TextBox ID="TextBox3" runat="server" CssClass="InputText" Columns="50" /> Nell’ambito dell’applicazione è possibile specificare un particolare skin per un controllo web tramite la proprietà SkinID. <asp:TextBox ID="txtEmail" Runat="server" SkinID="LongText"/> Per creare un tema basta inserire una nuova sotto cartella nella cartella APP_THEMES dell’applicazione web. Il nome della cartella rappresenta il nome del tema e in essa vanno posizionati i file CSS e gli skin. Nella cartella relativa al tema possono essere presenti più file .CSS e .SKIN. Le immagini vanno collocate nella sotto cartella IMAGES. I temi possono essere definiti per la singola pagina, per tutte le pagine di un’applicazione o anche a livello di sistema. Server side um 130 di 243 A livello di singola pagina il tema può essere impostato tramite l’attributo Theme nella direttiva @ Page. <%@ Page Language="C#" Theme="DarkBeer" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="Tema.WebForm1" %> È possibile associare un tema ad una pagina anche dinamicamente via codice. L’associazione dinamica può essere fatta solamente nell’ambito dell’event handler dell’evento di pagina PreInit, dato che il run-time di ASP.NET carica le informazioni relative al tema subito dopo questo evento. void Page_PreInit(object sender, EventArgs e) { string theme = "Default"; if(Page.Request["DarkBeer"] != null) theme = Page.Request["DarkBeer "].ToString(); this.Theme = theme; } A livello di applicazione, il tema può essere impostato dentro al WEB.CONFIG tramite l’attributo theme del TAG <pages>. <configuration> <system.web> <pages theme=" DarkBeer"/> </system.web> </configuration> È fondamentale che esista una corrispondenza tra il nome del tema specificato e il nome di una delle sotto cartella presenti nella cartella APP_THEMES. Server side um 131 di 243 WEB USER CONTROL INTRODUZIONE ASP.NET consente due modalità per riutilizzare il codice. 1. Custom control: sono classi vere e proprie che ereditano da Control o da un altro controllo e sono composte da codice Visual C# o Visual Basic: sono complessi, sono distribuiti su più progetti e sono compilati in Web Control Library. 2. Web User Control: sono pezzi di pagina, composti più da HTML che da codice vero e proprio, sono semplici ma meno portabili, sono file con estensione ASCX. Un Web User Control è progettato come una Web Form, quindi è dotato di un’UI e di un file con il code-behind; a differenza di una Web Form, tuttavia, è contenuto in un file ASCX e non può avere i TAG <html>, <body> e <form> poiché è inserito in una Web Form che questi TAG li contiene già. Un Web User Control è una classe ed eredita le sue funzionalità di base da SYSTEM.WEB.UI.USERCONTROL. Per definire un Web User Control, dopo aver creato un nuovo progetto ASP.NET, fare clic con il tasto destro del mouse sul nome del progetto all’interno di Esplora soluzioni e selezionare la voce Aggiungi/Nuovo elemento…. Si aprirà una finestra di dialogo in cui sono mostrati tutti i tipi di file che possono essere aggiunti, selezionare Controllo utente Web Form. Grazie al run-time comune del .NET Framework è possibile avere una Web Form scritta in Visual C# al cui interno si trova un Web User Control realizzato con Visual Basic e viceversa. Il nuovo Web User Control sarà aggiunto al progetto e contiene i seguenti file. File WEBUSERCONTROL1.ASCX <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="WebUserControl1.ascx.cs" Inherits="Temi.WebUserControl1" %> File WEBUSERCONTROL1.ASCX.CS using System; namespace Temi { public partial class WebUserControl1 : System.Web.UI.UserControl { protected void Page_Load(object sender, EventArgs e) { } } } File WEBUSERCONTROL1.ASCX.DESIGNER.CS Il codice è generato automaticamente dal template. Esempio, progettare un controllo composto da una Label e da una TextBox. Avere un Web Control che li definisce entrambi in una sola volta permette di risparmiare tempo nella creazione delle pagine. Server side um 132 di 243 Impostare le proprietà degli oggetti. lblEtichetta txtNome Label TextBox Text = Etchetta Aggiungere le proprietà che consentono di lavorare con l’etichetta e la casella di testo. Dal momento che il Web User Control, una volta inserito in una pagina ASP.NET, è visto come un unico oggetto, deve disporre di proprietà che permettano di modificare gli oggetti contenuti al suo interno. Nel file del code-behind definire le proprietà Caption e Text, per leggere e cambiare, rispettivamente, il testo della Label e il contenuto della TextBox. using System; namespace Temi { public partial class WebUserControl1 : System.Web.UI.UserControl { public string Caption { get { return lblEtichetta.Text; } set { lblEtichetta.Text = value; } } public string Text { get { return txtNome.Text; } set { txtNome.Text = value; } } protected void Page_Load(object sender, EventArgs e) { } } } Aggiungere il controllo ad una pagina ASP.NET. Visualizzare la Web Form in cui inserire l’oggetto, quindi all’interno di Esplora soluzioni, selezionare il Web User Control e trascinarlo nella posizione desiderata sulla pagina. La finestra delle proprietà dell’oggetto mostra anche le proprietà che si sono definite in precedenza. Server side um 133 di 243 I valori di Caption e Text possono essere impostati sia in fase di progettazione utilizzando la finestra delle proprietà sia da codice. using System; namespace Temi { public partial class WebForm1 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { WebUserControl1.Caption = "Sono nella label Etichetta"; WebUserControl1.Text = "Sono nella TextBox Nome"; } } } Modificando una proprietà in fase di progettazione, il Web User Control non sarà aggiornato con i nuovi valori, che, però, saranno correttamente visualizzati quando si caricherà la pagina nel browser. Selezionare il modo Origine per visualizzare il codice HTML della Web Form. Quando si aggiunge un Web User Control ad una pagina, è inserita la direttiva @ Register ad indicare che nella pagina si utilizza un controllo il cui codice è contenuto in un file esterno. <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="Temi.WebForm1" %> <%@ Register src="WebUserControl1.ascx" tagname="WebUserControl1" TagPrefix ="uc1" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>WEB USER CONTROL</title> </head> <body> <form id="form1" runat="server"> <div> <uc1:WebUserControl1 ID="WebUserControl1" runat="server" /> </div> </form> </body> </html> L’attributo src è riferito al path per arrivare al file sorgente e può essere un percorso relativo o assoluto ma deve sempre puntare ad una risorsa raggiungibile localmente. Server side um 134 di 243 L’attributo TagPrefix indica il prefisso da utilizzare per la dichiarazione nella pagina. L’attributo Namespace è il riferimento al namespace che contiene i controlli. L’attributo Assembly indica il file risultante dalla compilazione. COMUNICARE CON LA PAGINA Visualizzare a video la data attuale. File TEST1.ASCX <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="test1.ascx.cs" Inherits="Web_User_Control.test1" %> <p>Data attuale: <asp:literal id="data" runat="server" /></p> File TEST1.ASCX.CS using System; namespace Web_User_Control { public partial class test1 : System.Web.UI.UserControl { protected void Page_Load(object sender, EventArgs e) { data.Text = DateTime.Now.ToLongDateString(); } } } Il Web User control deve comunicare con gli altri soggetti presenti sulla pagina, infatti, può essere contenuto all’interno di una pagina ma anche all’interno di un altro Web User Control, di fatto non è detto che l’oggetto contenitore sia a sua volta un altro controllo di questo tipo, anzi spesso è proprio così. Per semplicità, si specifica come oggetto contenitore una pagina. Un Web user Control è un qualsiasi oggetto e quindi pertanto può essere esteso, per esempio con una serie di proprietà per far sì che l’aspetto grafico sia più gradevole. Dotare l’oggetto di una proprietà di testo di nome CssClass che serve per specificare uno stile da associare al testo scritto. // proprietà per specificare uno stile private string cssClass; public string CssClass { get { return cssClass; } set { cssClass = value; } } Associare questo valore alla rispettiva proprietà di un panel all’interno del quale si rinchiude il testo già presente sul controllo. Si deve specificare, nella pagina, il valore da associare a questa proprietà. <uc1:test2 ID="test2" runat="server" CssClass="date"/> Fare la stessa modifica per specificare un testo alternativo, da associare ad un controllo Literal inserito sul controllo, da visualizzare prima della data. // proprietà per specificare un testo di descrizione private string text = "Data attuale:"; public string Text { get { return text; } set { text = value; } } Server side um 135 di 243 Defininire un evento sul Page_Load che vada a formattare gli oggetti della pagina con le proprietà specificate. protected void Page_Load(object sender, EventArgs e) { testo.Text = Text; data.Text = DateTime.Now.ToLongDateString(); if (cssClass != null) contenitore.CssClass = cssClass; Trace.Write("Control_Data", "Render"); } MANTENERE LO STATO CON IL VIEWSTATE Assegnare le proprietà attraverso codice, si nota che, tra i vari PostBack, i valori non sono mantenuti e che, soprattutto, gli eventi si verificano, nella pagina e nei controlli, secondo modalità differenti. Sequenza degli eventi tra pagina e controlli Sia la pagina che eredita dalla classe Page, sia gli user controls che ereditano da UserControl, hanno una serie di eventi in comune che servono per eseguire codice in base al verificarsi di determinate condizioni. Il giusto ordine in cui si verificano questi eventi all’interno dei controlli è il seguente. Un Web User Control contenuto in una pagina, avrà la stessa sequenza di eventi della pagina, con la differenza che ogni evento sarà invocato subito dopo l’evento corrispondente contenuto nella pagina, in pratica, la sequenza sarà la seguente. 1. Page_Init 1.1. Della pagina 1.2. Degli user Controls 2. Page_Load 2.1. Della pagina 2.2. Degli user Controls 3. Page_PreRender 3.1. Della pagina 3.2. Degli user Controls 4. Page_Render 4.1. Della pagina 4.2. Degli user Controls Se ad esempio si devono passare, da un Web User Control, informazioni che la pagina dovrà leggere nell’evento Page_Load, si deve scrivere il codice all'interno del Page_ Init. Analogamente, se un Web User Control è contenuto in un altro Web User Control, questi eventi si verificheranno sempre nel controllo contenitore. Per quanto riguarda il primo problema, è essenzialmente dovuto al fatto che tutti i controlli che si usano sulle pagine mantengono il loro stato attraverso il ViewState. Dunque per fare in modo che il controllo mantenga lo stato delle proprietà tra i vari PostBack, si deve salvare e leggere il valore di queste proprietà da questo state bag. // proprietà per specificare uno stile private string cssClass; public string CssClass { // leggiamo dal ViewState get { return ViewState["data_cssClass"] as String; } Server side um 136 di 243 // scriviamo nel ViewState set { ViewState["data_cssClass"] = value; cssClass = value; } } Per recuperare la proprietà, si legge dal ViewState, dato che in fase di assegnazione si è salvato il valore. L’ultima modifica poi, riguarda il momento in cui applicare le proprietà al controllo. Tra gli eventi a disposizione per chi sviluppa controlli, quello che rappresenta il momento migliore in cui andare ad applicare eventuali proprietà di tipo “grafico” è Page_PreRender che si verifica dopo Page_Init e Page_Load e prima che la pagina sia costruita per essere inviata al client. Allora, bisogna spostare tutta la logica in questo evento, in modo che sia possibile effettuare tutte le operazioni quando il ViewState è stato già caricato. Aggiungere un pulsante nella pagina alla cui pressione lo stile associato al controllo sarà modificato. protected void btnCambia_Click(object sender, EventArgs e) { Trace.Write(test3.CssClass); if (test3.CssClass == "date") test3.CssClass = "datedark"; else test3.CssClass = "date"; } Server side um 137 di 243 WEB CONTROL LIBRARY INTRODUZIONE La differenza principale tra i due tipi di controlli riguarda la fase di progettazione e consiste nella semplicità di creazione da una parte e nella facilità d’uso dall’altra. I Web User Control sono semplici da creare, dal momento che si realizzano in maniera visuale, dispongono del code-behind e supportano la gestione degli eventi. Tuttavia, un Web User Control non può essere aggiunto alla Casella degli strumenti ed è rappresentato in maniera statica quando è aggiunto ad un pagina, in pratica la modifica di una sua proprietà non si riflette nelle visualizzazione all'interno del designer ma è visibile solo in run-time. Inoltre, l’unico modo di condividere il controllo tra applicazioni diverse consiste nell’inserirne una copia distinta in ciascun progetto, soluzione che richiede una maggiore gestione nel caso in cui si apportino modifiche al controllo. Una Web Control Library, invece, si realizza scrivendo a mano il codice che poi sarà compilato, dunque è più difficile da realizzare. Una volta realizzato il controllo, tuttavia, è possibile aggiungerlo alla Casella degli strumenti e gestirlo in modo analogo a quanto visto per i controlli standard. CREAZIONE Per creare un nuovo progetto, fare clic su File/Nuovo/Progetto… (CTRL+MAIUSC+N). Nella finestra di dialogo Nuovo Progetto selezionare Modelli/Visual C#/Web, quindi selezionare il tipo di applicazione, Controllo Server ASP.NET. File SERVERCONTROL1.CS using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; namespace ServerControl { [DefaultProperty("Text")] [ToolboxData("<{0}:ServerControl1 runat=server></{0}:ServerControl1>")] public class ServerControl1 : WebControl { [Bindable(true)] [Category("Appearance")] [DefaultValue("")] [Localizable(true)] public string Text { get { String s = (String)ViewState["Text"]; return ((s == null)? "[" + this.ID + "]" : s); } set { ViewState["Text"] = value; } } protected override void RenderContents(HtmlTextWriter output) Server side um 138 di 243 { output.Write(Text); } } } Eseguire il progetto, compare la finestra seguente perché si ottiene SERVERCONTROL1.DLL che sarà utilizzato da un’applicazione ASP.NET. il file Ogni controllo estende la classe WebControl che fornisce proprietà, metodi ed eventi comuni a tutti i controlli server web. Alla dichiarazione della classe e alla proprietà Text sono stati aggiunti, tra ([]), i Custom Attributes, in altre parole una serie di attributi che fornisce informazioni aggiuntive sulle classi, sui metodi e sulle proprietà. Ad esempio, ToolboxData indica quale TAG è associato al controllo: in pratica, quando sarà inserito in una pagina, esso sarà identificato dal TAG specificato in questo attributo. Category specifica la categoria in cui sarà visualizzata la proprietà Text all’interno della visualizzazione per categorie della finestra Proprietà. Il metodo RenderContents, è il cuore del Web Control: specifica qual è il codice HTML che dev’essere inserito nella pagina per visualizzare il controllo, riceve come parametro un oggetto di tipo HtmlTextWriter su cui scrivere il codice HTML. Il metodo RenderContents è automaticamente richiamato dal run-time del .NET sia quando il controllo è visualizzato in una pagina ASP.NET sia quando dev’essere generato il contenuto HTML da inviare al browser. Nel primo caso, output rappresenta la pagina all'interno dell’editor, nel secondo si riferisce alla pagina che è inviata al client per la visualizzazione nel browser, in pratica, il codice HTML che è scritto dal metodo RenderContents è il codice che l’utente riceve quando richiede la pagina al server. Anche i controlli web standard si comportano nello stesso modo: ogni controllo inserito in una pagina genera il codice HTML necessario alla sua visualizzazione tramite il metodo RenderContents, ad esempio, in un controllo Label produce il seguente codice HTML. <span id="Label1">Label</span> Il metodo RenderContents si limita a scrivere nella pagina il contenuto della variabile Text, senza aggiungere alcuna formattazione HTML. UTILIZZO Per aggiungere un nuovo progetto, fare clic su File/Aggiungi/Progetto…. Nella finestra di dialogo Nuovo Progetto selezionare Altri linguaggi/Visual C#/Web, quindi selezionare il tipo di applicazione, Applicazione Web ASP.NET vuota. Fare clic destro in Esplora soluzioni sul nome del progetto, dal menu contestuale Server side um 139 di 243 selezionare Aggiungi/Nuovo elemento… (CTRL+MAIUSC+A). Alla voce Visual C# selezionare Web/Web Form. Aprire DEAFULT.ASPX in modalità Progettazione. La Casella degli strumenti ha una nuova scheda che corrisponde al ServerControl e contiene ServerControl1 che può essere inserito nella pagina. Aggiungere il contollo alla pagina e, nella finestra delle proprietà, modificare il valore di Text: a differenza di quanto avviene con i Web User Control, la modifica si riflette anche in fase di progettazione. Eseguire l’applicazione. Esempio, modificare il controllo affinchè il testo specificato appaia in grassetto. È sufficiente modificare il codice HTML generato dal controllo, in pratica intervenire sul metodo Render. Poichè il metodo Write dell’oggetto HtmlTextWriter si limita a scrivere sulla pagina tutto quello che riceve come parametro, basta inserire tutto in un’unica istruzione. output.Write("<b>" + Text + "</b>"); Questa soluzione è corretta ma rischia di creare confusione, inoltre, la possibilità di dimenticare qualche (<>) oppure (/) cresce all’aumentare del numero di TAG da inserire. Server side um 140 di 243 Si possono usare altri metodi messi a disposizione da HtmlTextWriter per produrre codice più leggibile e con una distinzione maggiore tra TAG HTML e contenuto vero e proprio. protected override void Render(HtmlTextWriter output) { output.WriteBeginTag("b"); output.Write(HtmlTextWriter.TagRightChar); output.Write(Text); output.WriteEndTag("b"); } Il metodo WriteBeginTag scrive il (<) e il TAG indicato come parametro ma non lo chiude con (>) perchè, in generale, esso potrebbe contenere degli attributi, da specificare con il metodo WriteAttribute. Per questo motivo è necessaria la successiva istruzione di Write che scrive il carattere (>) (HtmlTextWriter.TagRightChar). In alternativa a queste due istruzioni, si può usare la riga seguente. // scrive <b> output.WriteFullBeginTag("b"); Dopo aver stampato il testo vero e proprio, chiudere il TAG con il metodo WriteEndTag. Ricompilare il controllo ed eseguire l’applicazione web. Visualizzare il sorgente della pagina all’interno del browser web, ad esempio, in IExplorer, con il comando Visualizza/Origine, il testo specificato nella proprietà Text del controllo è stato inserito tra i TAG <b>Ciao, mondo!</b>. Anche questi controlli, come i Web Control, una volta inseriti in una pagina prevedono la direttiva @ Register. <%@ Register assembly="ServerControl" namespace="ServerControl" TagPrefix ="cc1" %> WEB USER CONTROL O WEB CONTROL LIBRARY Se si ha bisogno di un controllo che abbia quasi solo funzionalità visive, allora un Web User Control è perfetto, perché è un file sorgente con HTML mischiato ad un po’ di codice. Se, invece, si ha bisogno di un “vero” controllo, con possibilità di creare una classe, gestirla, ereditarla, o più semplicemente se si ha bisogno di modificare un controllo esistente, come il Repeater, perché offra funzionalità aggiuntive, allora la scelta cade sui Web Control Library. La vera differenza è che questi ultimi sono compilati in assembly, mentre i Web User Control no. Server side um 141 di 243 CONVALIDA INTRODUZIONE Un’applicazione web dev’essere in grado d’individuare gli errori (gli eventi non previsti) che si verificano mentre l’utente la usa e deve correggerli (gestirli) per consentire una corretta conclusione delle operazioni. Nel caso in cui non sia possibile correggerli, allora bisogna avvisare l’utente con informazioni che gli facciano capire come proseguire. Metodologie di gestione degli errori: da quella più bassa a quella più alta. 1. A LIVELLO DI WEB.CONFIG All’interno dell’area <system.web> usando il TAG <customErrors> è possibile definire delle pagine di errore su cui redirigere l’utente quando si verifica un errore. È possibile definire sia una pagina di errore di default, ErrorPage.html, sia pagine specifiche per tipologia di errore, per esempio per i codici di errore 401 (necessaria autenticazione) ErrorPage401.html, 404 (risorsa non trovata) ErrorPage404.html e 500 (errore interno del server) ErrorPage500.html. Inoltre, bisogna settare le proprietà mode="On" per informare ASP.NET che deve redirezionare l’utente alle pagine di errore indicate. La gestione degli errori a questo livello è la più semplice disponibile ma anche la meno dettagliata, infatti, non è possibile dare all’utente informazioni dettagliate circa l’errore che si è verificato. Questo perché quando si verifica un errore, il CLR se ne accorge e redireziona l’utente sulla pagina corrispondente alla tipologia di errore che si è verificato. In questo redirezionamento, però, è persa la traccia dell’errore. <?xml version="1.0" encoding="utf-8"?> <configuration> <system.web> <customErrors mode="On" defaultRedirect="ErrorPage.html"> <error statusCode="401" redirect="ErrorPage401.html"/> <error statusCode="404" redirect="ErrorPage404.html"/> <error statusCode="500" redirect="ErrorPage500.html"/> </customErrors> <compilation debug="true" targetFramework="4.5" /> </system.web> </configuration> È buona regola di programmazione definire sempre in questo file una pagina di errore. File ERRORPAGE.HTML <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> </head> <body> <h2>Errore: Si è verificato un errore non gestito dall'applicazione.</h2> <br /> <br /> Server side um 142 di 243 </body> </html> File ERRORPAGE401.HTML <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> </head> <body> <h2>Errore: 401--Unauthorized--Necessaria autenticazione.</h2> <br /> <br /> </body> </html> File ERRORPAGE404.HTML <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> </head> <body> <h2>Errore: 404--Not found--Il server non ha trovato la risorsa richiesta.</h2> <br /> <br /> </body> </html> File ERRORPAGE500.HTML <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> </head> <body> <h2>Errore: 500--Internal server error--Errore interno al server.</h2> <br /> <br /> </body> </html> 2. A LIVELLO di APPLICAZIONE WEB Quando si verifica un errore in un’applicazione web, difficilmente si riesce a risolverlo, perché spesso gli errori dipendono da risorse al momento non disponibili, per esempio WS offline, DB o server non raggiungibili. In questi casi l’intervento da codice da parte del programmatore non serve. Per questo motivo è meglio gestire gli errori in modo centralizzato, in altre parole a livello di applicazione web. Bisogna scrivere il codice di gestione degli errori all’interno del metodo Application_Error che si trova nel file GLOBAL.ASAX. protected void Application_Error(object sender, EventArgs e) Server side um 143 di 243 { // prendiamo l'ultimo errore dal server Exception exc = Server.GetLastError(); if (exc is HttpUnhandledException) { if (exc.InnerException != null) { HttpContext.Current.Session["lastError"] = exc; Response.Redirect("ErrorPage.aspx", false); Server.ClearError(); } } } Questo metodo scatta ogni volta che si verifica un errore che non è stato gestito dall’applicazione web. L’istruzione Server.GetLastError prende l’ultima eccezione sollevata, se si tratta di un’eccezione non gestita HttpUnhandledException si verifica se possiede un’eccezione interna InnerException. Quando un’eccezione sollevata non è gestita dall’applicazione web, il CLR crea un’eccezione non gestita di tipo HttpUnhandledException all’interno della quale colloca l’eccezione originale. Adesso si mette questa eccezione originale in sessione, in modo da averla a disposizione nella pagina di errore ERRORPAGE.ASPX, grazie all’istruzione Response.Redirect. Per evitare di mettere l’eccezione in sessione, si potrebbe usare il metodo Server.Transfer che conserva l’informazione sull’errore, cosa che, invece, non fa il metodo Response.Redirect. Server.Transfer non modifica l’indirizzo del browser, quindi, l’utente non capisce che si trova su una pagina diversa da quella dove si è verificato l’errore. Dopo aver gestito l’errore, bisogna eliminarlo dal server con l’istruzione Server.ClearError, se non lo si fa l’errore continuerebbe a essere considerato come non risolto e potrebbe essere gestito nuovamente in un’altra parte dell’applicazione web. La gestione degli errori deve prevedere l’uso di pagine di errore che siano chiare per l’utente, in pratica devono spiegare che errore si è verificato, cosa comporta l’errore e cosa si deve fare in seguito. Inoltre, occorrono delle sezioni che riportino informazioni dettagliate per il programmatore, in modo tale che quando l’applicazione web gira in fase di test possa individuare il punto che ha generato l’errore. File ERRORPAGE.ASPX <%@ Page Language="C#" AutoEventWireup="true" MasterPageFile="~/Site.Master" CodeBehind="ErrorPage.aspx.cs" Inherits="WebApplication1.ErrorPage" %> <asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server"> <h2>Errore:</h2> <br /> <%-- area utente--%> Cosa è successo: <asp:Label ID="FriendlyErrorMsg" runat="server" Text="Label" FontSize="Large" style="color: red"></asp:Label> <asp:Panel ID="ErrorPanel" runat="server"> <p> Conseguenze: <br /> <asp:Label ID="WhatErrorMsg" runat="server" Font-Bold="true" Font-Size="Large" /><br /> </p> Server side um 144 di 243 <p> Cosa puoi fare: <br /> <asp:Label ID="ActionsErrorMsg" runat="server" Font-Bold="true" FontSize="Large" /><br /> </p> <p> Informazioni di supporto: <br /> <asp:Label ID="SupportErrorMsg" runat="server" Font-Bold="true" FontSize="Large" /><br /> </p> </asp:Panel> <br /> <br /><br /> <br /> <%-- area programmatore --%> <asp:Panel ID="DetailedErrorPanel" runat="server" Visible="false"> <p> Errore Dettagliato: <br /> <asp:Label ID="ErrorDetailedMsg" runat="server" Font-Bold="true" FontSize="Large" /><br /> </p> <p> Messaggio di errore dettagliato: <br /> <asp:Label ID="InnerMessage" runat="server" Font-Bold="true" Font-Size="Large" /><br /> </p> <pre> <asp:Label ID="InnerTrace" runat="server" /> </pre> </asp:Panel> </asp:Content> Il codice associato all’area utente si trova nel file ERRORPAGE.ASPX.CS. Exception ex = (Exception)Session["lastError"]; // se l'eccezione non esiste più allora creane una generica if (ex == null) ex = new Exception(unhandledErrorMsg); else { if (ex.InnerException is CustomException) { CustomException myex = (CustomException)ex.InnerException; FriendlyErrorMsg.Text = myex.WhatHappened; WhatErrorMsg.Text = myex.WhatHasBeenAffected; ActionsErrorMsg.Text = myex.WhatActionsCanUserDo; SupportErrorMsg.Text = myex.SupportInformation; } } Bisogna prendere l’eccezione che si era messa in sessione nel metodo Application_Error. Se l’eccezione è nulla, si crea un’eccezione base con un messaggio di errore generico. Nel caso in cui l’eccezione presa dalla sessione ne ha una interna InnerException di tipo CustomException, in pratica quella personalizzata, allora s’istanzia un’eccezione di questo Server side um 145 di 243 tipo e si valorizzano le caselle di testo della pagina di errore con le proprietà corrispondenti dell’eccezione personalizzata. La proprietà InnerException restituisce un’eccezione generica di tipo Exception, quindi si deve fare il cast esplicito a CustomException. Il codice associato all’area programmatore si trova nel file ERRORPAGE.ASPX.CS. // visualizza i dettagli degli errori solo per il programmatore in fase di test if (Request.IsLocal) { // mostra il pannello per il programmatore DetailedErrorPanel.Visible = true; // messaggio di errore dettagliato ErrorDetailedMsg.Text = ex.Message; if (ex.InnerException != null) { InnerMessage.Text=ex.GetType().ToString()+"<br/>"+ex.InnerException.Message; InnerTrace.Text = ex.InnerException.StackTrace; } else { InnerMessage.Text = ex.GetType().ToString(); if (ex.StackTrace != null) InnerTrace.Text = ex.StackTrace.ToString().TrimStart(); } } Attraverso la proprietà IsLocal della classe Request si vede se la richiesta HTTP è arrivata dalla stessa macchina in cui risiede l’applicazione web. Se l’utente è il programmatore, allora si visualizza il pannello. Si valorizzano le caselle di testo con i valori presenti nelle proprietà della classe Exception ovvero GetType che dice di che tipo di eccezione si tratta, Message riporta il messaggio di errore restituito dall’eccezione e StackTrace riporta l’intero percorso dell’errore a partire dalla riga di codice della pagina dove si è verificato fino alla riga di codice della pagina dove è stato gestito. Esempio, simulare un errore sollevando un’eccezione con l’istruzione throw. File DEFAULT.ASPX <%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="default.aspx.cs" Inherits="WebApplication1._default" %> <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server"> <h2>Home</h2><br /> <asp:Label ID="lblOra" runat="server"></asp:Label> </asp:Content> File DEFAULT.ASPX.CS using System; namespace WebApplication1 { public partial class _default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { InvalidOperationException ex1 = new InvalidOperationException("An InvalidOperationException "); try { throw ex1; } Server side um 146 di 243 catch (Exception exsss) { throw new CustomException(ex1.Message, ex1, "E' stata eseguita un’operazione non valida", "l'esecuzione si è interrotta", "riprova a caricare la pagina", "se il problema persiste scrivi a: [email protected]"); } } } } Si è istanziata un’eccezione di tipo InvalidOperationException e si è sollevata dentro ad un blocco try. Questo fa in modo che l’esecuzione del codice passi all’interno del blocco catch dove si solleva l’eccezione personalizzata di tipo CustomException. Al costruttore di questa eccezione si passa il messaggio di errore dell’eccezione originale. A questo punto il CLR genererà un’eccezione non gestita perché si è sollevata un’eccezione che non sarà gestita da nessun’altra parte nell’applicazione web. Questo significa che andrà in esecuzione il metodo Application_Error che metterà l’eccezione personalizzata in sessione e farà un redirect alla pagina di errore area utente. Testare l’applicazione web. Impostare la pagina DEFAULT.ASPX come pagina iniziale. Inserire un breakpoint nel metodo Page_Load del file DEFAULT.ASPX.CS. Inserire un breakpoint nel metodo Application_Error del file GLOBAL.ASAX. Inserire un breakpoint nel metodo Page_Load del file ERRORPAGE.ASPX. 3. A LIVELLO di PAGINA La gestione degli errori su ogni pagina o su pagine particolari è gestita con il codice seguente. File DEFAULT2.ASPX.CS using System; namespace WebApplication1 { public partial class _default2 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { throw new InvalidOperationException("An InvalidOperationException "); } private void Page_Error(object sender, EventArgs e) { // Get last error from the server. Exception exc = Server.GetLastError(); // prendi l'ultimo errore dal server if (exc is InvalidOperationException) { // passa l'errore alla pagina di errore Server.Transfer("ErrorPage2.aspx", true); } } Server side um 147 di 243 } } All’interno del sorgente di una pagina si deve definire il metodo Page_Error e all’interno del metodo si deve prendere l’errore con il metodo GetLastError delal classe Server. Se l’eccezione è di tipo InvalidOperationException si chiama la pagina di errore secondaria ERRORPAGE2.ASPX. Testare l’applicazione web. Impostare la pagina DEFAULT2.ASPX come pagina iniziale. Inserire un breakpoint nel metodo Page_Load del file DEFAULT.ASPX.CS. Inserire un breakpoint nel metodo Page_ERROR del file DEFAULT.ASPX.CS. Inserire un breakpoint nel metodo Page_Load del file ERRORPAGE2.ASPX. 4. A LIVELLO di CODICE Il programmatore gestisce gli errori per risolvere gli inconvenienti o per lo meno informare dettagliatamente l’utente. string pippo = "testo"; try { // apro una connessione ad un DB Conn.Open(); // eseguo una query sul DB, converto in decimale la variabile pippo: eccezione Decimal n = Decimal.Parse(pippo); } catch (Exception ex) { Server.Transfer("ErrorPage.aspx", true); } finally { // chiudo la connessione Conn.Close(); } LOGGING È buona regola di programmazione registrare gli errori per effettuare un monitoraggio, una verifica e consentire la loro correzione. Nel file di log bisognerebbe tenere traccia anche degli eventi principali che caratterizzano l’applicazione web. Log4net Permette di generare log da codice su diversi supporti persistenti. Può essere usato sia per monitorare gli errori sia per tenere traccia delle operazioni più importanti svolte durante le sessioni di lavoro dagli utenti. http://logging.apache.org/log4net Per utilizzarlo aggiungere le seguenti righe nel file WEB.CONFIG, indicare il percorso Server side um 148 di 243 dove memorizzare il log e il nome del file <log4net> <appender name="RollingFile" type="log4net.Appender.RollingFileAppender"> <file value="F:\Esercizi\asp\WebApplication1\web.log" /> <appendToFile value="true" /> <maximumFileSize value="1024KB" /> <maxSizeRollBackups value="5" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date %level %logger - %message%newline" /> </layout> </appender> <root> <level value="DEBUG" /> <appender-ref ref="RollingFile" /> </root> </log4net> Aggiungere un riferimento alla libreria LOG4NET.DLL nel progetto. Importare la libreria nei file dove si vuole usarla. using log4net; Richiamare il metodo GLOBAL.ASAX. seguente nell’evento Application_Start presente nel file protected void Application_Start(object sender, EventArgs e) { log4net.Config.XmlConfigurator.Configure(); } Definire il log nella pagina dove serve e richiamare il suo metodo Error per annotare l’errore. private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase. GetCurrentMethod().DeclaringType); log.Error("Si è verificato un errore:", ex); La variabile di tipo ILog rappresenta il log e si passa al suo metodo Error il messaggio di errore e l’eccezione intercettata. Esempio, file di log creato da Log4net. Server side um 149 di 243 ELMAH Funziona esclusivamente su applicazioni ASP.NET. Non si deve scrivere codice e l’installazione aggiunge in automatico le righe di configurazione nel file WEB.CONFIG. I log prodotti sono consultabili nel file ELMAH.AXD. Permette di monitorare le sole eccezioni non gestite dall’applicazione http://code.google/p/elmah CONTROLLI DI VALIDAZIONE/VALIDATORI ASP.NET supporta nativamente la validazione dei dati all’interno di un form perchè ogni validatore deriva da una classe BaseValidator che a sua volta deriva dalla classe Label, quindi il programmatore non dovrà più sviluppare codice JavaScript per realizzare le stesse cose. In effetti l’obiettivo finale dell’utilizzo di un controllo di validazione è quello di mostrare un’informazione, tramite un’etichetta di testo che illustri se e quale errore si sia verificato nel compilare i campi di una pagina client side o anche in qualche operazione avvenuta server side. Ogni controllo validatore può riferirsi ad un controllo d’input, per esempio una TextBox, una DropDownList e ognuno dei controlli d’input potrà essere affidato alla verifica da parte di più controlli di validazione. Per esempio, nel caso in cui la TextBox debba contenere un indirizzo email e questo debba essere obbligatoriamente inserito, si potrebbe associare alla casella di testo sia un controllo che verifichi la validità del formato sia un secondo che verifichi l’avvenuta compilazione del campo. La classe Page fornisce una proprietà Validators che restituisce la collezione di tutti i controlli validatori presenti nella pagina stessa. I controlli presenti nella collezione possono essere poi validati tutti in un sol colpo, ad esempio quando si fa clic su un pulsante per inviare i dati di un modulo al server, oppure singolarmente. Per gestire il primo caso, la classe Page fornisce il metodo Validate, posseduto anche da ogni controllo derivato da BaseValidator che mette poi a disposizione una proprietà IsValid che restituisce un valore booleano indicante se il controllo contiene dati validi, impostato appunto dopo la chiamata al metodo Validate. Per essere certi che la convalida sia avvenuta, server side, si deve richiamare la proprietà IsValid della classe Page, dimenticando di farlo, l’effetto sarà di non verificare la convalida, dato che quella client side può essere aggirata disattivando il supporto JavaScript. Per associare un controllo di validazione ad un controllo d’input, la classe BaseValidator fornisce la proprietà ControlToValidate che dev’essere impostata utilizzando il valore della proprietà ID del controllo destinatario della validazione. Le classi che è possibile validare sono identificate dall’attributo ValidateProperty e fra questi, quelli utilizzati sono i controlli TextBox, ListBox, DropDownList, File Upload, RadioButtonList, mentre non sono associabili ad un controllo validatore i controlli CheckBox, RadioButton e CheckBox List. Ciò non significa che questi ultimi non sono verificabili ma si dovrà fare del lavoro in più, con un validatore personalizzato. Ogni validatore possiede la proprietà Display che può assumere i seguenti valori. Static: lo spazio per la Label contenente il messaggio di errore è riservato sulla pagina e occupato sia che l’errore si verifichi sia nel caso contrario. Dynamic: il calcolo dello spazio da allocare sulla pagina è fatto dinamicamente, questo è di fondamentale importanza quando si utilizzano più controlli di validazione affiancati, in quanto nel caso si verifichi solo uno degli errori e magari non il primo, la Label che Server side um 150 di 243 mostra l’errore apparirebbe in mezzo alla pagina, lasciando uno sgradevole spazio bianco riservato staticamente ad un altro errore. None: non visualizza il messaggio anche in caso di errore. Esempio, modulo di registrazione. Tutti i campi sono obbligatori tranne il campo Anno di nascita. Il campo Username dev’essere compilato con una stringa lunga da 5 a 15 caratteri. Il campo Password dev’essere confermato dal campo di Conferma password. Il campo Email deve contenere una stringa nel formato [email protected]. Verificare i campi obbligatori Quando è necessario rendere obbligatoria la compilazione di un campo d’input, si usa il validatore RequiredFieldValidator. Il primo passo è creare il controllo aggiungendolo alla pagina, in corrispondenza del punto in cui si vuole visualizzare il messaggio, trascinandolo dalla barra degli strumenti di Visual Studio, oppure inserendo nel codice HTML il TAG corrispondente. Per esempio per controllare che il campo Username sia stato compilato si deve scrivere il codice seguente. <td style="width: 158px; text-align: left;"> <asp:TextBox ID="txtUsername" runat="server" Width="148px"></asp:TextBox></td> </td> <td> <asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="txtUsername" Display="Dynamic" ErrorMessage="Username obbligatoria">*</asp:RequiredFieldValidator> <asp:CustomValidator ID="UsernameLengthValidator" runat="server" ErrorMessage="Username deve essere lungo da 5 a 15 caratteri" OnServerValidate="UsernameLengthValidator_ServerValidate" ControlToValidate="txtUsername" SetFocusOnError="True" ClientValidationFunction="ControllaLunghezzaUsername" Display="Dynamic" EnableClientScript="False"></asp:CustomValidator> </td> Il controllo d’input di cui verificare l’avvenuta compilazione, è indicato tramite la proprietà ControlToValidate. La proprietà ErrorMessage serve a impostare il messaggio da visualizzare nei casi in cui si verifichi un errore, nell’esempio l’assenza della stringa nel campo txtUsername. Se al posto della proprietà ErrorMessage o in concomitanza ad essa, s’imposta la proprietà Text, il valore di quest’ultima sarà visualizzato in ogni caso all’esecuzione della pagina. Così facendo sarà possibile impostare un valore che indichi all’utente d’inserire lo Username, per esempio un (*), mentre il valore di ErrorMessage potrà essere visualizzato Server side um 151 di 243 in un riepilogo definito tramite un ValidationSummary. Eseguire l’applicazione e premere il pulsante Invia senza compilare alcuno dei campi. La pagina web ha una serie di errori mostrati sia con singoli validatori sia con un ValidationSummary e anche con una MessageBox. Confrontare due campi Il controllo CompareValidator esegue un confronto fra i valori inseriti in due campi grazie alla proprietà ControlToCompare, oppure fra il contenuto di un campo e un altro valore costante assegnato con ValueToCompare, la proprietà Type specifica il tipo di valore mentre Operator il tipo di operatore da usare. <asp:CompareValidator ID="CompareValidator1" runat="server" ControlToCompare="txtPassword" ControlToValidate="txtConfermaPassword" ErrorMessage="I campi password non coincidono"> </asp:CompareValidator> È necessario indicare il controllo da validare, mediante il suo ID, nell’esempio txtConfermaPassword, il controllo con cui eseguire il confronto è invece determinato dalla proprietà ControlToValidate, quindi la proprietà ErrorMessage contiene la stringa da mostrare nel caso di valore inserito non valido. Se, invece, si vuol eseguire un controllo fra il contenuto di un campo e una costante, si possono utilizzare diverse proprietà. Per esempio, verificare che il campo Anno di nascita, se compilato, contenga un valore numerico e che sia minore dell’anno corrente. Il tipo di confronto da eseguire è determinato dalla proprietà Operator che può essere impostata ad uno dei valori dell’enumerazione ValidationCompareOperator, i cui membri definiscono gli operatori di confronto. Server side um 152 di 243 Per default sarà considerato come Equal, in pratica un confronto di uguaglianza. Nell’esempio si esegue il controllo da codice e piuttosto che impostare una costante 2023, si ricava l’anno attuale dalla data del sistema, impostando la proprietà ValueToCompare. Il valore è intero, allora s’imposta la proprietà Type al valore ValidationDataType.Integer. protected void Page_Load(object sender, EventArgs e) { YearCompareValidator.ValueToCompare = DateTime.Today.Year.ToString(); YearCompareValidator.Type = ValidationDataType.Integer; YearCompareValidator.Operator = ValidationCompareOperator.LessThan; } Verificare intervalli di valori Il controllo RangeValidator consente di verificare che il valore inserito in un controllo sia compreso in un intervallo di valori predeterminato oppure calcolato a run-time. Per esempio, per verificare che il campo Anno contenga un valore positivo e minore di 2023, basta aggiungere il seguente codice di markup. <asp:TextBox ID="txtAnno" runat="server"></asp:TextBox> <asp:RangeValidator ID="RangeValidatorAnno" runat="server" ControlToValidate="txtAnno" ErrorMessage="Inserire un valore fra 0 e 2023" MaximumValue="2023" MinimumValue="0" Type="Integer"> </asp:RangeValidator> È impostato il tipo Integer mediante la proprietà Type, mentre l'intervallo di valori consentito è determinato dai valori dati a MinimumValue e MaximumValue. La stessa cosa può essere realizzata da codice. RangeValidatorAnno.MaximumValue = Datetime.Today.Year.ToString(); Espressioni regolari Le applicazioni ASP.NET hanno il controllo RegularExpressionValidator per convalidare l’input inserito dagli utenti nelle TextBox delle Webforms. Con la proprietà ValidationExpression è possibile impostare il pattern d’input corretto, in modo da segnalare all’utente eventuali errori e impedire l’invio dei dati. Nella finestra Proprietà, se si fa clic sulla proprietà ValidationExpression del controllo, è possibile utilizzare alcuni template offerti dalla finestra di dialogo. Server side um 153 di 243 <td style="width: 158px; height: 21px"> <asp:TextBox ID="txtEmail" runat="server" Width="148px"></asp:TextBox> </td> <td style="height: 21px"> <asp:RequiredFieldValidator ID="RequiredFieldValidator4" runat="server" ControlToValidate="txtEmail" Display="Dynamic" ErrorMessage="Email obbligatoria">*</asp:RequiredFieldValidator> <asp:RegularExpressionValidator ID="EmailRegularExpressionValidator" runat="server" ControlToValidate="txtEmail" ErrorMessage="Indirizzo non valido" ValidationExpression="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"> </asp:RegularExpressionValidator></td> Visual Studio permette d’impostare alcune delle espressioni regolari più comuni. Fare clic su Modifica/Trova e sostituisci/Sostituisci nei file (CTRL+MAIUSC+H). Nella finestra che si apre espandere Opzioni di ricerca, segno di spunta in Usa Espressioni regolari, i pulsanti dei campi Trova: e Sostituisci con: sono disponibili. Server side um 154 di 243 Validazione personalizzata Se nessuno dei precedenti validatori fornisse le funzionalità ricercate o se si volesse validare un controllo che non è marcato con l’attributo ValidationProperty, si usa il controllo CustomValidator. Nell’esempio, il campo Username deve contenere una stringa di lunghezza minima 5 caratteri e massima di 15. Per eseguire questa verifica si possono usare due metodi. Client side Inserire il nome della funzione JavaScript, ControllaLunghezzaUsername, da eseguire nella proprietà ClientValidationFunction. La proprietà ValidateEmptyText indica che il contenuto dev’essere verificato anche se il campo è vuoto. Il parametro args ricava il valore del campo e imposta la validità con la proprietà IsValid. function ControllaLunghezzaUsername(source, args) { if(args.Value.length >4 && args.Value.length <16 ) args.IsValid=true; else args.IsValid = false; } Server side um 155 di 243 Server side. Bisogna gestire l’evento ServerValidate. protected void UsernameLengthValidator_ServerValidate(object source, ServerValidateEventArgs args) { if (args.Value != null) { int len = args.Value.Length; args.IsValid = (len > 4 && len < 16); } } È anche possibile tralasciare d’impostare la proprietà ControlToValidate e quindi ricavare server side i valori contenuti in tutti i controlli che si vuole, dato che in tal caso la proprietà Value del parametro args sarà uguale ad una stringa vuota. Bisogna però tenere presente che il metodo UsernameLengthValidator_ServerValidate è eseguito dopo un PostBack e quindi solo nel caso in cui non ci siano altri errori gestiti da altri validatori nella pagina a bloccare tale PostBack. Riepilogare gli errori Il controllo ValidationSummary permette di riepilogare in una singola Label tutti gli eventuali errori trovati dai validatori di una pagina. È possibile mostrare la lista di errori in differenti modi, definiti dall’enumerazione ValidationSummaryDisplayMode, impostando la proprietà DisplayMode del controllo. Per default essa è pari al valore BulletList, in pratica è un elenco puntato. È possibile anche fornire un testo d’intestazione con la proprietà HeaderText. Oltre alla Label sulla pagina, in corrispondenza del ValidationSummary, è possibile visualizzare una MessageBox con lo stesso riepilogo, impostando la proprietà ShowMessage Box. <asp:ValidationSummary ID="ValidationSummary1" runat="server" HeaderText="Riepilogo degli errori" ShowMessageBox="True" Width="411px" /> Il riepilogo degli errori è visualizzato solo se c’è almeno un errore nella pagina e se la proprietà ShowSummary non è stata impostata a False. Esempio. File DEFAULT1.ASPX <%@ Page Language="C#" CodeFile="Validator.aspx.cs" Inherits="Form" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Validazione dati</title> </head> <body> <form runat="server"> <div> <asp:PlaceHolder ID="EntryForm" runat="server">Inserisci il tuo nome: <asp:TextBox ID="txtNome" runat="server" /> <asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="txtNome" ErrorMessage=" Casella di testo vuota!" /> <br /> <asp:Button ID="btnConferma" runat="server" Text="Conferma" Server side um 156 di 243 OnClick="VisualizzaNome" /> </asp:PlaceHolder> <asp:PlaceHolder ID="Results" runat="server" Visible="false"> Il tuo nome è <asp:Literal ID="lblNome" runat="server" /> </asp:PlaceHolder> </div> </form> </body> </html> File DEFAULT1.ASPX.CS using System; using System.Web.UI; public partial class Form3 : Page { protected void VisualizzaNome(object sender, EventArgs e) { EntryForm.Visible = false; Results.Visible = true; lblNome.Text = txtNome.Text; } } RequiredFieldValue Effettua il controllo più semplice: verifica che ci sia del testo. UnobtrustiveValidation Grazie all’adaptive rendering di ASP.NET, è possibile attivare questa funzionalità continuando ad utilizzare i validatori. Uno switch nel WEB.CONFIG e l’uso della libraria di jQuery consentono un sistema di validazione estendibile, personalizzabile e moderno. <add name="ValidationSettings:UnobtrusiveValidationMode" value="WebForms" /> L’attivazione può avvenire anche da codice, impostando la proprietà SYSTEM.WEB.UI.VALIDATIONSETTINGS.UNOBTRUSIVEVALIDATIONMODE. A questo punto, il codice prodotto dai controlli sarà differente: si avranno degli attributi nell’HTML e non una serie di registrazioni, nella pagine, d’istruzioni JavaScript, includere nell’applicazione la libreria MICROSOFT.JQUERY.UNOBTRUSIVE.VALIDATION. ANTIXSS LIBRARY È una libreria dedicata alla sicurezza, applica meccanismi più robusti di controllo delle stringhe ed è maggiormente indicata quando si vuole applicare un algoritmo più aggressivo alla gestione dei dati che arrivano dall’utente. Va attivata agendo sul WEB.CONFIG. Server side um 157 di 243 <httpRuntime encoderType = "System.Web.Security.AntiXss.AntiXssEncoder,System.Web,Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> Sfrutta la possibilità per alcune chiamate, metodi HtmlEncode/Decode UrlEncode/Decode di HttpUtility, di sfruttare il provider model e, quindi, di essere rimappate su un’implementazione differente. In questo caso, le chiamate che già si hanno a questi metodi sono automaticamente rigirate sull’implementazione basata sull’AntiXSS Library, con benefici da parte della sicurezza. Anche il sistema di validazione delle richieste è stato migliorato, semplificando le modalità con cui introdurre le eccezioni. Per aggiungere le regole di validazione sui dati, si usano gli attributi di validazione contenuti nel namespace System.ComponentModel.DataAnnotations. Creare un’apposita classe all’interno della quale, grazie all’utilizzo degli attributi, si possono definire le regole di validazione. Le classi che costituiscono il modello dati creato con EF sono classi partial. Ciò consente di creare una nuova classe partial con lo stesso nome e nello stesso namespace e aggiungervi la logica di controllo personalizzata. Esempio, creare una classe Contatto e decorarla con l’attributo MetaDataType che accetta come parametro un tipo che identifica una classe destinata a contenere le regole di validazione e i metadati necessari per istruire il run-time di ASP.NET MVC nel trattare i dati. namespace NegozioMvcApp.Models { [MetadataType(typeof(ContattoMetadata))] public partial class Contatto {} class ContattoMetadata { [ScaffoldColumn(false)] public object IDContatto; [Required(ErrorMessage="Campo nome obbligatorio")] [StringLength(50, MinimumLength=5)] public object Nome; [RegularExpression(@"\b[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b", ErrorMessage="Inserisci una email valida")] public object Email; } } Nella classe ContattoMetadata sono esplicitati i campi corrispondenti ai dati dell’entità prodotto. Non è importante che siano property e che siano dello stesso tipo di dato definito nel modello. L’importante è che abbiano lo stesso nome delle proprietà della classe Prodotto in quanto svolgono il solo scopo di segnaposto per applicare gli attributi di validazione. La classe permette di concentrare in un unico posto le regole di validazione. Sono a disposizione numerosi attributi di validazione. DataTypeAttribute Consente di specificare il tipo di dato da associare ad un campo dell’entità. Server side um 158 di 243 EditableAttribute Specifica se il campo è editabile o readonly. RangeAttribute Consente di specificare un range di valori tra un minimo e un massimo. RegularExpressionAttribute Per specificare una regular expression che sarà valutata nel momento in cui il valore sarà impostato. RequiredAttribute Rende il campo obbligatorio. ScaffoldColumnAttribute Istruisce il run-time a non visualizzare il campo nell’interfaccia utente. StringLengthAttribute Per specificare un numero massimo di caratteri, accetta altri parametri come il numero minimo consentito. UIHintAttribute Consente di specificare un custom template da utilizzare per visualizzare il campo nell’interfaccia utente. Se gli attributi di validazione presenti non fossero sufficenti, è possibile creare attributi personalizzati creando una classe che derivi dalla classe base ValidationAttribute. Esempio, creare un attributo per la validazione del campo Prezzo. L’attributo creato è definito in una nuova classe in cui è presente una proprietà che consente di specificare un prezzo minimo e una proprietà booleana IsValid nella quale è possibile indicare le regole di validazione. Se il valore Prezzo è NULL non sarà dichiarato invalido consentendo così di non rendere obbligatorio questo valore. Per l’obbligatorietà dei dati si può utilizzare l’attributo Required. using System; using System.ComponentModel.DataAnnotations; namespace NegozioMVCApp.Models { public class PrezzoAttribute : ValidationAttribute { public double PrezzoMinimo { get; set; } public override bool IsValid(object value) { if (value == null) return true; var prezzo = Convert.ToDouble(value); if (prezzo < PrezzoMinimo) return false; // ...altre regole di validazione return true; } } } Usare l’attributo PrezzoAttribute nella classe ProdottoMetadata decorando il campo Server side um 159 di 243 Prezzo. using System.ComponentModel.DataAnnotations; namespace NegozioMVCApp.Models { [MetadataType(typeof(ProdottoMetatada))] public partial class Prodotto {} class ProdottoMetatada { [Prezzo(PrezzoMinimo = 5.50)] public object Prezzo; [Required] [StringLength(50, MinimumLength = 5] public object Nome; } } Anche ASP.NET MVC può utilizzare la libreria System.ComponentModel.DataAnnotations per decorare le classi del modello. È possibile definire che l’IDCategoria sia visualizzato in fase di editing con una drop down list presente in un apposito template. Per creare un template per visualizzare la proprietà IDCategoria, quando l’utente modifica i dati, si deve creare una cartella di nome EDITORTEMPLATES all’interno della cartella del controller Prodotti. Il path del template di nome ucCategoria utile per la proprietà IDCategoria utilizzato in fase di editing sarà: \VIEWS\PRODOTTI\EDITORTEMPLATES\UCCATEGORIA.ASPX. Mentre il template utilizzabile per la visualizzazione avrà il seguente path. \VIEWS\PRODOTTI\DISPLAYTEMPLATES\UCCATEGORIA.ASPX Se il template creato non è legato solo ai prodotti ma può essere utilizzato per visualizzare parti d’interfaccia utente comuni ad altri dati e controller allora si deve creare la sotto cartella EDITORTEMPLATES e DISPLAYTEMPLATES all’interno della cartella SHARED. \VIEWS\SHARED\EDITORTEMPLATES\UCCATEGORIA.ASPX Il contenuto del template ucCategoria per l’editing, dovrà visualizzare una lista drop down popolata con l’elenco delle categorie e il valore corrente della proprietà IDCategoria selezionato. Si può utilizzare l’HTML helper Html.DropDownList e passargli l’insieme di categorie come SelectList. <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <%= Html.DropDownList("", new SelectList((IEnumerable)ViewData["Categorie"], "IDCategoria","Descrizione", Model)) %> L’oggetto ViewData legge l’elenco che il controller Prodotti dovrà aver prima popolato. Nella view di editing dell’entità prodotto, per fare in modo che il run-time di ASP.NET MVC esamini i metadati e utilizzi le istruzioni che si sono definite, si può utilizzare un HTML helper che si chiama Html.EditorForModel. Richiamando questo metodo nella view sarà creato il markup HTML necessario per reindirizzare i controlli per ogni proprietà del modello. <% using (Html.BeginForm()) {%> <fieldset> <legend>Fields</legend> <%= Html.EditorForModel() %> <p> <input type="submit" value="Save" /> Server side um 160 di 243 </p> </fieldset> <% } %> Per visualizzare i dettagli con un template, usare il metodo Html.DisplayForModel. Grazie ai metadati associabili alle entità e ai template si può decidere come visualizzare i dati in un punto unico, non ripetendo codice e in maniera più ordinata. Per dichiarare nella classe dei metadati di utilizzare il template ucCategoria si usa l’attributo UIHint. [MetadataType(typeof(ProdottoMetatada))] public partial class Prodotto {} class ProdottoMetatada { [UIHint("ucCategoria")] public int IDCategoria { get; set; } } Server side um 161 di 243 APPLICAZIONI MULTILINGUA INTRODUZIONE Globalizzazione e Localizzazione sono due facce della stessa medaglia e costituiscono la base del processo d’internazionalizzazione delle applicazioni. Globalizzazione È il processo con cui si prepara l’applicazione a supportare diversi linguaggi. Localizzazione È il processo che si occupa della traduzione dell’applicazione nella lingua desiderata. Le risorse di un’applicazione che necessitano di una traduzione sono i seguenti. Immagini: alcune immagini di un’applicazione potrebbero contenere del testo che dovrà essere localizzato. Testi statici: anche label, descrizioni testuali statiche, tooltip devono essere tradotte nelle lingue supportate dall’applicazione. Testi da DB: le applicazioni fanno uso di contenuti provenienti da DB. Date, numeri: anche se queste informazioni non devono essere “tradotte”, andranno, comunque, visualizzate in modo diverso in base alla lingua di destinazione, per esempio il formato delle date: il 20 ottobre 2023 è scritto 20/10/2023 per la lingua italiana, 10/20/2023 per l’inglese americano. DB Server side um 162 di 243 Creare il DB e la tabella che conterrà le informazioni sui prodotti. Campi: nome, descrizione, prezzo e data disponibilità. Chiave della tabella: è composta dalla coppia idProdotto e lingua. Popolare la tabella. PROGETTO Una scheda legge i dettagli di un prodotto da una tabella di DB e li visualizza in una pagina web. Nel .NET Framework è presente il namespace System.Globalization destinato alla Server side um 163 di 243 localizzazione delle informazioni. La classe principale è CultureInfo che rappresenta una cultura specifica e ne definisce la visualizzazione di numeri, valute, date ed altro. La classe RegionInfo fornisce informazioni sul paese. Altre classi utili sono NumberFormatInfo per la formattazione dei numeri e DatetimeFormatInfo per la formattazione delle date. Culture Per specificare in modo univoco una cultura, è stato introdotto uno standard chiamato RFC (Request for Comments) 1766 che definisce codici univoci per ciascuna di esse. Questi sono formati da due parti separate da un (-). 1. La prima (in minuscolo) rappresenta la lingua. 2. La seconda (in maiuscolo) specifica il paese. Esempi. it-IT: italiano – Italia. en-GB: inglese – Regno Unito. en-US: inglese – Stati Uniti. File LANGUAGECONTROLLER.ASCX È uno “User Control” in cui inserire i pulsanti per cambiare la lingua attiva e il codice necessario per notificarne il cambiamento all’applicazione. Il controller non fa altro che scrivere un cookie con il nome della lingua selezionata e ricaricare la pagina. File GLOBAL.ASAX <%@ Application Language="VB" %> <%@ Import Namespace="System.Globalization" %> <%@ Import Namespace="System.Threading" %> <script runat="server"> Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs) Dim lang As CultureInfo If Request.Cookies("culture") Is Nothing Then Response.Cookies("culture").Value = "it-IT" End If Try lang = New CultureInfo(Request.Cookies("culture").Value) Catch lang = New CultureInfo("it-IT") End Try Thread.CurrentThread.CurrentCulture = lang Thread.CurrentThread.CurrentUICulture = lang End Sub </script> Il metodo Application_BeginRequest, legge il cookie che è stato scritto; crea un oggetto CultureInfo in base alla lingua selezionata e imposta la lingua del Thread corrente. È eseguito a ogni richiesta di pagina, questo consente di risparmiare codice in un sito che ha più pagine perchè non si deve inserire il codice per l’impostazione della lingua in tutte le pagine La riga seguente. lang = New CultureInfo(Request.Cookies("culture").Value) Crea l’oggetto CultureInfo in base alla lingua selezionata dall’utente. Server side um 164 di 243 Le righe seguenti. Thread.CurrentThread.CurrentCulture = lang Thread.CurrentThread.CurrentUICulture = lang Assegnano l’oggetto CultureInfo al Thread corrente. CurrentCulture specifica la cultura utilizzata per visualizzare informazioni e dati, è usata per l’ordinamento delle stringhe, la visualizzazione di date, numeri e valute. CurrentUICulture permette di specificare la cultura per l’interfaccia utente ed è utilizzata quando si ricercano le informazioni nei file di risorsa. File DEFAULT.ASPX Crea la pagina che visualizza i prodotti. È presente il controllo che permette di cambiare la lingua corrente. Gli altri componenti sono: un’immagine che conterrà il titolo della pagina, le label con le descrizioni e alcune TextBox che conterranno i dati letti dal DB. Per localizzare le immagini, creare la cartella IMAGES e, al suo interno, tante cartelle quante sono le lingue che si vogliono supportare, chiamandole con la sigla della lingua in questione. Nell’esempio sono quattro: it-IT, en-US, fr-FR e de-DE. A questo punto, creare le quattro immagini con il titolo nelle diverse lingue e salviarle nelle cartelle della lingua corrispondente con nome HEADER.GIF. File DEFAULT.ASPX.VB Dichiarare una variabile globale che rappresenta la lingua selezionata. Private lang As CultureInfo Creare la procedura seguente. Protected Sub Page_PreInit(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreInit lang = Thread.CurrentThread.CurrentCulture ' immagine img_titolo.ImageUrl = "images/" & lang.Name & "/header.gif" Legge il valore di CurrentCulture e assegna l’immagine corretta utilizzando la proprietà Name di lang. Per localizzare le etichette con le descrizioni e altri elementi della pagina nelle varie lingue si usano i file di risorsa. File RESOURCE.RESX È il file per la lingua di default, è composto da due colonne. 1. Nome: nomi delle risorse cui accedere poi da codice per localizzare i contenuti. 2. Valore: inserire il valore della risorsa. I file di risorsa sono dei file che utilizzano XML per l’accesso ai dati e possono contenere testi, immagini, icone e altri oggetti. Per le altre quattro lingue, chiamare il file RESOURCE.LINGUA.RESX, per esempio il francese si chiama RESOURCE.FR-FR.RESX. Per localizzare i testi delle etichette si usa la procedura Page_PreInit. Inserire le seguenti righe. ' etichette lbl_nome.Text = Resource.lbl_nome Server side um 165 di 243 lbl_descrizione.Text = Resource.lbl_descrizione lbl_prezzo.Text = Resource.lbl_prezzo lbl_disponibile_dal.Text = Resource.lbl_disponibile_dal ' titolo Pagina Page.Title = Resource.titolo_pagina End Sub Così facendo, i testi delle label e il Title della pagina saranno letti dinamicamente dai file di risorsa in base alla lingua selezionata. File PRODOTTO.VB Legge i contenuti dal DB. Creare la classe Prodotto per mappare i campi della tabella prodotti. La classe ha un costruttore overloaded che legge dal DB i dettagli del prodotto in base alla lingua e li salva nei campi privati accessibili tramite delle proprietà di sola lettura. File GLOBALIZER.VB Date e numeri hanno modalità di visualizzazione diversa in base alla lingua, basti pensare al separatore dei decimali che può essere il punto oppure la virgola. Per visualizzare correttamente questi ultimi, creare una classe Globalizer che contiene due metodi FormatData e FormatNumero. Server side um 166 di 243 Server side um 167 di 243 RILEVAZIONE DEL BROWSER INTRODUZIONE È possibile impostare il valore delle proprietà dei server control in base al browser utilizzato per la visualizzazione della pagina Esempio, inserire una Label il cui testo visualizzato sia diverso a seconda che l’utente stia utilizzando IExplorer o Firefox. Similmente, si può realizzare un pulsante il cui evento OnClientClick vari a seconda del browser utilizzato. <asp:Label ID="lblVariableLabel" runat="server" ie:Text="Testo per gli utenti che utilizzano Internet Explorer" mozilla:Text="Testo per gli utenti che utilizzano Firefox" Text="Testo per tutti gli altri utenti"> </asp:Label> <asp:Button ID="txtVariableButton" runat="server" ie:Text="Pulsante per utenti IE" ie:OnClientClick="javascript:alert('Internet Explorer');" mozilla:Text="Pulsante per utenti Firefox" mozilla:OnClientClick="javascript:alert('Mozilla Firefox');" Text="Pulsante per altri browser" OnClientClick="javascript:alert('Altri browser');"></asp:Button> Se nell’ambito di uno stesso controllo vi sono proprietà che presentano un prefisso queste sono valutate per prime e solo se il browser utilizzato dall’utente non rientra in nessuno di quelli per cui è stato previsto uno specifico valore della proprietà, allora è preso in considerazione il valore di default, senza prefisso. Non sono solo le proprietà “semplici” a poter essere gestite in maniera condizionale sulla base del browser utilizzato ma anche i controlli di tipo template, per esempio il controllo Menu. <asp:Menu runat="server" id="myVariableMenu" BackColor="#B5C7DE" DynamicHorizontalOffset="2" Font-Names="Verdana" Font-Size="0.8em" ForeColor="#284E98" StaticSubMenuIndent="10px"> <ie:Items> <asp:MenuItem Text="Voce 1 per IE" /> <asp:MenuItem Text="Voce 2 per IE" /> </ie:Items> <mozilla:Items> <asp:MenuItem Text="Voce 1 per Firefox" /> <asp:MenuItem Text="Voce 2 per Firefox" /> </mozilla:Items> <DynamicHoverStyle BackColor="#284E98" ForeColor="White" /> <DynamicMenuItemStyle HorizontalPadding="5px" VerticalPadding="2px" /> <DynamicMenuStyle BackColor="#B5C7DE" /> <DynamicSelectedStyle BackColor="#507CD1" /> <Items> <asp:MenuItem Text="Voce 1 per altri browser" /> <asp:MenuItem Text="Voce 2 per altri browser" /> </Items> Server side um 168 di 243 <StaticHoverStyle BackColor="#284E98" ForeColor="White" /> <StaticMenuItemStyle HorizontalPadding="5px" VerticalPadding="2px" /> <StaticSelectedStyle BackColor="#507CD1" /> </asp:Menu> Le voci di menu saranno diverse per ciascun browser considerato. Tutti i prefissi utilizzabili si possono trovare nella cartella seguente. C:\WINDOWS\MICROSOFT.NET\FRAMEWORK64\V4.0.30319\CONFIG\BROWSERS Dove sono memorizzati diversi file con estensione .BROWSER che contengono, per ciascun browser, l’ID che si può utilizzare per ciascuna specifica versione. File CHROME.BROWSER <browsers> <!-- Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/530.1 (KHTML, like Gecko) Chrome/2.0.168.0 Safari/530.1 --> <browser id="Chrome" parentID="WebKit"> <identification> <userAgent match="Chrome/(?'version'(?'major'\d+)(\.(?'minor'\d+)?)\w*)" /> </identification> <capabilities> <capability name="browser" value="Chrome" /> <capability name="majorversion" value="${major}" /> <capability name="minorversion" value="${minor}" /> <capability name="type" value="Chrome${major}" /> <capability name="version" value="${version}" /> <capability name="ecmascriptversion" value="3.0" /> <capability name="javascript" value="true" /> <capability name="javascriptversion" value="1.7" /> <capability name="w3cdomversion" value="1.0" /> <capability name="supportsAccesskeyAttribute" value="true" /> <capability name="tagwriter" value="System.Web.UI.HtmlTextWriter" /> <capability name="cookies" value="true" /> <capability name="frames" value="true" /> <capability name="javaapplets" value="true" /> <capability name="supportsCallback" value="true" /> <capability name="supportsDivNoWrap" value="false" /> <capability name="supportsFileUpload" value="true" /> <capability name="supportsMaintainScrollPositionOnPostback" value="true" /> <capability name="supportsMultilineTextBoxDisplay" value="true" /> <capability name="supportsXmlHttp" value="true" /> Server side um 169 di 243 <capability name="tables" </capabilities> </browser> </browsers> value="true" /> In alternativa, è possibile creare dei file .BROWSER personalizzati posizionandoli all’interno dell’applicazione nella cartella APP_BROWSERS e rispettando lo schema XML. Server side um 170 di 243 SEO (SEARCH ENGINE OPTIMIZATION) INTRODUZIONE È una tecnica volta a ottimizzare l’indicizzazione di un sito all’interno dei motori di ricerca. L’URL routing consente di rigirare le richieste su una pagina, a fronte di un URL più lungo. In applicazioni dal contenuto dinamico, è frequente l’uso di parametri inseriti in querystring per caricare informazioni e generare pagine per ogni specifica richiesta. Questo approccio non è il migliore da adottare, perché non contiene informazioni utili nell’URL Queste informazioni, come una descrizione breve, possono essere utili tanto ai motori di ricerca, quanto all’utente che, da un URL, può capire meglio il contenuto di un indirizzo. Per sfruttare l’URL routing si devono registrare gli URL nel file seguente. Gli URL sono costruiti nelle seguenti sezioni. NomeController / NomeAction / Parametro È possibile aggiungere regole di routing, ASP.NET quando riceve una richiesta analizza le regole nell’ordine con cui sono registrate. Prima le regole più specifiche e poi a seguire quelle più generiche fino a quelle di default. File GLOBAL.ASAX <%@ Application Language="C#" %> <%@ Import Namespace="System.Web.Routing" %> <script RunAt="server"> protected void Application_Start(object sender, EventArgs e) { RouteTable.Routes.Add("ProductsRoute", new Route("products/{ProductID}({ProductName}", new PageRouteHandler("~/products.aspx"))); ScriptManager.ScriptResourceMapping.AddDefinition("jquery", new ScriptResourceDefinition() { Path = "~/scripts/jquery-1.9.1.min.js", DebugPath = "~/scripts/jquery-1.9.1.min.js", CdnPath = "http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.min.js", CdnDebugPath = "http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.js" }); } </script> Quando l’utente fa clic su un pulsante per inviare i dati inseriti in un form oppure quando fa clic su un link che punta ad un certo indirizzo dell’applicazione ASP.NET MVC, la richiesta HTTP è analizzata lato server e sulla base della regola di routing presente in questo file il run-time individua il Controller giusto e la specifica action richiesta. Server side um 171 di 243 La route definita rende possibile che tutte le chiamate effettuate a URL del tipo Bicicletta con ID=15, siano in realtà rigirate alla pagina PRODUCTS.ASPX. File PRODUCTS.ASPX All’interno di questa pagina, è possibile accedere al valore di uno o più parametri con una sintassi particolare che si chiama expression builder. <%@ Page Language="C#" CodeFile="Products.aspx.cs" Inherits="Products" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Esempio di SEO</title> </head> <body> <form id="form1" runat="server"> <div> <h1><asp:Literal runat="server" Text="<%$ RouteValue: ProductName %>" /></h1> </div> </form> </body> </html> File PRODUCTS.ASPX.CS using System.Web.UI; public partial class Products : Page { } File URLROUTING.ASPX È possibile creare un link che sfrutti la route, con questa tecnica, al variare del percorso, in automatico, i link seguiranno la stessa strada, evitando di doverli definire manualmente. <%@ Page Language="C#" CodeFile="UrlRouting.cs" Inherits="UrlRouting" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Esempio di URL routing</title> </head> <body> <form id="form1" runat="server"> <div> <asp:HyperLink ID="HyperLink1" NavigateUrl="<%$ RouteUrl: RouteName=ProductsRoute,ProductID=15, Server side um 172 di 243 ProductName = Bicicletta-ASP.NET %>" runat="server">Bicicletta con ID=15</asp:HyperLink> </div> </form> </body> </html> File URLROUTING.CS using System.Web.UI; public partial class UrlRouting : Page { } Server side um 173 di 243 MODULO 6 ASP.NET MVC Introduzione ASP.NET MVC vs ASP.NET WebForms Server side um 174 di 243 INTRODUZIONE PAGINA Permette soluzioni migliori sia per l’architettura sia per la pulizia del codice ma richiede tempi di sviluppo più lunghi. È un framework per la creazione di applicazioni web, si può utilizzare come alternativa a Web Form per tre ragioni. 1. Necessità di controllare l’HTML per aderire ad uno standard web. 2. Migliore testabilità della logica applicativa che sta “dietro” alle pagine ASPX. 3. Migliore predisposizione all’indicizzazione nei motori di ricerca. Il pattern MVC è basato sulla separazione dei compiti fra i componenti S/W che compongono un’applicazione web e interpretano tre ruoli principali. 1. Model Sono i dati che Controller e View si scambiano, si può utilizzare un ORM (Object Relational Mapping) come Entity Framework o LinQ to SQL, meglio sarebbe applicare anche un pattern come il Repository per disaccoppiare la logica presente nel Controller dalla logica di persistenza dei dati. 2. View È l’interfaccia: le pagine ASPX che contengono il codice HTML necessario a visualizzare l’UI (User Interface) nel browser non usano più il code-behind perché i dati sono preparati dal Controller e resi disponibili pre-caricati alla View. 3. Controller È la business logic, per esempio Visual C#. RICHIESTE HTTP Quando nel browser si digita un indirizzo: http://127.0.0.1/contatti.aspx si aspetta che nella cartella presente sul server web vi sia memorizzato il file CONTATTI.ASPX che sarà richiamato per elaborare e visualizzare la risposta. In un’applicazione ASP.NET MVC invece è diverso, a ogni richiesta non corrisponde un file. Le richieste sono indirizzate ad un Controller e ad una specifica action contenuta nel Controller stesso. Per esempio, il seguente indirizzo http://127.0.0.1/Prodotti/Details/5 sarà passato alla classe Controller di nome ProdottiController al cui interno sarà la action Details a elaborare la richiesta. URL così formati possono contenere dei parametri, ASP.NET MVC oltre a passare la richiesta al controller cercherà un’action al suo interno con il nome Details con un Server side um 175 di 243 parametro compatibile con il valore presente nell’URL. Una volta trovata l’action, popolerà in automatico con il valore 5 il parametro dell’action Details. In pratica, mentre nella applicazioni web gli URL corrispondono a dei file ASPX, con ASP.NET MVC gli URL sono mappati a classi Controller che elaborano la richiesta, leggono i dati dal Model e reindirizzano il rendering della pagina su una determinata View. PROGETTO Per creare un nuovo progetto, fare clic su File/Nuovo/Progetto… (CTRL+N). Nella finestra di dialogo Nuovo Progetto selezionare Altri linguaggi/Visual C#/Web, quindi selezionare il tipo di applicazione, Applicazione Web ASP.NET MVC 4. Si apre la seguente finestra. Fare clic su OK. Server side um 176 di 243 ASP.NET MVC implementa il concetto che le configurazione: questo è visibile tra il meccanismo dell’associazione della View al Controller stesso. Infatti, un Controller è una classe che deriva dalla classe nome controller e offre dei metodi particolari, chiamati derivato da ActionResult. convenzioni vincono sulla di attivazione del Controller e Controller e che ha nel suffisso il action che restituiscono un tipo File CONTROLLERS\HOMECONTROLLER.CS using MyMvcApplication.Models; using System.Linq; using System.Web.Mvc; namespace MyMvcApplication.Controllers { public class HomeController : Controller { // GET: /Home/ public ActionResult Index() { using (var db = new StoreContext()) { var customers = db.Customers.OrderBy(f => f.Id).Take(5); return View(customers.ToList()); } } } } Questa action si richiama all’indirizzo seguente: /Home/Index. La convenzione indica che il Controller è stabilito dal primo pezzo dell’URL, mentre l’action dal secondo, Index è anche l’action di default, quindi può essere omessa. Anche ASP.NET MVC usa le route di ASP.NET che possono essere personalizzate agendo sul file seguente. File APP_START\ROUTECONFIG.CS using System.Web.Mvc; using System.Web.Routing; namespace MyMvcApplication { Server side um 177 di 243 public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } } } Per avere la View, per convenzione è ricercato il file che si trova nel percorso seguente. FileVIEW\HOME\INDEX.CSHTML Il nome del Controller e dell’action sono utilizzati per comporre dinamicamente il percorso nel quale andare a recuperare la View. Il ciclo mostra a video i dati prelevati dal DB, in pratica ASP.NET MVC non ha il concetto di data binding perché basta avere una View tipizzata ed estrarre le informazioni. Questa sintassi si chiama Razor ed è specifica di ASP.NET MVC. @model IEnumerable<MyMvcApplication.Models.Customer> <ul> @foreach (var item in Model) { <li>@item.Name</li> } </ul> @Html.ActionLink("Admin", "Index", "Admin") Poiché si è passato nel Controller un elenco di clienti, la View sarà tipizzata usando il Model. Server side um 178 di 243 Fare clic con il tasto destro sulla cartella View, dal menu contestuale selezionare Aggiungi/Visualizza…, è proposto in automatico di utilizzare i tipi che sono disponibili. RAZOR È un View engine per ASP.NET MVC pensato per generare HTML utilizzando un paradigma incentrato sul codice. La View di default che ASP.NET MVC utilizza è basato sui concetti di pagine, controlli e Master Page. Fa parte anche di WebMatrix, comprende una serie di template per costruire siti web e una serie di applicazioni web open source, fra cui WordPress, Joomla!, DotNetNuke e Orchard su cui personalizzare stili e contenuti per realizzare applicazioni web complesse. Nella modalità ASP.NET MVC è possibile utilizzare Razor come sintassi per progettare e gestire le View. L’obiettivo principale di Razor è minimizzare il numero di caratteri e parole chiave all’interno di una View per rendere molto veloce la creazione e la manutenzione del flusso di codice all’interno del codice di presentazione. Il parser, infatti, è in grado di distinguere i blocchi di codice da eseguire sul server rispetto alle parti di codice client HTML, stili e JavaScript, senza dover esplicitamente marcare queste aree con attributi e/o TAG particolari. Esempio, non è necessario marcare il codice della View con attributi particolari come ad esempio i <% %>. È possibile definire variabili e riutilizzarle all’interno del flusso della pagina, senza preoccuparsi di definire delle aree di codice server. Per definire variabili è sufficiente iniziare la riga con il simbolo della chiocciola. Server side um 179 di 243 Le variabili possono essere usate in espressioni inline senza TAG e attributi. <!—Istruzioni su una sola riga --> @{ var totale = 10000; } @{ var messaggio = "Ciao, mondo!"; } <h1>Ciao Razor</h1> <p>Questo è un esempio di sintassi Razor</p> <p>Il totale è : @totale </p> <p>Il messaggio che vogliamo darti è : @messaggio</p> Seguendo un paradigma simile a JavaScript o C# è possibile definire istruzioni multiriga. @{ var saluti = "Benvenuti sul nostro sito!"; var weekDay = DateTime.Now.DayOfWeek; var message = saluti + " Oggi è : " + weekDay; } <h1>Sito</h1> <p> @ message </p> Razor contiene molte classi helper per facilitare la costruzione di applicazioni che interagiscono con DB o servizi esterni. Queste classi sono nate per nascondere tutti i dettagli implementativi e di codice .NET dei servizi che utilizzino. ASP.NET MVC va bene anche per creare maschere d’inserimento dati. Tramite wizard è possibile, in fase di creazione di un Controller, far generare in automatico le action e le View necessarie. Selezionare il template che fa riferimento a Entity Framework tra le opzioni e poi specificare la classe di modello e quella con il context. File ADMINCONTROLLER.CS Si ha un Controller al cui interno c’è una action di nome Create decorata con un attributo che consente di utilizzarla solo con richieste POST. using System.Data; using System.Linq; using System.Web.Mvc; using MyMvcApplication.Models; namespace MyMvcApplication.Controllers { public class AdminController : Controller { Server side um 180 di 243 private StoreContext db = new StoreContext(); // GET: /Admin/ public ActionResult Index() { return View(db.Customers.ToList()); } // GET: /Admin/Details/5 public ActionResult Details(int id = 0) { Customer customer = db.Customers.Find(id); if (customer == null) return HttpNotFound(); return View(customer); } // GET: /Admin/Create public ActionResult Create() { return View(); } // POST: /Admin/Create [HttpPost] [ValidateAntiForgeryToken] public ActionResult Create(Customer customer) { if (ModelState.IsValid) { db.Customers.Add(customer); db.SaveChanges(); return RedirectToAction("Index"); } return View(customer); } // GET: /Admin/Edit/5 public ActionResult Edit(int id = 0) { Customer customer = db.Customers.Find(id); if (customer == null) return HttpNotFound(); return View(customer); } // POST: /Admin/Edit/5 [HttpPost] [ValidateAntiForgeryToken] public ActionResult Edit(Customer customer) { if (ModelState.IsValid) { db.Entry(customer).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } return View(customer); } // GET: /Admin/Delete/5 public ActionResult Delete(int id = 0) { Customer customer = db.Customers.Find(id); if (customer == null) return HttpNotFound(); return View(customer); } // POST: /Admin/Delete/5 [HttpPost, ActionName("Delete")] [ValidateAntiForgeryToken] public ActionResult DeleteConfirmed(int id) Server side um 181 di 243 { Customer customer = db.Customers.Find(id); db.Customers.Remove(customer); db.SaveChanges(); return RedirectToAction("Index"); } protected override void Dispose(bool disposing) { db.Dispose(); base.Dispose(disposing); } } } Il metodo ActionResult Edit rappresenta la chiamata che è fatta per visualizzare il form d’inserimento dati, al cui invio sarà invocata l’action che risponde solo al metodo POST e che, infatti, è decorata con l’attributo HttpPost. In questo metodo sono recuperati i dati, modificati e salvati con Entity Framework: se tutto va bene saranno inviati all’action di default che visualizza l’elenco delle informazioni. Per reindirizzare un form dentro una View occorre il codice seguente. L’ActionResult Create senza parametri è invocata inizialmente con una richiesta HTTP GET e restituirà una View di nome con il modello dati vuoto. Si riempie il form d’inserimento e si preme sul pulsante. Il pulsante provoca una richiesta HTTP POST al cui interno sono presenti i dati inseriti nel form. In questo caso, il run-time individua la seconda action come quella su cui indirizzare la richiesta e utilizza un Model Binder per popolare automaticamente il parametro con i dati inseriti nel form. Il Model Binder di ASP.NET MVC fa il lavoro di analizzare la richiesta e, sulla base dei valori presenti, crea un oggetto popolandolo con i dati. File VIEWS\ADMIN\EDIT.CSHTML @model MyMvcApplication.Models.Customer @{ ViewBag.Title = "Edit"; } <h2>Edit</h2> @using (Html.BeginForm()) { @Html.AntiForgeryToken() @Html.ValidationSummary(true) <fieldset> <legend>Customer</legend> @Html.HiddenFor(model => model.Id) <div class="editor-label"> @Html.LabelFor(model => model.Name) </div> <div class="editor-field"> @Html.EditorFor(model => model.Name) @Html.ValidationMessageFor(model => model.Name) </div> <div class="editor-label"> @Html.LabelFor(model => model.City) </div> <div class="editor-field"> @Html.EditorFor(model => model.City) @Html.ValidationMessageFor(model => model.City) </div> <div class="editor-label"> Server side um 182 di 243 @Html.LabelFor(model => model.Country) </div> <div class="editor-field"> @Html.EditorFor(model => model.Country) @Html.ValidationMessageFor(model => model.Country) </div> <p> <input type="submit" value="Save" /> </p> </fieldset> } <div> @Html.ActionLink("Back to List", "Index") </div> @section Scripts { @Scripts.Render("~/bundles/jqueryval") } L’uso degli helper LabelFor, EditorFor e ValidationMessageFor che in automatico andando a lavorare con i tipi delle proprietà del modello, danno rispettivamente il titolo, un editor per il tipo che tiene conto di come è fatto e un messaggio di validazione. Queste funzionalità lavorano con le data annotations che sono un modo per annotare e arricchire le classi con le informazioni aggiuntive, come la tipologia di campo, se è obbligatorio, oltre che con il modello stesso che in questo caso è basato su Entity Framework e può avere attributi che includono queste informazioni. APPLICAZIONI PER DISPOSITIVI MOBILI Se si sceglie questo progetto, i fogli di stile predefiniti sfruttano le media query, così che il layout si adatti a diverse risoluzioni del browser e sia correttamente visualizzabile anche se l’accesso avviene da uno smartphone. ASP.NET MVC pone grande attenzione nello sviluppo di applicazioni web mobile, tra i vari template presenti, si trova un’applicazione che sfrutta jQuery Mobile e mostra come integrare questo framework JavaScript all’interno delle View. Questa funzionalità si chiama Display Mode e consente di associare un particolare suffisso ad alcune caratteristiche della request per creare delle View ad hoc. Se, per esempio, si vogliono produrre delle View specifiche per Windows Phone, basterà registrare un nuovo display mode allo startup dell’applicazione, specificando un particolare suffisso, in questo caso "wp". DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("wp") { ContextCondition = x => x.GetOverriddenUserAgent().IndexOf("windows phone", StringComparison.InvariantCultureIgnoreCase) > -1 }); A questo punto, tutte le volte che si visita il sito web da un device Windows Phone, sarà cercata una View con estensione .WP.CSHTML e, se non trovata, il sistema effettuerà automaticamente il fallback sulla View di default. Server side um 183 di 243 ASP.NET MVC VS ASP.NET WEBFORM INTRODUZIONE Con un’applicazione WebForm si hanno una o più pagine ASPX che rispondono alle richieste HTTP. Con ASP.NET MVC si hanno uno o più Controller che rispondono alle richieste HTTP. All’interno dei Controller le action svolgono il ruolo dei gestori di evento nella pagine di code-behind delle WebForm. Con ASP.NET MVC si organizza il codice in maniera più ordinata. Sviluppando un sito web utilizzando le WebForm, si mettono nel code-behind tanti gestori di eventi con l’inevitabile logica di controllo legata al codice ASPX di non facile gestione. Il meccanismo dei PostBack e del ViewState finisce per pesare sulle performance. Con le WebForm si ha a disposizione una serie di eventi che formano il ciclo di vita della pagina, i PostBack per interagire con l’utente e il ViewState per mantenere lo stato dei controlli tra un PostBack e l’altro. ASP.NET MVC, invece, non utilizza il ViewState, la richiesta HTTP ha un ciclo di vita differente e non necessita di una programmazione orientata agli eventi e ai PostBack dei controlli. In una applicazione ASP.NET MVC non sono più utilizzati i controlli data bound cui i programmatori di applicazioni WebForm si sono abituati e che forniscono una ricca serie di funzionalità già implementate. Si deve scrivere gran parte del codice a mano o utilizzando gli helper HTML. Questi sono metodi che semplificano la scrittura del codice HTML ma che non hanno la stessa facilità d’uso dei controlli visuali normalmente utilizzati nelle WebForm. In sintesi, con ASP.NET MVC si deve scrivere molto di più sin dall’inizio e controllare bene il flusso di esecuzione del codice. Con WebForm si deve scrivere meno codice senza però avere lo stesso controllo sull’HTML e con un degrado di performance dovuto ai PostBack e al relativo ViewState. Server side um 184 di 243 MODULO 7 ASP.NET AVANZATO Accesso Dati Accesso al DB Programmazione client side CAPCTHA Server side um 185 di 243 ACCESSO INTRODUZIONE L’autenticazione degli utenti all’interno di un’applicazione web è un’operazione delicata ma, soprattutto, ripetitiva. Delicata perché ogni qualvolta si permette ad un utente d’inserire informazioni all’interno del sito, si apre una “porta” verso il server, basti pensare a tutti i casi di SQL Injection. Ripetitiva perché è un’operazione che coinvolge sempre le stesse risorse, ad esempio un DB e il codice per autenticare l’utente che vanno ripetute tra le pagine dei vari siti. ASP.NET mette a disposizione del programmatore un potente meccanismo per gestire l’autenticazione e l’autorizzazione per l’accesso alle risorse. Il primo passo per l’attuazione di politiche di sicurezza su un sistema informatico è sempre costituito dall’implementazione di un sistema di gestione delle credenziali di autenticazione. In pratica, l’accoppiata Username/Password rappresenta il primo passo per concedere l’utilizzo di determinate risorse in un applicativo. ChangePassword Permette ad un utente di cambiare la propria password. CreateUserWizard Raccoglie le informazioni di registrazione di un utente. Login Raccoglie le informazioni dell’utente e avvia il processo di autenticazione. LoginName Riporta automaticamente il nome dell’utente che si è autenticato. LoginStatus Indica lo stato di autenticazione dell’utente; visualizza un link per effettuare il Login quando l’utente è ancora anonimo e uno per il Logout quando l’utente è autenticato. LoginView Definisce una zona dove inserire viste basate sullo stato di autenticazione dell’utente; in queste viste è possibile inserire i controlli e le informazioni da far visualizzare a seconda dello stato di autenticazione dell’utente. Server side um 186 di 243 PasswordRecovery Recupera la password smarrita da un utente o ne genera una nuova; il controllo è in grado d’inviare un’email all’utente con i dati della password previa impostazione del server di posta SMTP. L’autenticazione via form invia al server i dati di autenticazione e garantisce l’accesso solo a chi è in possesso delle giuste credenziali. Il primo passo sarà creare una pagina HTML di login, in modo che sia possibile collegarsi ad un DB e confrontare i dati inseriti dall’utente con quelli presenti nel DB. Poiché il web è stateless, per tenere traccia dello stato dell’autenticazione ASP.NET utilizza un cookie. Le informazioni contenute nel cookie possono essere criptate, attraverso un’opzione, in modo che non sia possibile risalirne al contenuto. Questo sistema non funzionerebbe nel caso in cui il browser non supporti i cookie ma la stessa identica limitazione si avrebbe utilizzando un oggetto Session. AUTENTICAZIONE NON È AUTORIZZAZIONE Per costruire applicazioni che abbiano aree protette è necessario comprendere a fondo entrambi i concetti, dato che sono strettamente legati tra di loro. 1. Autenticazione: è la parte dedicata al riconoscimento dell’utente e ricade sotto le responsabilità dei form di autenticazione. 2. Autorizzazione: è gestita da ASP.NET, consente o meno l’accesso ad una risorsa utilizzando le credenziali fornite in fase di autenticazione e confrontandole con una lista di permessi; gli utenti autenticati potranno accedere a quella parte di risorsa che i permessi ad essa associati gli concedono, ad esempio un certo utente avrà il permesso di accedere ad una parte del disco ma non ad un'altra. Esempio, progettare una pagina di login. Una volta costruite le tabelle, si deve creare la maschera d’input. Dalla Casella degli strumenti, selezionare il controllo CreateUserWizard. AUTORIZZAZIONE DA WEB.CONFIG È necessario modificare il file WEB.CONFIG che essendo il sistema demandato alle configurazioni dell’applicazione, andrà a contenere le politiche di accesso alle risorse. Server side um 187 di 243 Esempio, garantire a PAGINA.ASPX l’accesso solo agli utenti autenticati. <?xml version="1.0" encoding="utf-8"?> <!-Per ulteriori informazioni sulla configurazione dell'applicazione ASP.NET, visitare http://go.microsoft.com/fwlink/?LinkId=169433 --> <configuration> <location path="pagina.aspx"> <system.web> <compilation debug="true" targetFramework="4.0" /> <authorization> <deny users="?" /> </authorization> </system.web> </location> </configuration> L'effetto è che la pagina con il nome specificato non potrà essere visitata, chiave deny, dagli utenti (?) che corrispondono a tutti quelli che non hanno fatto l’autenticazione. Se invece, si volesse garantire l’accesso a tutti gli utenti autenticati basta specificare come valore (*). È possibile anche specificare una lista di utenti da bloccare, inserendo lo username. Nel caso in cui si volesse specificare una lista di utenti cui è consentito l'accesso, è necessario sostituire deny con allow. Per quanto riguarda i ruoli il discorso è esattamente lo stesso. Esempio, il ruolo amministratore ha accesso e quello utente è bloccato. <location path="admin.aspx"> <system.web> <authorization> <deny roles="user" /> <allow roles="admin" /> </authorization> </system.web> </location> Ovviamente è possibile combinare sia politiche di accesso per ruolo sia per utente. L’attributo path del TAG <location> rappresenta il percorso relativo, primo (/) escluso, rispetto alla root dell’applicazione in cui gira l’autenticazione. Si può anche specificare una cartella e in questo modo la protezione è estesa a tutte le risorse ASP.NET che contiene. Server side um 188 di 243 DATI INTRODUZIONE I dati di un’applicazione web sono recuperati dai contesti più disparati che possono essere file di testo, documenti XML, un WS o un RDBMS. ASP.NET usa il data binding, associazione di dati, per recuperarli e presentarli all’interno delle applicazioni web con un’architettura che permetta di astrarre dal tipo di fonte dati. Il data binding è l’associazione di una fonte dati ad un controllo, garantita dalla possibilità della pagina di distinguere tra creazione e caricamento dei dati nei controlli. Senza i controlli per visualizzare i dati, il data binding sarebbe inutile, infatti permettono di associare i dati e visualizzarli nella pagina, sfruttando dei template per ripetere gli stessi. I controlli che supportano la visualizzazione dei dati sono chiamati Data Bound Control. I controlli principali sono tre. 1. Repeater: ripete i dati contenuti nel template. 2. DataList: offre funzionalità più avanzate rispetto al Repeater, lasciando comunque una certa libertà di personalizzazione, produce output di solo testo o in formato XML. 3. DataGrid: offre funzionalità di ordinamento, paginazione, modifica e produce una tabella HTML. Ciò che hanno in comune è la proprietà DataSource che serve per specificare la sorgente dei dati, un metodo DataBind per associare questi dati e tre eventi. 1. ItemCreated: scatenato all’aggiunta di ogni singolo item al controllo. 2. ItemDataBound: scatenato quando è effettuata l’associazione dei dati su ogni singolo item. 3. ItemCommand: scatenato quando un controllo contenuto in un item richiede l’esecuzione di un comando, in genere associato alle funzionalità di modifica o cancellazione. Non bisogna, però, confondere il data binding con l’uso della proprietà DataSource che i controlli offrono. Server side um 189 di 243 Questa proprietà accetta un oggetto di tipo IList, IEnumerable o ICollection e, ovviamente, qualsiasi tipo che implementi una di queste tre interfacce e, quindi, si presta a garantire la possibilità di essere utilizzata con qualsiasi fonte dati, non esclusivamente un DB. Dopo aver associato la sorgente, dietro le quinte si eseguono le seguenti operazioni. 1. È valutato il contenuto della proprietà DataSource. 2. Se ci sono elementi all’interno della sorgente, è effettuato un ciclo che visualizza, a seconda della logica che implementa il controllo cui la sorgente è associata. Questo meccanismo è trasparente al programmatore che non deve fare altro che richiamare il metodo DataBind sul controllo cui è associata la sorgente. Questo metodo è presente direttamente sulla classe Control del namespace System.Web.UI da cui tutti i controlli derivano. LIST CONTROL Sono controlli speciali, per esempio DropDownList o RadioButton che, pur appartenendo ai data bound control hanno un layout prefissato. File LISTCONTROL.ASPX <%@ Page Language="C#" CodeFile="05-ListControl.aspx.cs" Inherits="ListControl" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Esempio di data binding</title> </head> <body> <form id="form1" runat="server"> <div> <asp:DropDownList ID="DropDownList1" DataTextField="CompanyName" DataTextFormatString="- {0}"DataValueField="CustomerID" runat="server" /> </div> </form> </body> </html> L’uso delle proprietà DataTextField e DataTextFormatString indicano rispettivamente la proprietà da utilizzare per visualizzare e per formattare il dato, mentre DataValueField indica qual è il valore da associare al controllo. File LISTCONTROL.ASPX.CS using System; using System.Web.UI; using NorthwindModel; using System.Linq; public partial class ListControl : Page { protected void Page_Load(object sender, EventArgs e) { using (var entities = new NorthwindEntities()) { var customers = entities.Customers.OrderBy(c => c.CompanyName); DropDownList1.DataSource = customers; DropDownList1.DataBind(); } } } In questo codice si vede come sia assegnata, attraverso la proprietà DataSource, la Server side um 190 di 243 sorgente che è prelevata grazie a Entity Framework e salvata in una collezione locale e come questa sia effettivamente interrogata all’invocare del metodo DataBind. TEMPLATE Per personalizzare l’output associato ad un controllo, si usano i template. In realtà c'è un Data Control, tra tutti, in grado di estrarre i dati senza specificare alcun template ed è il DataGrid. Per tutti gli altri, invece, è necessario specificare almeno un template e in modo specifico almeno l’ItemTemplate. Un template non è altro che un controllo che al proprio interno può contenerne di altri. I seguenti controlli hanno il supporto per i template. Repeater Dato un template, lo ripete per tutti gli elementi della sorgente dati, in pratica si usa quando si vogliono visualizzare dati. GridView Visualizza in una tabella la sorgente dati, permettendone la paginazione, l’ordinamento e la modifica delle righe. DetailsView Visualizza in una tabella una singola riga recuperata dalla sorgente dati, permettendone l’inserimento e la modifica. FormView Simile al precedente, permette un layout personalizzato per la rappresentazione dei dati. ListView Consente la visualizzazione con un template personalizzato. La scelta di questi controlli stabilisce le modalità di visualizzazione, inserimento e modifica che si desidera utilizzare all’interno della pagina. I template sono gestiti con una classe di tipo ITemplate, un’interfaccia particolare che è implementata da una classe generata al volo con il page parser. Generalmente, questi controlli sono dotati di template specifici per i diversi stati. Per esempio, ItemTemplate è il template per il singolo elemento, mentre invece HeaderTemplate rappresenta l’intestazione. Per comporre il template e posizionare il contenuto della sorgente dati nei template, si deve usare l’istruzione seguente. <%# %> %> È una direttiva che il page parser riconosce e che fa sì che ciò che è contenuto sia invocato insieme all’evento DataBinding che si verifica quando il controllo scatena il data binding. Questi controlli offrono accesso ai dati attraverso l’interfaccia IDataItemContainer che si usa grazie alla proprietà Container del template. File REPEATER.ASPX Questo codice rappresenta a video un elenco di elementi. Dato che non è specificato diversamente, è invocato il metodo ToString che, in questo caso, produce la visualizzazione del nome della classe. Server side um 191 di 243 <%@ Page Language="C#" CodeFile="Repeater.aspx.cs" Inherits="Repeater" %> <%@ Import Namespace="NorthwindModel" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Esempio di Repeater</title> </head> <body> <form id="form1" runat="server"> <div> <asp:Repeater id="CustomerView" runat="server"> <ItemTemplate> <%#((Customer)Container.DataItem).CompanyName%> <br /> </ItemTemplate> </asp:Repeater> </div> </form> </body> </html> File REPEATER.ASPX.CS using System; using System.Web.UI; using System.Linq; using NorthwindModel; public partial class _06_Repeater: Page { protected void Page_Load(object sender, EventArgs e) { using (var entities = new NorthwindEntities()) { var customers = entities.Customers.OrderBy(c => c.CompanyName); CustomerView.DataSource = customers; CustomerView.DataBind(); } } } Per accedere alle proprietà, bisogna specificare il tipo messo in binding, senza fare il casting, la proprietà ItemType indica il tipo da utilizzare nei template di binding. La proprietà SelectMethod consente d’indicare il metodo da richiamare per popolare la griglia. Deve restituire qualcosa che implementi l’interfaccia IEnumerable o IQueryable, dove T è il tipo specificato dalla proprietà ItemType. File DATABINDING.ASPX <%@ Page Language="C#" CodeFile="DataBinding.cs" Inherits="DataBindingaspx" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Esempio di Repeater</title> </head> <body> <form id="form1" runat="server"> <div> <asp:Repeater ID="Repeater1" runat="server" Server side um 192 di 243 ItemType="NorthwindModel.Customer" SelectMethod="GetCustomers"> <ItemTemplate> <%#Item.CompanyName%><br /> </ItemTemplate> </asp:Repeater> </div> </form> </body> </html> File DATABINDING.ASPX.CS Cambiando il metodo GetCustomers, con l’aggiunta di un parametro cui si è aggiunto un attributo, si è in grado, in automatico, di far caricare quella proprietà attraverso la query string, leggendo dal parametro n. In questo caso si è usato la query string come sorgente ma ne sono supportate anche altre, come controlli in pagina, form e session. using System; using System.Web.UI; using System.Web.ModelBinding; using NorthwindModel; using System.Linq; public partial class DataBindingaspx : Page { NorthwindEntities db = new NorthwindEntities(); public IQueryable<Customer> GetCustomers([QueryString("n")] string name) { var customers = db.Customers.AsQueryable(); if (!String.IsNullOrEmpty(name)) customers = customers.Where(f => f.CompanyName.Contains(name)); return customers; } public override void Dispose(){ if (db !=null) db.Dispose(); base.Dispose(); } } File DATABINDING.ASPX <%@ Page Language="C#" CodeFile="DataBinding.cs" Inherits="DataBindingaspx" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>Esempio di GridView</title> </head> <body> <form id="form1" runat="server"> <div> <asp:GridView ID="GridView1" runat="server" SelectMethod="GetCustomers" UpdateMethod="UpdateCustomers" ItemType="NorthwindModel.Customer"> </asp:GridView> </div> Server side um 193 di 243 </form> </body> </html> File DATABINDING.ASPX.CS using System; using System.Web.UI; using System.Web.ModelBinding; using NorthwindModel; using System.Linq; public partial class DataBindingaspx : Page { NorthwindEntities db = new NorthwindEntities(); public IQueryable<Customer> GetCustomers([QueryString("n")] string name) { var customers = db.Customers.AsQueryable(); if (!String.IsNullOrEmpty(name)) customers = customers.Where(f => f.CompanyName.Contains(name)); return customers; } protected void UpdateCustomer(Customer c) { TryUpdateModel<Customer>(c); db.Attach(c); db.SaveChanges(); } public override void Dispose() { if (db != null) db.Dispose(); base.Dispose(); } } Grazie al fatto che i metodi restituiscono un tipo IQueryable<T>, il risultato ottenuto è che il controllo ricostruirà i criteri di ricerca sotto forma di lambda, così che Entity Framework possa produrre l’esatto risultato. L’uso dell’attributo all’interno dei parametri supportati dal metodo si chiama model binding, in pratica, s’indica che la sorgente dati è in querystring. Il risultato è che se si passa alla pagina un valore specifico, per esempio tabella.aspx?n=Andrea, la proprietà sarà valorizzata. È possibile recuperare questi valori dalle sorgenti più diverse, per esempio form, cookie o session. Lo stesso approccio è possibile in fase di aggiornamento o inserimento dei dati. Si riceveranno in automatico, i dati caricati all’interno del tipo specificato per la tabella: ciò vuol dire che non ci si deve preoccupare di conversioni ma solamente aggiornare i dati. Quindi, si dovrà fare l’attach al contesto dell’entità e modificarne i dati. In conclusione, sono ridotte le possibilità di errori, poiché non ci sono stringhe ma codice compilato e strongly typed. Infine, per quanto riguarda il data binding, è stato aggiunto il supporto all’HTMLEncode direttamente in fase di binding, con questa istruzione. <%#: Item.CustomerName %> Così facendo, si garantisce che sia applicato in maniera specifica l’encoding, evitando problemi di XSS (Cross Site Scripting) con dati prelevati dal DB. Server side um 194 di 243 ACCESSO AL DB RICHIESTA DI DATI I passi da seguire con un linguaggio server side sono i seguenti. 1. Dichiarazione, inizializzazione e utilizzo di un oggetto Connection. 2. Dichiarazione, inizializzazione e utilizzo di un oggetto DataAdapter. 3. Dichiarazione e riempimento di un DataSet. 4. Utilizzo dell’evento Load della pagina per impostare il DataSource della griglia in modo da visualizzare i dati. In totale circa una trentina di istruzioni da scrivere all’interno del codice ma soprattutto da riscrivere nel caso in cui un’altra pagina avesse dovuto effettuare un’altra query al DB. Il controllo SqlDataSource, invece, contiene due proprietà. 1. ConnectionString per specificare la stringa di connessione verso un DB SQL Server, ricavata direttamente dal file WEB.CONFIG. 2. SelectCommand per specificare l’istruzione SQL da eseguire per ricavare i dati dal DB. Grazie a questo controllo, la GridView può visualizzare il risultato della query senza dover scrivere alcun altro tipo d’informazioni. Sarà il controllo stesso ad aprire e chiudere la connessione, creare un oggetto DataAdapter, eseguire la query e restituire i dati. <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>" SelectCommand="SELECT * FROM [Category Sales for 1997]"> </asp:SqlDataSource> <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="True" DataSourceID="SqlDataSource1"> </asp:GridView> INTRODUZIONE Per creare un nuovo progetto, fare clic su File/Nuovo/Progetto… (CTRL+N). Nella finestra di dialogo Nuovo Progetto selezionare Altri linguaggi/Visual C#/Web, quindi selezionare il tipo di applicazione, Applicazione Web ASP.NET vuota. Immettere il nome del progetto nel campo Nome: per esempio WebForm, quindi per creare il progetto, premere OK. Invece, manualmente nella cartella App_Data fare clic con il tasto destro Aggiungi/Elemento esistente… per inserire il DB, DATI.MDB. Fare clic destro in Esplora soluzioni sul nome del progetto, dal menu contestuale selezionare Aggiungi/Nuovo elemento… (CTRL+MAIUSC+A). Alla voce Visual C# selezionare Web/Web Form. Server side um 195 di 243 VISUALIZZARE I DATI DELLA TABELLA ELENCO DEL DB DATI Doppio clic sul file DEFAULT.ASPX, selezionare Visualizza finestra di progettazione si apre l'editor visuale della pagina web in modo Progettazione. Dopo aver scritto: Visualizzazione dati, selezionare il controllo GridView ed inserirlo nella pagina. Selezionare un’origine dati: <Nuova origine dati…> Server side um 196 di 243 Nella finestra selezionare un tipo di origine dei dati. Nella finestra selezionare il DB. Server side um 197 di 243 Nella finestra configurare l’istruzione SQL Select. Nella finestra si può eseguire il test per visualizzare un’anteprima dei dati. Server side um 198 di 243 Selezionare Visualizza finestra di progettazione si apre l’editor visuale della pagina web in modo Progettazione. Selezionare il modo Origine per visualizzare il codice HTML. <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits=" primodb._Default._default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Visualizzazione dei dati della tabella elenco del DB dati</title> </head> <body> <form id="form1" runat="server"> <h1>Visualizzazione dati</h1> <p> </p> <p> <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataSourceID="AccessDataSource1"> <Columns> <asp:BoundField DataField="citta" HeaderText="citta" SortExpression="citta" /> <asp:BoundField DataField="CAP" HeaderText="CAP" SortExpression="CAP" /> </Columns> </asp:GridView> <asp:AccessDataSource ID="AccessDataSource1" runat="server" DataFile="~/App_Data/dati.mdb" SelectCommand="SELECT [citta], [CAP] FROM [elenco]"></asp:AccessDataSource> </p> <p> </p> </form> </body> </html> Server side um 199 di 243 Esecuzione dell’applicazione. Esecuzione dell’applicazione nel server web IIS. Copiare il file DEFAULT.ASPX nella cartella del server web C:\INETPUB\WWWROOT. Copiare le cartelle BIN e APP_DATA nella cartella del server web. C:\INETPUB\WWWROOT\BIN C:\INETPUB\WWWROOT\APP_DATA. Server side um 200 di 243 Modifiche Selezionare le righe e ordinarle nel controllo GridView. Selezionare il modo Origine per visualizzare il codice HTML. <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="primodb._default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Visualizzazione dei dati della tabella elenco del DB dati</title> </head> <body> <form id="form1" runat="server"> <h1>Visualizzazione dati</h1> <p> </p> <p> <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataSourceID="AccessDataSource1" AllowSorting="True"> <Columns> <asp:BoundField DataField="citta" HeaderText="citta" SortExpression="citta" /> <asp:BoundField DataField="CAP" HeaderText="CAP" SortExpression="CAP" /> </Columns> </asp:GridView> <asp:AccessDataSource ID="AccessDataSource1" runat="server" DataFile="~/App_Data/dati.mdb" SelectCommand="SELECT [citta], [CAP] FROM [elenco]"></asp:AccessDataSource> </p> <p> </p> Server side um 201 di 243 </form> </body> </html> Esecuzione dell’applicazione. Formattazione automatica. Server side um 202 di 243 Esecuzione dell’applicazione. RICERCARE DATI DELLA TABELLA ELENCO DEL DB DATI Configurare le impostazioni dell’origine dati, si seleziona lo stesso DB. Fare clic sul pulsante WHERE… Nella finestra si può eseguire il test per visualizzare un’anteprima dei dati. Server side um 203 di 243 Questo file RICERCADBASP.HTM è usato per l’inserimento della città da ricercare, impostarlo con il menu Progetto/Imposta come pagina iniziale. <html> <head> <title>Ricerca dati nella tabella elenco del DB dati</title> </head> <body> <h1>Ricerca dati</h1> <form method="get" action="Default.aspx"> <table> <tr><td>Città</td> <td><input type=text name="citta"></td></tr> <tr><td>CAP</td> <td><input type=text name="CAP"></td></tr> </table> <input type="submit" value ="Ricerca nel DB"/> </body> </html> Server side um 204 di 243 Configurare le impostazioni dell’origine dati, si seleziona lo stesso DB. Fare clic sul pulsante WHERE… Selezionare il modo Origine per visualizzare il codice HTML. <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="primodb._default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title> Ricerca dati nella tabella elenco del DB dati</title> </head> <body> <form id="form1" runat="server"> <h1>Visualizzazione dati</h1> <p> </p> <p> <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataSourceID="AccessDataSource1"> <Columns> <asp:BoundField DataField="citta" HeaderText="citta" SortExpression="citta" /> <asp:BoundField DataField="CAP" HeaderText="CAP" SortExpression="CAP" /> </Columns> </asp:GridView> <asp:AccessDataSource ID="AccessDataSource1" runat="server" DataFile="~/App_Data/dati.mdb" SelectCommand="SELECT [citta], [CAP] FROM [elenco] WHERE ([citta] = ?)"> <SelectParameters> <asp:QueryStringParameter Name="citta" QueryStringField="citta" Type="String"/> Server side um 205 di 243 </SelectParameters> </asp:AccessDataSource> </p> <p> </p> </form> </body> </html> Esecuzione dell’applicazione. Server side um 206 di 243 PROGETTO Gestione di una videoteca. 1. Identificazione del sistema Il progetto può essere scomposto in due parti. 2. ANALISI DEI DATI Le entità del progetto sono le seguenti. clienti. film. Tra l’entità clienti e l’entità film esiste un’associazione 1:N, in quanto un cliente possiede uno o più DVD e un DVD è posseduto da un cliente. Gli attributi di clienti sono: tessera, cognome, indirizzo, username, password. Gli attributi di film sono: codice, tessera, titolo, genere, noleggiato, data_noleggio. 3. Costruzione del modello Modello E/R Modello logico clienti (tessera, cognome, indirizzo, username, password) Tabelle clienti Campo tessera cognome indirizzo username password Chiave Primaria Formato Testo Testo Testo Testo Testo Dimensione 4 20 20 10 10 film (codice, tessera, titolo, genere, noleggiato, data_noleggio) Tabelle film Campo codice tessera titolo genere noleggiato data_noleggio Chiave Primaria Esterna Formato Testo Testo Testo Testo Booleano Data Dimensione 4 4 20 20 Creazione tabelle CREATE TABLE clienti ( tessera CHAR(4), cognome CHAR (20), indirizzo CHAR(20), username CHAR(10), password CHAR (10), Server side um 207 di 243 PRIMARY KEY(tessera) ); CREATE TABLE film ( codice CHAR(4), tessera CHAR (4), titolo CHAR(20), genere CHAR(20), noleggiato YESNO, data_noleggio DATE, PRIMARY KEY(codice), FOREIGN KEY(tessera) REFERENCES clienti (tessera) ); Server side um 208 di 243 Visualizzare i contenuti della tabella film in ordine di codice. Effettuare il login alla videoteca. Impostare la proprietà TextMode della casella di testo a password, affinchè al momento dell’inserimento non sia visibile la stringa inserita. Progettare il medesimo codice con query parametriche con variabili. Server side um 209 di 243 Inserire un nuovo cliente della videoteca. L’uso di parole riservate, per esempio le password è possibile solo indicandole tra ([]) per non creare ambiguità. Visualizzare i film non in prestito. Server side um 210 di 243 Prendere in prestito un film. Server side um 211 di 243 PROGRAMMAZIONE CLIENT SIDE INTRODUZIONE Gli sviluppatori ASP.NET sono programmatori server side e non conoscono la programmazione client side ma vista l’importanza dell’UX, Microsoft ha integrato in Visual Studio le librerie JavaScript. ASP.NET AJAX È l’implementazione per i programmatori .NET di AJAX. Se si volesse agire su una pagina web senza creare PostBack, l’unico modo per farlo è progettare una funzione JavaScript, invece, si possono inserire i controlli web all’interno dell’UpadatePanel così che i Postback generati sono tutti intercettati da AJAX. File WEBFORMAJAX.ASPX È una pagina web con un’etichetta e un pulsante all’interno di un UpadatePanel e un’etichetta e un pulsante fuori. Inserire un controllo ScriptManager all’interno del TAG <form> per segnalare alla pagina che si usa la tecnologia AJAX, quindi inserire un controllo UpdatePanel. Associare a ciascuno dei due pulsanti il metodo OnClick per cambiare il testo dell’etichetta corrispondente. <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebFormAjax.aspx.cs"Inherits="WebApplicationJuiceUI.WebFormAjax" %> Server side um 212 di 243 <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>AJAX</title> </head> <body> <form id="form1" runat="server"> <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager> <div> <asp:UpdatePanel ID="UpdatePanel1" runat="server"> <ContentTemplate> <asp:Label ID="Label1" runat="server" Text="Label" Width="80px"></asp:Label> <asp:Button ID="Button1" runat="server" Text="Pulsante 1" OnClick="Button1_Click" /> </ContentTemplate> </asp:UpdatePanel> <asp:Label ID="Label2" runat="server" Text="Label" Width="80px"></asp:Label> <asp:Button ID="Button2" runat="server" Text="Pulsante 2" OnClick="Button2_Click" /> </div> </form> </body> </html> Eseguire l’applicazione e fare clic sul Pulsante 1 che si trova all’interno dell’UpadatePanel e poi sul Pulsante 2 che si trova all’esterno. Entrambi i pulsanti cambiano il testo dell’etichetta corrispondente ma il primo pulsante non provoca un PostBack perché l’UpadatePanel carica solo la porzione di pagina interessata. Il secondo pulsante provoca un PostBack con il conseguente caricamento della pagina. L’UpadatePanel dev’essere usato con dovizia perché rallenta le prestazioni del server web in quanto usa pesantemente le risorse di sistema. AJAX CONTROL TOOLKIT Fare clic su Strumenti/Gestione pacchetti NuGet/Gestisci pacchetti NuGet per la soluzione…, nella finestra che si apre digitare, all’interno della casella di testo, AjaxControlToolkit. Per inserire i nuovi controlli nella Casella degli strumenti, fare clic con il pulsante destro sulla scheda Generale e selezionare Aggiungi scheda e assegnare il nome AjaxControlToolkit, quindi fare di nuovo clic con il pulsante destro e selezionare la voce Scegli elementi… e selezionare il file che si trova nella cartella BIN del progetto che si chiama AJAXCONTROLTOOLKIT.DLL. Server side um 213 di 243 File WEBFORMAJAXCONTROLTOOLKIT.ASPX È una pagina web con un controllo Accordion. <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebFormAjaxControlToolkit.aspx.cs" Inherits="WebApplicationJuiceUI.WebFormAjaxControlToolkit" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>AjaxControlToolkit</title> <style type="text/css"> .accordion { width: 800px; } .accordionTestata { border: 1px solid #2F4F4F; color: white; background-color: #2E4d7B; font-family: Arial, Sans-Serif; font-size: 12px; Server side um 214 di 243 font-weight: bold; padding: 5px; margin-top: 5px; cursor: pointer; } .accordionTestataSelezione { border: 1px solid #2F4F4F; color: white; background-color: #5078B3; font-family: Arial, Sans-Serif; font-size: 12px; font-weight: bold; padding: 5px; margin-top: 5px; cursor: pointer; } .accordionContenuto { background-color: #D3DEEF; border: 1px dashed #2F4F4F; border-top: none; padding: 5px; padding-top: 10px; } </style> </head> <body> <form id="form1" runat="server"> <ajaxToolkit:ToolkitScriptManager runat="server"></ajaxToolkit:ToolkitScriptManager> <div> <ajaxToolkit:Accordion ID="Accordion1" runat="server" CssClass="accordion" HeaderCssClass="accordionTestata" HeaderSelectedCssClass="accordionTestataSelezione" ContentCssClass="accordionContenuto" > <Panes> <ajaxToolkit:AccordionPane ID="AccordionPane1" runat="server"> <Header>Andrea Sperelli</Header> <Content> Ciao, mondo da Andrea! </Content> </ajaxToolkit:AccordionPane> <ajaxToolkit:AccordionPane ID="AccordionPane2" runat="server"> <Header>Guido Lavespa</Header> <Content> Ciao, mondo da Guido! </Content> </ajaxToolkit:AccordionPane> <ajaxToolkit:AccordionPane ID="AccordionPane3" runat="server"> <Header>Mario Rossi</Header> <Content> Ciao, mondo da Mario! </Content> </ajaxToolkit:AccordionPane> Server side um 215 di 243 <ajaxToolkit:AccordionPane ID="AccordionPane4" runat="server"> <Header>Remo Labarca</Header> <Content> Ciao, mondo da Remo! </Content> </ajaxToolkit:AccordionPane> </Panes> </ajaxToolkit:Accordion> </div> </form> </body> </html> JAVASCRIPT File WEBFORMJAVASCRIPT.ASPX Client side: si usa il metodo OnClientClick del controllo Button, quando il pulsante è premuto, manda in esecuzione la funzione JavaScript: finestra di avvertimento. <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebFormJavascript.aspx.cs" Inherits="WebApplicationJuiceUI.WebFormJavascript" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> </head> <body> <form id="form1" runat="server"> <div> <asp:Button ID="Button1" runat="server" Text="Uno" OnClientClick="javascript:alert('Avviso!');" /><br /> <asp:Button ID="Button2" runat="server" Text="Due" /> Server side um 216 di 243 </div> </form> </body> </html> File WEBFORMJAVASCRIPT.ASPX.CS Server side: si aggiunge un attributo al pulsante che in corrispondenza dell’evento clic associa la stessa funzione JavaScript. using System; namespace WebApplicationJuiceUI { public partial class WebFormJavascript : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Button2.Attributes.Add("onclick", "javascript:alert('Avviso!');"); } } } Esempio, realizzare una casella di testo che mostri l’orario sulla pagina web. Si può agire server side nel metodo Page_Load usando la classe DateTime e leggendo la proprietà Now per determinare l’orario attuale. L’ora ricavata è poi visualizzata sul browser ma in tal caso l’ora sarebbe quella del server che potrebbe anche non coincidere con quella del client e anche se così fosse, si dovrebbe tener conto dell’intervallo che intercorre tra la richiesta al server e la visualizzazione sul client che potrebbe influire anche di diversi secondi sulla precisione dell’orario. Client side: si usa JavaScript per prelevare la data client side e mostrarla nel browser. Progettare la funzione ShowTime, inserirla nel body della pagina e invocarla all’evento onload, inserendo il nome all’interno del TAG <body>. <%@ Page Language="C#" AutoEventWireup="true" CodeFile="simple.aspx.cs" Inherits="simple" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Esempio 1</title> </head> Server side um 217 di 243 <body onload="ShowTime();"> <script type="text/javascript"> <!-function ShowTime() { document.forms[0]['txtClientTime'].value=Date(); } // --> </script> <form id="form1" runat="server"> <div> <asp:Label ID="labTitle" runat="server" Font-Bold="True" Font-Italic="True" FontNames="Arial" ForeColor="#400040" Text="Esempio 1"></asp:Label><br /> <br /> <asp:Label ID="Label1" runat="server" Text="Sul client sono le: "></asp:Label> <asp:TextBox ID="txtClientTime" runat="server" Width="250px"></asp:TextBox><br /> <asp:Label ID="Label2" runat="server" Text="Sul server sono le: "></asp:Label> <asp:TextBox ID="txtServerTime" runat="server" Width="250px"></asp:TextBox><br /><br /> <asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="~/Default.aspx">Home</asp:HyperLink><br /> <br /> </form> </body> </html> Server side: si usa ASP.NET per prelevare la data server side e mostrarla nel browser. using System; public partial class simple : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { txtServerTime.Text=DateTime.Now.ToString(); } } ASP.NET e il .NET Framework contengono classi e relativi metodi che permettono d’inserire codice JavaScript in una pagina web, ad esempio il passaggio parametri dal codice client side a quello server side o viceversa; in pratica è ASP.NET che genera anche il codice JavaScript che dev’essere utilizzato dal client. La classe ClientScriptManager è utilizzata per “creare dinamicamente” gli script client side utilizzando codice server side. Server side um 218 di 243 Per ottenere un oggetto ClientScriptManager in una pagina web ASP.NET è sufficiente utilizzare la proprietà ClientScript dell’oggetto Page. La classe ClientScriptManager identifica univocamente gli script utilizzando una chiave di tipo String e un Type. Gli script con la stessa chiave e lo stesso tipo sono considerati dei duplicati. Il tipo è utilizzato per distinguere degli script nel caso in cui differenti controlli nella stessa pagina definiscano degli script con una stessa chiave. Esempio, data una pagina con due caselle di testo, copiare il contenuto della prima nella seconda ma trasformando in maiuscolo il testo. Client side: il metodo utilizzabile per inserire uno script client side in una pagina web è RegisterClientScriptBlock. <%@ Page Language="C#" AutoEventWireup="true" CodeFile="registerclientscriptblock.aspx.cs" Inherits="registerclientscriptblock" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Esempio 2</title> </head> <body> <form id="form1" runat="server"> <div> <asp:Label ID="labTitle" runat="server" Font-Bold="True" Font-Italic="True" FontNames="Arial" ForeColor="#400040" Text="Esempio 3"></asp:Label><br /> <br /> <asp:TextBox ID="txtSource" runat="server" Width="250px" ></asp:TextBox><br/><br/> <asp:Button ID="btCopyServerSide" runat="server" OnClick="btCopyServerSide_Click" Text="Copia server side" /> <input type="button" id="btCopyClient" runat="server" value="Copia client side" /><br/><br/> <asp:TextBox ID="txtTarget" runat="server" Width="250px"></asp:TextBox><br /><br /> <asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="~/Default.aspx">Home</asp:HyperLink><br /> <br /> </div> </form> </body> </html> Server side: nell’evento Page_Load sarà creato il codice JavaScript da eseguire al clic sul pulsante btCopyClient, registrato nella pagina con il metodo RegisterClientScriptBlock. In questo caso è utilizzato un overload del metodo RegisterClientScriptBlock con un parametro booleano che indica se inserire o meno nel blocco di codice generato anche i TAG <script> di apertura e chiusura del blocco. using System; public partial class registerclientscriptblock : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { string idTarget = txtTarget.ID; string idSource = txtSource.ID; Server side um 219 di 243 string scriptCopy = @"function Copy() { document.forms[0]['"+idTarget+"'].value=document.forms[0]['"+idSource+@"'].value.toUpp erCase(); };"; ClientScript.RegisterClientScriptBlock(this.GetType(), "Script_Copy", scriptCopy, true); btCopyClient.Attributes["onClick"]="Copy()"; } } protected void btCopyServerSide_Click(object sender, EventArgs e) { txtTarget.Text = txtSource.Text.ToUpper(); } } Il pulsante a destra esegue la copia e trasforma in maiuscolo il contenuto della prima TextBox, senza eseguire un PostBack della pagina sul server, in pratica utilizza il codice JavaScript generato dinamicamente. Esempio, progettare la funzione client side Date in maniera da ottenere la data attuale e scriverla in una TextBox appena la pagina è caricata, quindi senza necessità di premere un pulsante o di forzare un altro evento. Client side: si usa il metodo RegisterStartupScript per registrare lo script da eseguire. <%@ Page Language="C#" AutoEventWireup="true" CodeFile="registerstartupscript.aspx.cs" Inherits="registerstartupscript" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Esempio 3</title> </head> <body> <form id="form1" runat="server"> <div> <asp:Label ID="labTitle" runat="server" Font-Bold="True" Font-Italic="True" FontNames="Arial" ForeColor="#400040" Text="Esempio 3"></asp:Label><br /> <br /> Server side um 220 di 243 <asp:Label ID="Label1" runat="server" Text="Sono le: "></asp:Label> <asp:TextBox ID="txtClientTime" runat="server" Width="250px"></asp:TextBox><br /> <br /> <asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="~/Default.aspx">Home</asp:HyperLink><br /> <br /> </div> </form> </body> </html> Server side. using System; public partial class registerstartupscript : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { string scriptShowTime = @"document.forms[0]['txtClientTime'].value=Date();"; ClientScript.RegisterStartupScript(this.GetType(), "Script_ShowTime", scriptShowTime, true); } } Esempio, eseguire il codice client side al submit di un modulo HTML, in pratica non appena si verifica l’evento onsubmit. Client side. <%@ Page Language="C#" AutoEventWireup="true" CodeFile="RegisterOnSubmitStatement.aspx.cs" Inherits="RegisterOnSubmitStatement" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Esempio 4</title> </head> <body> <form id="form1" runat="server"> <div> <asp:Button ID="Button1" runat="server" Text="Invia" OnClick="Button1_Click" /></div> </form> </body> Server side um 221 di 243 </html> Server side: per registrare un blocco di codice in modo che sia eseguito, si usa il metodo RegisterOnSubmitStatement. In questo caso, al submit della pagina, sarà mostrata una finestra di dialogo per confermarlo e solo se l’utente fa clic su OK sarà eseguito il codice del gestore Button1_Click. using System; public partial class RegisterOnSubmitStatement : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { string script = "return confirm('Sei sicuro di fare il submit di questo form?');"; ClientScript.RegisterOnSubmitStatement(this.GetType(), "onsubmitscript", script); } protected void Button1_Click(object sender, EventArgs e) { Button1.Text = "Submit eseguito!"; } } Esempio, inserire in una pagina web, dei blocchi di codice JavaScript salvati in uno o più file esterni e referenziarli per mezzo di un URL. File SCRIPTS\EXTERNAL.JS function Saluti(str) { alert('Ciao '+str); } Il metodo RegisterClientScriptInclude funziona come RegisterClientScriptBlock ma legge lo script da un file esterno con estensione JS. Il file da includere sarà inserito prima di ogni altro script eventualmente presente e dunque potrebbe non avere modo di accedere a qualche elemento della pagina. Client side. <%@ Page Language="C#" AutoEventWireup="true" CodeFile="registerclientscriptinclude.aspx.cs" Inherits="registerclientscriptinclude" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Esempio 5</title> Server side um 222 di 243 </head> <body> <form id="form1" runat="server"> <div> <asp:Button ID="Button1" runat="server" Text="Ciao" /></div> </form> </body> </html> Server side: al clic sul pulsante Button1, sarà invocata la funzione Saluti con parametro “Andrea Sperelli” e quindi il risultato sarà una Message Box client side. using System; public partial class registerclientscriptinclude : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { ClientScript.RegisterClientScriptInclude(this.GetType(),"externaljs","Scripts/external.js"); Button1.Attributes.Add("onclick", "Saluti('Andrea Sperelli')"); } } Il codice client side non può, in generale, comunicare con quello server side. Per esempio, una funzione JavaScript non può passare dei valori al codice della pagina durante un PostBack: due possibili soluzioni. 1. Campi hidden e cookie. 2. CallBack client side. Funzione CallBack È una speciale funzione che è chiamata direttamente dal SO quando ha finito o sta per iniziare un compito. Non occorre interrogare Windows ogni intervallo di tempo per sapere se, ad esempio, qualcuno ha premuto un tasto. Sarà Windows a spedire un messaggio di tipo WM_KEYDOWN che, tramite una CallBack, può essere intercettato e gestito dall’applicazione web. Inoltre, gli eventi, all’interno di .NET, non sono altro che delle funzioni CallBack chiamate dal framework. In pratica, permette di lavorare su una pagina, leggerne i valori e scriverli, senza PostBack, in pratica senza dover rigenerare l’intera pagina. Questo è chiamato anche CallBack fuoribanda, perché in tale situazione una funzione Server side um 223 di 243 client side, per esempio in JavaScript, invia una richiesta asincrona ad una pagina, al di fuori del ciclo normale di vita. La pagina ASP.NET a questo punto, esegue una versione modificata del proprio ciclo di vita e processa la richiesta. Per ottenere un riferimento alla funzione client che, una volta invocata, inizia il CallBack, si usa la classe ClientScriptManager e in particolare il metodo GetCallBackEventReference. Esempio, aggiornare una parte della pagina, senza eseguire alcun PostBack e dunque con un’UX più piacevole ed efficace. Client side: la pagina HTML contiene una DropDownList con i nomi delle regioni italiane, e un pulsante alla cui pressione si vuole ricavare l’elenco delle province appartenenti alla regione selezionata. La funzione di CallBack usata dal server è DisplayResultsCallback, una volta letto il risultato dal server, genera un elenco puntato, per mezzo dei TAG <ul> e <li> e a questo punto lo visualizza inserendolo nell’elemento divContents. <%@ Page Language="C#" AutoEventWireup="true" CodeFile="ClientCallback.aspx.cs" Inherits="ClientCallback" %> <html> <head> <title>Esempio 6</title> <script language="javascript"> function GetProvince() { var regione = document.forms[0].ddlRegioni.value; GetProvinceOnServer(regione, "txtRegione"); } function DisplayResultsCallback(result, context ) { var strHTML=""; var s=result; if(result=!"") { var temp = new Array(); temp=s.split("|"); if(temp.length>0) { strHTML="Province:<br><ul>"; for(i=0;i<temp.length;i++) { strHTML+="<li>"+temp[i]+"</li>"; } strHTML+="</ul>"; } else strHTML += "<br><br><b>data not found</b>"; } else { strHTML += "<br><br><b>data not found</b>"; } divContents.innerHTML = strHTML; } function DisplayErrorCallback( error, context ) { alert(" Query Failed. " + error); } </script> </head> <body> <form id="Form1" runat="server"> <H1> Regioni e province</H1> Server side um 224 di 243 <asp:AccessDataSource ID="AccessDataSource1" runat="server" DataFile="~/App_Data/reg_prov.mdb" SelectCommand="SELECT [Nome] FROM [Regioni]"></asp:AccessDataSource> <br><br> <font color="#800080"><b>Seleziona la regione:</b> </font> <asp:DropDownList ID="ddlRegioni" runat="server" DataSourceID="AccessDataSource1" DataTextField="Nome"> </asp:DropDownList> <INPUT id="btnGetProvince" type="button" value="Mostra province" onclick="GetProvince()"> <div id="divContents"> </div> </form> </body> </html> Server side: si usa un DB Access con due tabelle Regioni e Province e una query. L’elenco delle province deve apparire senza dare l’impressione che la pagina sia ricaricata e dunque si usa il meccanismo dei CallBack, per aggiornare solo il codice HTML contenuto in un elemento <div> della pagina. Il metodo GetProvince ricava l’elenco delle province di una data regione e crea una stringa contenente le province separate con un carattere (|), perché è una stringa che la funzione di CallBack si aspetta. La Web Form deve, inoltre, implementare l’interface ICallbackEventHandler che definisce due metodi. Il primo processa l’evento di CallBack inviato dal client, ricevendo un argomento string, in questo caso il nome della regione. Il secondo si occupa d’invocare il metodo di accesso al DB GetProvince e i restituire al client, il risultato. Dal client side la pagina web contiene due funzioni JavaScript, una che invia il CallBack al server e una per ricevere i risultati. La prima è creata al primo caricamento della pagina, per mezzo del metodo GetCallbackEventReference per ottenere un riferimento alla funzione CallBack e poi utilizzando il RegisterClientScriptBlock. using System; using System.Data; using System.Data.OleDb; using System.Web.UI; public partial class ClientCallback : System.Web.UI.Page,ICallbackEventHandler { private string _callbackArg; public void Page_Load(object sender, EventArgs e) { if (!Request.Browser.SupportsCallback) throw new ApplicationException("Il browser non supporta Client callbacks!"); if (!IsPostBack) { string src = Page.ClientScript.GetCallbackEventReference(this,"arg", "DisplayResultsCallback", "ctx", "DisplayErrorCallback", false); string mainSrc = @"function GetProvinceOnServer(arg, ctx){ " + src + "; }"; Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "GetProvinceOnServer", mainSrc, true); } } void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument) { _callbackArg = eventArgument; } Server side um 225 di 243 string ICallbackEventHandler.GetCallbackResult() { try { return GetProvince(_callbackArg); } catch (Exception ex) { throw new ApplicationException("Errore durante il run-time: " + ex.Message); } } public string GetProvince(string regione) { try { AccessDataSource1.SelectCommand="SELECT Province.Nome" + "FROM Regioni"+ "INNER JOIN Province ON Regioni.IDRegione = Province.IDRegione" + "WHERE Regioni.Nome='"+regione+"'"; DataView dv=(DataView)AccessDataSource1.Select(DataSourceSelectArguments.Empty); DataTable dt = dv.Table; string province=""; foreach (DataRow dr in dt.Rows) province += dr[0].ToString() + "|"; if (province.Length > 0) province = province.Substring(0, province.Length - 1); return province; } catch (Exception ex) { throw ex; } } } La figura mostra come interagisce il client con il server utilizzando il CallBack. Il processo è avviato quando l’utente seleziona una regione dalla casella a discesa e preme il pulsante di ricerca. A questo punto è invocato il metodo GetProvinceOnServer, creato dinamicamente sul server, in maniera da conoscere la funzione client di CallBack. Ora il client genera l’evento di CallBack, utilizzando il metodo RaiseCallbackEvent, il Server side um 226 di 243 server ricerca le province sul DB e le restituisce al client per mezzo di GetCallbackResult. Infine, il client ottiene i risultati e può occuparsi di aggiornare solo la parte di pagina che mostrerà i risultati. ANIMAZIONI CON JQUERY Per usare queste funzionalità si deve includere la libreria nell’applicazione web, due modi. 1. CDN (Content Delivery Network) Si usa la libreria pubblicata sui server remoti, riduce il tempo di caricamento dell’applicazione web perché la richiesta di download non arriva al server web che la ospita ma al CDN chiamato in causa. <script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.9.0.min.js"</script> 2. Libreria inclusa in Visual Studio Le versioni MIN sono compresse, quindi più veloci da caricare. Il linguaggio si basa sui selettori CSS che indicano su quali elementi della pagina applicare le animazioni, effetti e tutte le altre proprietà disponibili nella libreria. File WEBFORMJQUERY.ASPX <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebFormjQuery.aspx.cs" Inherits="WebApplicationJuiceUI.WebFormjQuery" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>Animazione con jQuery</title> <script src="Scripts/jquery-1.8.3.min.js"></script> <style type="text/css"> body{ margin:0; text-align:left; background-color:white; } #main{ width:800px; height:400px; background-color:red; } .appari{ color:black;} .scompari{ color:black;} #content{ width:300px; height:200px; background-color:yellow; display:none;} #menu{ width:300px; height:100px;} #menu li{ float:left; list-style:none; cursor:pointer;margin:20px;} </style> <script type="text/javascript"> $(document).ready(function () { $(".appari").click( function () { $("#content").fadeIn("slow"); }); Server side um 227 di 243 $(".scompari").click( function () { $("#content").fadeOut("slow"); }); }); </script> </head> <body> <form id="form1" runat="server"> <div id="container"> <ul id="menu"> <li class="appari">APPARI</li> <li class="scompari">SCOMPARI</li> </ul> <div id="content"></div> </div> </form> </body> </html> Quando l’utente fa clic sulle voci di menu, jQuery, grazie ai suoi selettori applicati alle classi .appari e .scompari, capisce su quali elementi della pagina agire. Le proprietà fadeIn e fadeOut fanno apparire e scomparire un livello con effetto comparsa. Server side um 228 di 243 JUICE UI S’installa nello stesso modo e si aggiungono i suoi controlli come nell’AjaxControlToolkit. File WEBFORMJUICE.ASPX È la stessa pagina WEBFORMAJAXCONTROLTOOLKIT.ASPX con il controllo Accordion. <%@ Page Language="C#"AutoEventWireup="true"CodeBehind="WebFormJuice.aspx.cs"Inherits=" WebApplicationJuiceUI.WebFormJuice"%> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>Juice</title> <script src="Scripts/jquery-1.8.3.min.js"></script> <script src="Scripts/jquery-ui-1.9.2.min.js"></script> <script src="Scripts/amplify.min.js"></script> <style type="text/css"> .accordion { width: 800px; } </style> </head> <body> <form id="form1" runat="server"> Server side um 229 di 243 <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager> <div> <Juice:Accordion runat="server" Collapsible="true" ID="Accordion1" CssClass="accordion"> <Juice:AccordionPanel ID="AccordionPanel1" runat="server" Title="Andrea Sperelli"> <PanelContent> Ciao, mondo da Andrea! </PanelContent> </Juice:AccordionPanel> <Juice:AccordionPanel ID="AccordionPanel2" runat="server" Title="Guido Lavespa"> <PanelContent> Ciao, mondo da Guido! </PanelContent> </Juice:AccordionPanel> <Juice:AccordionPanel ID="AccordionPanel3" runat="server" Title="Mario Rossi"> <PanelContent> Ciao, mondo da Mario! </PanelContent> </Juice:AccordionPanel> <Juice:AccordionPanel ID="AccordionPanel4" runat="server" Title="Remo Labarca"> <PanelContent> Ciao, mondo da Remo! </PanelContent> </Juice:AccordionPanel> </Juice:Accordion> </div> </form> </body> </html> Permette di usare i suoi controlli come se fossero controlli server side, quindi il programmatore client side, non deve scrivere codice JavaScript ma solo CSS. File WEBFORMJUICE2.ASPX <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebFormJuice2.aspx.cs" Inherits="WebApplicationJuiceUI.WebFormJuice2" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title></title> <script src="Scripts/jquery-1.8.3.min.js"></script> <script src="Scripts/jquery-ui-1.9.2.min.js"></script> <script src="Scripts/amplify.min.js"></script> </head> <body> <form id="form1" runat="server"> <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager> <juice:Accordion ID="Accordion1" runat="server"> </juice:Accordion> <div> </div> </form> Server side um 230 di 243 </body> </html> File WEBFORMJUICE2.ASPX.CS S’istanzia AccordionPanel e si settano le proprietà Title e PanelContent. La classe AccordionPanelTemplate eredita dall’interface ITemplate ed è usata per aggiungere testo all’AccordionPanel. using System; using System.Web.UI; namespace WebApplicationJuiceUI { public partial class WebFormJuice2 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Juice.AccordionPanel panel1 = new Juice.AccordionPanel(); panel1.Title = "Andrea Sperelli"; AccordionPanelTemplate c1=new AccordionPanelTemplate("Ciao, mondo da Andrea!"); panel1.PanelContent = c1; Accordion1.AccordionPanels.Add(panel1); Juice.AccordionPanel panel2 = new Juice.AccordionPanel(); panel2.Title = "Guido Lavespa"; AccordionPanelTemplate c2=new AccordionPanelTemplate("Ciao, mondo da Guido!"); panel2.PanelContent = c2; Accordion1.AccordionPanels.Add(panel2); } public class AccordionPanelTemplate : ITemplate { private string _testo; public AccordionPanelTemplate(string testo) { _testo= testo; } public void InstantiateIn(Control container) { LiteralControl l = new LiteralControl(_testo); container.Controls.Add(l); } } } } Server side um 231 di 243 Spa (single page applicationS) Sono applicazioni web mono pagina che caricano una sola pagina HTML e la aggiornano dinamicamente invece di caricare nuove pagine. Dopo il caricamento della pagina web, l’applicazione dialoga con il server attraverso richieste AJAX facendo uso di jQuery, KNOCKOUT.JS e l’ASP.NET WEB API. Questo rende la comunicazione performante ed elimina il referesh della pagina. Fare clic su, File/Nuovo Progetto… (CTRL+MAIUSC+N)/Modelli/Visual C#/Web/Applicazione Web ASP.NET. Server side um 232 di 243 Il template genera il progetto seguente. Eseguire l’applicazione web, genera automaticamente il DB SQL Server di nome ASPNET-SPA-20140425055118.MDF. Server side um 233 di 243 Fare clic su Esplora server. Selezionare la tabella ASPNETUSERS e con il pulsante destro del mouse fare clic su Mostra dati tabella. SPWS (SINGLE PAGE WEB SITE) È un sito web composto da una sola pagina: all’interno della quale sono organizzati tutti i contenuti. Elevato impatto comunicativo, per esempio curriculum vitae, tesina esame di stato, promozione di un prodotto. Adatto: a ogni tipo di browser, a ogni tipo di dispositivo: desktop, tablet e mobile. Si sfrutta il template HTML 5.0 Boilerplate. I link del menu punteranno a parti specifiche della pagina, individuate dagli ID degli elementi article. Ogni area della pagina è individuata da un article, contenente un header e una o più sezioni. Dato che tutto il sito è in una pagina, l’utente potrebbe scorrerla a lungo perdendo di vista la navigazione: ci sono diverse soluzioni. Nel progetto si è scelto di rendere fisso il menu e creare un effetto dinamico in modo che il browser non salti direttamente all’elemento desiderato ma lo raggiunga con uno scorrimento. Due soluzioni. 1. Plugin JQUERY.SCROLLTO.MIN.JS. 2. Metodo animate di JQuery. Server side um 234 di 243 Cartella CSS MAIN.CSS Contiene gli stili del sito web, le unità di misura sono relative ed espresse in % e in cm. Ridimensionando la finestra (cambia la larghezza della viewport), si vede il corpo della pagina allargarsi e restringersi. Il riferimento iniziale del lavoro è il desktop, in seguito si modificheranno alcuni elementi per adattarli agli altri dispositivi. NORMALIZE.CSS Rende la resa della pagina indipendente dal browser, infatti, provvede ad azzerare gli stili predefiniti dei browser affinché il sito sia cross-browser. Cartella JS A parte MODERNIZR-2.6.2.MIN.JS, tutti gli altri script sono inclusi in fondo al documento, per due motivi. 1. Consentire il caricamento del DOM prima dell’esecuzione degli script. 2. Evitare che il caricamento degli script ritardi l’accesso alla pagina da parte dell'utente. DETECTMOBILEBROWSER.JS È istanziata la variabile IsMobile che assume valore true o false secondo l’esito del test. MAIN2.JS Conoscere la posizione degli elementi rispetto alla viewport, in altre parole l’area visibile della pagina, permette di aggiungere funzionalità che arricchiscono l’UI, per esempio modificare il colore delle intestazioni man mano che si avvicinano alla sommità della viewport. Bisogna progettare un algoritmo che cambi progressivamente il colore di sfondo degli elementi header, discendenti da ogni article, man mano che questi si avvicinano, durante lo scorrimento, alla sommità della viewport (meno di 50px). Se il blocco article è al di sotto della metà della viewport, lo sfondo dell’header è nero; se è alla sommità, è rosso scuro; durante lo scorrimento il valore del rosso scuro cambia linearmente rispetto alla distanza di scorrimento. Server side um 235 di 243 Si possono calcolare i valori delle variabili con l’equazione di una retta. (y - y1) / (y2 - y1) = (x - x1) / (v2 - x1) Dove la y è il valore rosso scuro e la x la distanza di scorrimento. Sui dispositivi mobili, il browser non risponde agli eventi nello stesso modo del desktop. Per esempio, l’evento scroll sul desktop è eseguito pixel per pixel, sui tablet e smartphone, al contrario, è eseguito solo nel momento in cui lo scorrimento s’interrompe. Se il comportamento della pagina deve dipendere dallo scostamento graduale della viewport, si deve correggere la diversa reattività del browser gestendo gli eventi touchstart, touchmove e touchend. TS (TYPESCRIPT) È un nuovo linguaggio di programmazione basato su JavaScript. Progettato per estendere le funzionalità di JavaScript aggiungendo tipi, classi e moduli per consentire ai programmatori di realizzare applicazioni più complesse compatibili con ogni browser e ogni SO. Utilizza codice sorgente JavaScript e la compilazione fornisce gli stessi risultati. Nel corso degli ultimi anni la velocità di JavaScript è raddoppiata ogni nove mesi. Nonostante ciò è aumentato l’utilizzo di HTML 5.0 per realizzare applicazioni web in grado di fornire una migliore esperienza d’uso agli utenti. I programmatori hanno cercato di espandere JavaScript oltre il browser per includere anche app native per i dispositivi, come quelle presenti sul Windows Store o app per il cloud, come quelle in esecuzione su Windows Azure. Creare applicazioni su larga scala con JavaScript è però molto complicato, per cui Microsoft ha deciso di creare il linguaggio TS. TS è un un linguaggio di scripting client side per pagine web che integra tutti i tool per l’analisi e l’ottimizzazione del codice JavaScript in un unico linguaggio, adatto sia per applicazioni client side sia server side. TS garantisce l’interoperabilità con JavaScript perché funziona con gli stessi framework e le stesse librerie, il programmatore deve solo fare il copia/incolla del codice JavaScript all’interno di un file TS. Compilazione CLI (Command Line Interface) C:\Program Files (x86)\Microsoft SDKs\TypeScript\1.0>tsc Version 1.0.1.0 Syntax: tsc [options] [file ..] Examples: tsc hello.ts tsc --out foo.js foo.ts tsc @args.txt Options: --codepage NUMBER Specify the codepage to use when opening source files. -d, --declaration Generates corresponding .d.ts file. -h, --help Print this message. --mapRoot LOCATION Specifies the location where debugger should locate map files Server side um 236 di 243 instead of generated locations. -m KIND, --module KIND Specify module code generation: 'commonjs' or 'a md' --noImplicitAny Warn on expressions and declarations with an imp lied 'any' type. --out FILE Concatenate and emit output to single file. --outDir DIRECTORY Redirect output structure to the directory. --removeComments Do not emit comments to output. --sourcemap Generates corresponding .map file. --sourceRoot LOCATION Specifies the location where debugger should loc ate TypeScript files instead of source locations. -t VERSION, --target VERSION Specify ECMAScript target version: 'ES3' (default), or 'ES5' -v, --version Print the compiler's version: 1.0.1.0 @<file> Insert command line options and files from a fil e. File PRIMO.TS function greeter(person) { return "Hello, " + person; } var user = "Jane User"; document.body.innerHTML = greeter(user); I risultato della compilazione è il file seguente. File PRIMO.JS function greeter(person) { return "Hello, " + person; } var user = "Jane User"; document.body.innerHTML = greeter(user); Server side um 237 di 243 Compilazione MDE (Microsoft Development Environment) Per creare un nuovo progetto, fare clic su File/Nuovo/Progetto… (CTRL+MAIUSC+N). Nella finestra di dialogo Nuovo Progetto selezionare Modelli/TypeScript, quindi selezionare il tipo di applicazione, Applicazione HTML con TypeScript. Immettere il nome del progetto nel campo Nome: quindi per creare il progetto, premere OK. Visual C# crea una soluzione costituita da un progetto dallo stesso nome, composto a sua volta da una finestra vuota denominata FORM1.CS. File APP.CSS body { font-family: 'Segoe UI', sans-serif; } span { font-style: italic; } File APP.TS class Greeter { element: HTMLElement; span: HTMLElement; Server side um 238 di 243 timerToken: number; constructor(element: HTMLElement) { this.element = element; this.element.innerHTML += "The time is: "; this.span = document.createElement('span'); this.element.appendChild(this.span); this.span.innerText = new Date().toUTCString(); } start() { this.timerToken = setInterval(() => this.span.innerHTML = new Date().toUTCString(), 500); } stop() { clearTimeout(this.timerToken); } } window.onload = () => { var el = document.getElementById('content'); var greeter = new Greeter(el); greeter.start(); }; File INDEX.HTML <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <title>TypeScript HTML App</title> <link rel="stylesheet" href="app.css" type="text/css" /> <script src="app.js"></script> </head> <body> <h1>TypeScript HTML App</h1> <div id="content"></div> </body> </html> Server side um 239 di 243 CAPCTHA (COMPLETELY AUTOMATED PUBLIC TURING TEST TO TELL COMPUTERS AND HUMANS APART) INTRODUZIONE Letteralmente, test di Turing completamente automatizzato e pubblico per distinguere computer dagli umani. Un malintenzionato potrebbe progettare uno script per l’invio automatico di dati, eseguendo così un attacco di tipo flooding, in pratica inondando l’applicazione di dati fasulli. Un attacco di questo tipo può avere conseguenze disastrose: il sovraccarico dell’applicazione, il collasso del DB con la scrittura di numerosi dati spuri e l’attivazione di servizi non richiesti. Un possibile rimedio è un test per verificare la presenza di un operatore umano al momento dell’inserimento dei dati in un form. In pratica, sono codici di sicurezza informatica per distinguere tra umani e bot (robot). Un codice CAPTCHA è solitamente formato da stringhe alfanumeriche case sensitive, di lunghezza variabile. La particolarità sta nella maniera in cui queste stringhe sono “stampate a video”: le lettere sono distorte e semi cancellate tramite trasformazioni geometriche, così da renderle difficilmente comprensibili a occhio umano e in pratica indecifrabile per un qualsiasi S/W di tipo OCR (Optical Character Recognition). Per riuscire a superare il test, bisognerà trascrivere esattamente le stringhe all’interno del campo posto immediatamente sotto l’immagine. Un’evoluzione di questa tecnica è il reCAPTCHA sviluppato da Google, le stringhe alfanumeriche sono parole e numeri tratte da libri antichi e rari presenti in Google Books, vecchi giornali come le copie dell’archivio storico del New York Times o copioni di vecchi show radiofonici. L’intento di questo test è evitare che i bot possano emulare il comportamento dell’utente, per esempio il riempimento e l’invio di un form. Intorno alle tecniche di test CAPTCHA e in particolare al meccanismo di ripetizione di testo distorto contenuto in immagini, c’è un acceso dibattito che ne contesta i limiti all’accessibilità dei servizi da parte di non vedenti o ipo vedenti. BATTERE I CAPTCHA Tre tecniche. 1. Usare manodopera a basso costo con il solo scopo di decifrare anche centinaia di codici ogni ora, così facendo, si aggirano le restrizioni provocate dal CAPTCHA e si può portare avanti la propria azione fraudolenta. 2. Sfruttare eventuali falle di sicurezza nell’implementazione del S/W CAPTCHA all’interno del sito web. 3. Sviluppare S/W di riconoscimento testuale sempre più preciso e potente. Server side um 240 di 243 ASP.NET E CAPTCHA Per implementare in ASP.NET il CAPTCHA è disponibile un controllo open source scaricabile dal sito www.guru4.net. È una libreria che si chiama GURU4.NET.WEB.CONTROLS.CAPTCHALIBRARY.DLL. Per avere a disposizione i controlli, in maniera visuale, nell’IDE di Visual Studio fare clic con il tasto destro nella Casella degli strumenti e scegliere l’opzione Scegli elementi…. Nella finestra di dialogo che si aprirà selezionare il percorso su disco della libreria. Una volta aggiunta la libreria nella casella degli strumenti si hanno due nuovi controlli. VisualCaptcha: si riferisce all’immagine di test. VisualCaptchaValidator: gestisce la validazione dell’input dell’utente. Esempio, progettare un codice CAPTCHA. File WEB.CONFIG Inserire <httpHandlers> per abilitare l’handler della libreria che crea dinamicamente l’immagine CAPTCHA. Server side um 241 di 243 Un indirizzo di tipo AXD non deve necessariamente corrispondere ad un file fisico su disco, infatti si può assegnare l’indirizzo ad una classe che implementa HttpHandler che può essere definita in una libreria. Da ciò se ne deduce che l’indirizzo VISUALCAPTCHA.AXD è gestito dalla classe GURU4.NET.WEB.CONTROLS.CAPTCHALIBRARY.VISUALCAPTCHAHANDLER. <?xml version="1.0"?> <configuration> <system.web> <compilation debug="true" /> <authentication mode="Windows" /> <httpHandlers> <add verb="GET" path="visualcaptcha.axd" type="GURU4.net.Web.Controls.CaptchaLibrary.VisualCaptchaHandler" /> <add verb="GET" path="captcha.axd" type="GURU4.net.Web.Controls.CaptchaLibrary.BasicCaptchaHandler" validate="false"/> </httpHandlers> </system.web> </configuration> File DEFAULT.ASPX Inserire in un Panel i seguenti controlli. 1. VisualCaptcha: dispone di alcune proprietà per regolare il tempo di validità della stringa CAPTCHA, proprietà Expiration, il numero dei caratteri della stringa, ChallengeTextLength e l’URL usato per l’handler, RenderUrl che deve corrispondere a quello indicato nel WEB.CONFIG, nell’esempio VISUALCAPTCHA.AXD. 2. TextBox per la convalida del testo. 3. VisualCaptchaValidator che gestirà la validazione dell’input, occorre impostare la proprietà AssociatedVisualCaptchaControlId, ovvero l’ID del controllo VisualCaptcha cui è associato il Validator, nell’esempio ImgCaptcha; ControlToValidate è l’ID del controllo da validare, la TextBox. 4. Pulsante per eseguire il test. Inserire in un altro Panel, inizialmente non visibile, Visible = false, il messaggio di superamento del test e un link per ricaricare la pagina. File STYLESHEET.CSS body { font:12px verdana,sans-serif; } h2 { font:bold 16px arial,sans-serif; } Server side um 242 di 243 Server side um 243 di 243 UBERTINI MASSIMO http://www.ubertini.it [email protected] Dip. Informatica Industriale I.T.I.S. "Giacomo Fauser" Via Ricci, 14 28100 Novara Italy tel. +39 0321482411 fax +39 0321482444 http://www.fauser.edu [email protected]
© Copyright 2024 Paperzz