ASP: la guida introduttiva - Capitolo 1

ASP: la guida introduttiva
Capitolo 1 - Introduzione ad ASP
1.0 - Cenni introduttivi
ASP, acronimo di Active Server Pages, è una sigla utilizzata per indicare una particolare
tecnologia, sviluppata da Microsoft sulla falsariga di PHP, mediante la quale è possibile
realizzare delle pagine Web il cui codice venga processato dal server prima di essere inviato al
client. Lo scopo di questa guida è quella di introdurre il lettore all'utilizzo della tecnologia ASP,
mostrandone gli aspetti di base. Gli unici prerequisiti richiesti sono la completa conoscenza dei
meccanismi di HTML, la conoscenza della sintassi di base di JavaScript ed un po' di praticità
con il settore del web-publishing, oltre ad un minimo di confidenza con le più basilari tecniche
programmative. Non sono richieste conoscenze relative ad altre tecnologie server-side simili ad
ASP: l'apprendimento dei meccanismi di questo ambito lavorativo è uno degli scopi della guida
stessa. Chi avesse scelto questa lettura per migrare verso ASP avendo già utilizzato
precedentemente altre tecnologie server-side può considerare alcune nozioni che verranno
presentate nel corso della guida come un semplice ripasso. L'unica fonte ufficiale di diffusione
di questa guida è rappresentata da www.sauronsoftware.it, sito che potrà essere consultato quindi
per ricercare materiale di supporto o versioni più recenti del manuale in questione. La
redistribuzione di questo materiale è consentita solo su esplicito consenso del suo autore.
1.1 - Le Active Server Pages
Le Active Server Pages sono particolari pagine Web in grado di integrare al loro interno
porzioni di codice, redatte mediante diversi linguaggi di scripting, che verranno elaborate dal
server. Di questi script verranno inviati al client solo i rispettivi output. Il linguaggio di scripting
solitamente utilizzato da una pagina ASP è VBScript, tuttavia è possibile scrivere pagine in ASP
anche utilizzando altri linguaggi, come PerlScript o JScript, purché essi vengano supportati dal
server che dovrà ospitare ed elaborare le pagine realizzate. Di norma oltre a VBScript è
supportato perlomeno anche JScript. Gli esempi che presenteremo in seguito saranno scritti in
JScript, ossia la versione di JavaScript targata Microsoft. Questa scelta, insolita per una guida
introduttiva ad ASP, è motivata da diversi fattori. Per prima cosa JScript offre una maggiore
chiarezza sintattica rispetto a VBScript, grazie alla sua sintassi C-like, ed inoltre JavaScript
risulta comunque il linguaggio di scripting più diffuso nell'ambito delle tecnologie client-side.
Per concludere il tipo di sintassi di JScript faciliterà la migrazione verso ASP a chi già sappia
sviluppare pagine PHP e faciliterà il passaggio inverso a chi abbia deciso di approcciare le
tecnologie server-side mediante questa guida. L'unica cosa che occorre sapere a questo punto
è che ogni pagina che realizzeremo in seguito dovrà contenere come prima riga di codice la
seguente:
<%@ LANGUAGE = JScript %>
che comunque non verrà mai omessa dagli esempi. Come riferimento a JScript va presa in
considerazione la guida ufficiale liberamente distribuita da Microsoft, la cui versione italiana
può essere scaricata dal lettore all'indirizzo www.sauronsoftware.it/javascript.
1.2 - Questione di server
Il primo elemento da prendere in considerazione quanto si sviluppano degli script client-side è
indubbiamente il browser del potenziale utente. Nel caso di ASP e delle tecnologie server-side,
invece, il primo fattore a dover essere preso in considerazione è il server sul quale bisognerà
rendere disponibili le proprie realizzazioni. Non tutti i server supportano ASP, anzi, solo una
minoranza di quelli diffusi lungo la superficie terrestre offre supporto per questa tecnologia.
Sostanzialmente a supportare ASP sono solo i server firmati Microsoft (tranne eccezioni), come
i server IIS in esecuzione su sistemi Windows NT. Questo significa che, per vedere funzionare
quanto sarà realizzato a seguire all'interno di questa guida, è necessario trovarsi un server
Web in grado di supportare adeguatamente ASP. Moltissimi servizi di hosting a pagamento
garantiscono il supporto di ASP, mentre sono ben pochi gli spazi gratuiti che facciano
altrettanto. Per mettere il lettore in grado di sperimentare on line i propri risultati si consigliano
i seguenti servizi in grado di offrire gratuitamente ciò che si sta cercando:
asp4free.ravenna2000.it, www.mso.it , www.domaindlx.com e www.atfreeweb.com.
1.3 - Eseguire le pagine ASP in locale
Si presume che il lettore, prima di provare i risultati on line, sia intenzionato a verificare la
funzionalità delle pagine in locale sulla propria macchina. Per far ciò, purtroppo, non basta
aprire con il browser un file con estensione .asp residente sul proprio hard disk, in quanto le
pagine ASP hanno bisogno, per l'appunto, di essere prima elaborate da un server. Per risolvere
questo problema gli utilizzatori di Windows 98 e successivi hanno la possibilità di installare
all'interno del loro sistema operativo un piccolo server in grado di interpretare in maniera
corretta le pagine ASP. Inserendo infatti nel proprio lettore CD ROM il CD di Windows è
possibile installare il Personal Web Server (abbreviato PWS). Al termine dell'installazione l'hard
disk conterrà una nuova cartella (inetpub) e sul desktop sarà presente una nuova icona
sottotitolata Pubblica. I propri file .asp andranno a questo punto resi disponibili a partire dalla
cartella inetpub\wwwroot\ e, dopo aver avviato il PWS mediante la schermata presentata al
doppio click sulla nuova icona, sarà possibile visualizzarle all'interno del browser a partire
dall'indirizzo http://localhost/.
Per guidare il lettore in questo procedimento si prenda il considerazione il seguente esempio
effettuato con una comune pagina HTML. Si realizzi quindi un documento HTML e lo si vada a
salvare con il nome prova.htm nella cartella inetpub\wwwroot. Si avvii ora il PWS (se non è già
avviato) e si apra il browser Web preferito. Nella barra degli indirizzi del browser digitare
http://localhost/prova.htm. Se la procedura è stata eseguita in maniera corretta il lettore
dovrebbe essere ora in grado di visualizzare la pagina HTML realizzata all'interno del browser.
La stessa procedura dovrà essere osservata in seguito per visualizzare off line le pagine ASP
che verranno realizzate. La cartella inetpub\wwwroot è accessibile in tre diverse maniere:
http://localhost (come già indicato), http://127.0.0.1 e http://nome_della_propria_macchina.
Il nome della propria macchina può essere impostato da pannello di controllo > Rete >
Identificazione. Se si modifica il nome in rete della propria macchina sarà necessario riavviare
il sistema.
1.4 - La prima pagina ASP
E' ora finalmente possibile sperimentare la creazione di una prima pagina ASP di prova. Si salvi
il seguente codice in un file nominato primapagina.asp e quindi si provi a pubblicarlo e ad
eseguirlo on line o in locale, come suggerito nel paragrafo precedente.
<%@ LANGUAGE = JScript %>
<html>
<head><title>La mia prima pagina ASP</title></head>
<body>
Questa è una pagina asp!<br><br>
<%
var d = new Date();
var g = "" + d.getDate() + "/" + (d.getMonth()+1) + "/" + d.getYear();
Response.Write("Per il server oggi è il " + g);
%>
</body>
</html>
Eseguendo dal browser questa pagina il lettore potrà ammirare il risultato prodotto. Si provi ad
aprire il codice HTML con l'apposita opzione del proprio browser: sarà possibile vedere come
esso si discosti dal reale codice del file .asp. Per l'esattezza, infatti, tutto quello contenuto tra
<% e %> non verrà di fatto mostrato. Al suo posto sarà presente la semplice frase "Per il
server oggi è il " seguita dalla data odierna, come se si trattasse di una comune pagina HTML
dove manualmente sia stata inserita quella specifica data. Il lettore provi ora a salvare in un
secondo file lo stesso codice di sopra, cambiandone però il nome in primapagina.htm. Il
browser mostrerà il codice che nell'esempio precedente veniva invece eseguito.
Dal piccolo esempio presentato si evincono tre aspetti fondamentali di ASP.
Per prima cosa si impara che i file con estensione .asp vengono in qualche maniera elaborati
dal server prima di essere inviati al browser. Il server, infatti, processa questo tipo di file alla
ricerca di script per lui eseguibili. Solo l'output di ogni singolo script viene inserito nel risultato
finale inviato al browser.
Come seconda cosa si apprende il significato, in una pagina ASP, dei due tag speciali <% e
%>. Questi, infatti, servono per racchiudere gli script che il server dovrebbe essere in grado di
interpretare. Tutto quello che sarà contenuto all'interno di questi marcatori verrà inteso dal
server come codice realizzato nel linguaggio di scripting specificato alla prima riga (nel nostro
caso JScript). Se questo codice dovesse contenere errori l'esecuzione dello script viene
interrotta e l'analisi della pagina bloccata. Nell'output HTML sarà presente una descrizione
dell'errore.
Infine si può capire che ASP mette a disposizione del linguaggio di scripting utilizzato una serie
di comandi aggiuntivi, come lo è nel nostro caso Response.Write(), il cui utilizzo appare
evidente. Nel corso di questa guida saranno analizzati tutti questi "comandi speciali" e si
comprenderà la loro utilità e il loro scopo.
Capitolo 2 - Gli elementi di ASP
2.0 - Fare scripting con ASP
ASP permette di integrare due tipi di elementi distinti all'interno di una pagina: elementi di
scripting, che saranno eseguiti sul server, ed elementi di testo semplice, che verranno invece
inviati inalterati al client senza essere processati dal server. Gli elementi di scripting sono
distinti dal resto della pagina che li contiene in quanto racchiusi tra i due tag speciali <% e
%>. Questa non è comunque l'unica maniera di includere uno script in una pagina ASP. E'
altresì possibile usare una sintassi del tipo:
<script language="JScript" runat="server">
// corpo dello script
</script>
La sintassi utilizzata in questo caso ricorda molto da vicino quella necessaria per inserire script
client-side in una qualsiasi pagina HTML. L'unica cosa che rende distinta la dichiarazione di uno
script di questo tipo da quella relativa allo scripting client-side risiede nell'attributo
runat="server" aggiunto al classico tag <script>. Il vantaggio dell'utilizzare questa sintassi sta
nel fatto di poter selezionare l'utilizzo di più linguaggi di scripting all'interno di una stessa
pagina, a differenza di <% e %> che invece possono inglobare solamente script realizzati con
il linguaggio specificato alla prima riga del codice (<%@ LANGUAGE = JScript %>, ad
esempio) oppure con il linguaggio predefinito nel caso di assenza di questa riga (solitamente
questo è VBScript). Non si fermano comunque qui le differenze delle due distinte sintassi. Oltre
ad una differenza che non tratteremo relativamente all'ambito di visibilità delle variabili, c'è da
dire che <% e %>, per quanto limitati dal fatto di poter contenere script di un solo linguaggio,
hanno una migliore integrazione con le parti di testo semplice.
In ogni caso i due elementi costitutivi di una pagina ASP non sono separati in due parti distinte
del codice, anzi, spesso si ritrovano profondamente mescolati tra di loro. Per rendere meglio il
concetto si consideri la seguente pagina ASP:
<%@ LANGUAGE = JScript %>
<html>
<head><title>Prova ASP</title></head>
<body>
<% for (i=1;i<=10;i++) { %>
<b>Riga numero <%=i%></b><br>
<% } %>
</body>
</html>
Questa pagina si limita a stampare, in output HTML, 10 righe numerate. Si osservi però la
struttura mediante la quale è stato realizzato il tutto: codice JScript eseguibile sul server e
codice HTML da inviare al browser si autocompenetrano, e questo è possibile proprio grazie
alle caratteristiche offerte dall'uso di <% e %>. Si noti poi anche l'utilizzo della forma
abbreviata <%=i%>, del tutto equivalente a <% Response.Write(i); %>, ma molto più
semplice e veloce da digitare e da rileggere in seguito, specie in questi casi di codice HTML
intervallato da parti JScript.
2.1 - Server Side Include
Questa panoramica iniziale su ASP non sarebbe completa senza accennare alla libreria dei
comandi Server Side Include (SSI) e al più importante di essi: #include. I comandi SSI si
presentato a prima vista come dei semplici commenti HTML, se non per il fatto che rispettino
sempre la seguente sintassi:
<!--#nome_comando attributo="valore"-->
I comandi aderenti a questa sintassi, al pari degli script, vengono valutati dal server prima di
essere "girati" al client. Qualora il server dovesse riuscire a riconoscere un comando per lui
appetibile lo eseguirà, e solo l'output di questo comando sarà trasmesso al browser del
visitatore della pagina. Il comando SSI più utilizzato in ambito ASP è, come già accennato,
#include. Questo comando si comporta in maniera analoga all'omonima direttiva per il
compilatore di C++:
<!--#include file="menu.htm"-->
Questa riga di codice verrà automaticamente rimpiazzata dal server con il contenuto del file
menu.htm residente nella stessa cartella che contiene la pagina ASP in questione. L'utilità è
evidente: grazie ad #include non sarà più necessario copiare lo stesso codice in più pagine in
quanto questo potrà essere tranquillamente conservato in un unico file esterno. Una modifica
su questo file, poi, si rifletterà automaticamente su tutte le pagine che lo includono. Le
applicazioni base sono due, una relativa alla parte HTML di una pagina ASP, l'altra relativa alla
parte di puro scripting. Nel primo caso, infatti, sarà possibile ripetere lo stesso template HTML
all'interno di tutte le pagine che lo includono. Si pensi ad esempio al classico caso di un menù
per un sito Web: se lo stesso menù deve presentarsi su 200 pagine distinte sarà sicuramente
più conveniente includerlo in esse mediante SSI che non con il classico copia-incolla del codice.
Un'aggiornamento sul file contenente questo menù, poi, aggiornerà automaticamente il menù
di tutte le pagine del sito. La praticità è notevole. #include, comunque, non si limita ad
integrare solo HTML: con esso è possibile inserire ogni tipo di file all'interno di una pagina, e
quindi anche script server-side contenenti magari delle funzioni standard da utilizzare in più
pagine. Torneremo poi sull'argomento più avanti, per ora questo è tutto quello che occorre
sapere.
2.2 - Struttura di una pagina ASP
Riassumendo allora quanto detto sino ad ora possiamo concludere che una classica pagina ASP
può essere formata da quattro distinti elementi: la dichiarazione del linguaggio di scripting
utilizzato (se presente deve necessariamente essere la prima riga del file), i comandi SSI, gli
script eseguibili sul server e le parti "semplici" da inviare invariate al client. Imparare a
programmare con ASP significa avere padronanza di questi elementi e saperli dosare in un
giusto mix.
2.3 - La fase di scripting e gli oggetti built-in
La parte sulla quale si focalizzerà maggiormente l'attenzione in questa guida è quella relativa
allo scripting. Le possibilità offerte da ASP sono molteplici. Nel caso dello scripting client-side il
browser mette a disposizione del linguaggio diversi oggetti relativi a quel dato ambito, come ad
esempio l'oggetto navigator per rilevare quale sia il browser in uso. Analogamente ASP mette a
disposizione del linguaggio di scripting 5 diversi oggetti che, grazie ai loro metodi, consentono
il controllo del server e della comunicazione server-client. Questi 5 oggetti particolari, che sono
detti oggetti built-in, vengono automaticamente istanziati all'inizio dell'esecuzione di ogni
pagina. Questi sono:
Response
gestisce l'output del codice
Request
gestisce l'input del codice
Server
assolve a funzionalità relative all'ambito di esecuzione
Session
gestisce le sessioni degli utenti
Application
permette di condividere informazioni tra più utenti di una stessa
applicazione
2.4 - Gli oggetti ActiveX
Il linguaggio di scripting e gli oggetti built-in nulla potrebbero in ASP senza poggiarsi su oggetti
esterni, che in questo ambito sono definiti secondo la tecnologia ActiveX, in grado di assolvere
a quei compiti che non vengono svolti in maniera nativa. Né JScript né VBScript, ad esempio,
permettono di per sé di leggere o di scrivere file di testo sul server, così come non forniscono
nella loro definizione base la connettività con i database. Questi compiti, quindi, vengono svolti
attraverso il ricorso ad oggetti esterni di tipo ActiveX. Questi oggetti sono da considerarsi a se
stanti e non facenti parte dell'architettura di ASP. Molti di questi, comunque, sono considerati
standard e vengono installati su una macchina insieme al server stesso. Saranno questi gli
oggetti ActiveX ai quali faremo riferimento nel corso della guida. Per ogni compito di natura
non generale sarà poi sempre possibile rintracciare un componente ActiveX in grado di
renderne possibile l'attuazione, o sarà addirittura possibile realizzarne uno apposito in C++ o
in Visual Basic, volendo. Questa è l'ideologia su cui si basa ASP. Il linguaggio di scripting altro
non è che una manierà di coordinare e rendere efficaci gli oggetti built-in e i componenti
ActiveX. Al momento di applicare le nozioni ricevute ci si accerti sempre degli oggetti ActiveX
disponibili sul server che si sta utilizzando, non è detto che questi siano quelli standard.
2.5 - Una pagina esemplificativa
La pagina ASP qui di seguito rappresentata vuole essere di esempio a quanto detto in questo
capitolo della guida. Non si pretende che il lettore conosca l'esatto utilizzo degli oggetti ai quali
si farà ricorso, che saranno invece ampliamente trattati di seguito. L'intenzione è quella di
realizzare un contatore da includere poi mediante il comando #include di SSI all'interno di una
qualsiasi pagina ASP.
Si realizzi la seguente pagina ASP, che potrà essere denominata index.asp:
<%@ LANGUAGE = JScript %>
<html>
<head><title>Benvenuto!</title></head>
<body>
<center>
<h1>Benvenuto nel mio sito!</h1>
Sei il visitatore numero
<b><!--#include file="contatore.inc"--></b>
</body>
</html>
Come si può notare index.asp include in se stessa il file contatore.inc. L'estenzione .inc è quella
raccomandata per tutti i file realizzati al solo scopo di essere inclusi e non in grado di essere
eseguiti in maniera a se stante. Questa è solo una convenzione alla quale può risultare pratico
sottostare. Cambiando l'estensione del file il risultato rimane comunque lo stesso. Si vada ora
a realizzare un semplice file di testo nominato conteggio.txt e contenente il solo numero 0. Si
salvi infine il codice sotto riportato come contatore.inc:
<%
var file_di_testo = Server.MapPath("conteggio.txt");
var fobj = new ActiveXObject("Scripting.FileSystemObject");
var in_stream = fobj.OpenTextFile(file_di_testo);
var stringa_cifra = in_stream.ReadLine();
in_stream.Close();
var intero_cifra = parseInt(stringa_cifra);
intero_cifra++;
var out_stream = fobj.CreateTextFile(file_di_testo);
out_stream.WriteLine(intero_cifra);
out_stream.Close();
Response.Write(intero_cifra);
%>
Si raccolgano i tre file in una sola cartella sul server che abbia abilitati i permessi in
lettura/scrittura. Con IIS o PWS solitamente tutte le cartelle godono di questo vantaggio, ma
alcuni spazi gratuiti restringono i permessi ad una sola cartella, nominata il più delle volte dati.
Si leggano le FAQ relative al proprio spazio Web per saperne di più in merito. Si carichi quindi
con il browser la pagina index.asp e la si aggiorni più volte per vedere il contatore
incrementarsi. Si tornerà sull'architettura di questo codice non appena saranno trattati in
maniera esaustiva gli oggetti build-in e l'ActiveX denominato Scripting.FileSystemObject.
Capitolo 3 - L'oggetto built-in Response
3.0 - Scopo ed utilizzo dell'oggetto Response
Tra la famiglia degli oggetti built-in di ASP (cfr. capitolo precedente) Response, come il nome
lascia ad intendere, è l'oggetto dedicato alla formattazione e all'invio di un qualche output al
client. La sintassi necessaria per utilizzare l'oggetto Response è quella classica degli oggetti di
JavaScript, dove il nome dell'oggetto è separato dal nome della proprietà o del metodo che sta
venendo invocato con un singolo punto.
Response.nomeProprietà
Response.nomeMetodo(parametro1,parametro2,...)
Response.nomeCollezione("chiave")
Ecco schematicamente cosa fornisce l'oggetto Response:
Proprietà
Buffer
Booleano, specifica se il buffer è attivo o meno
CacheControl
Si utilizza per indicare ai proxy di aggiungere o meno l'output
della pagina alla propria cache
CharSet
Aggiunge all'header della pagina la specifica di un particolare
charset indicato
ContentType
Specifica il content type da inviare secondo le norme del
protocollo HTTP al client
Expires
Indica la durata della pagina all'interno della cache del browser
ExpiresAbsolute
Indica la data di scadenza della pagina archiviata nella cache del
browser
Status
Specifica la linea di stato restituita al client dal server
Metodi
AddHeader
Aggiunge un header specifico alla pagina, di cui è possibile
settare nome e valore
AppendToLog
Aggiunge una riga al Log del server
BinaryWrite
Invia all'output il parametro passato senza applicare conversioni
di charset
Clear
Ripulisce l'output, ma solo se bufferizzato
End
Interrompe l'interpretazione della pagina, e restituisce l'output
bufferizzato
Flush
Invia all'istante l'ouput bufferizzato
IsClientConnected
Verifica durante l'esecuzione della pagina se il client è ancora
connesso
Pics
Aggiunge un valore al PICS label dell'header
Redirect
Invia un messaggio di redirect al browser, spostandolo su un
differente URL
Write
Scrive sull'output della pagina
Collezioni
Cookies
Setta e recupera i valori relativi ai cookie
Non è intenzione di questa guida prendere in analisi sin da ora tutte le proprietà ed i metodi
sopra indicati. Molti di essi saranno trattati separatamente nei capitoli seguenti, come ad
esempio la collezione Cookies. Per il momento ci si accontenterà di prendere in analisi solo le
proprietà e i metodi di uso più ricorrente.
3.1 - Il metodo Response.Write
Nei due capitoli precedenti già si è accennato all'utilizzo di questo metodo, utile per inviare un
output di testo al client. La giusta sintassi di utilizzo è la seguente:
<% Response.Write(stringa); %>
Che comunque rimane del tutto equivalente alla più semplice da "embeddare" tra i codici
HTML:
<%=stringa%>
Il parametro stringa, come indica il nome stesso, può essere una stringa o comunque un tipo
di dato che abbia rappresentazione in stringa. Con JScript è possibile concatenare più stringhe
con l'operatore + direttamente all'interno di Write:
nome = "Pippo";
Response.Write("Ciao " + nome + ", io sono un output!");
3.2 - Il metodo Response.End
Il metodo End conclude l'esecuzione della pagina e invia al client gli output non ancora inviati.
Tutto quello che segue consequenzialmente una chiamata a Response.End non verrà preso in
considerazione, sia esso codice di scripting o semplice output di altro tipo. Si provi il seguente
esempio:
<%@ LANGUAGE = JScript %>
<%
Response.Write("IO VENGO SCRITTO");
Response.End;
Response.Write("IO NON VENGO SCRITTO");
%>
NEMMENO IO VERRO' SCRITTO!
3.3 - Il metodo Response.Redirect
Questo metodo segnala al browser del client di spostarsi su un altro URL. Tutto quello che
seguirà consequenzialmente una chiamata a Redirect non verrà né interpretato dal server, né
comunque ascoltato dal client che sarà passato a fare richiesta ad un altro URL. I percorsi
indicati possono essere relativi o assoluti. La sintassi è semplice. l'URL deve essere espresso
come una classica stringa. Si prenda a modello il seguente esempio:
Response.Redirect("pagina2.htm");
3.4 - La proprietà Buffer e gli output bufferizzati
La proprietà Buffer di Response gestisce una cosa assai importante nell'utilizzo di una
tecnologia server-side come ASP. La proprietà, accessibile sia in lettura che in scrittura,
rappresenta un valore Booleano (true o false) che sta ad indicare se l'output dovrà essere
bufferizzato o meno. Di default questa proprietà è settata a false. Questo significa che
normalmente gli output non saranno bufferizzati, e perciò verranno inviati al client man mano
che saranno generati. Impostandola invece su true si attiverà la bufferizzazione dell'output. In
questa maniera ogni dato inviato all'output (ad esempio tramite Reponse.Write) non sarà
girato al client il più presto possibile, ma sarà conservato in un buffer temporaneo, per essere
poi spedito al termine dell'esecuzione oppure su comando esplicito del codice (con il metodo
Flush). Questo ha vantaggi e svantaggi. Un output non bufferizzato dà l'impressione di essere
molto più veloce, specie relativamente a lunghe pagine HTML, e difatti l'utilizzo di output non
bufferizzati è la prassi per la generazione dinamica di pagine in HTML. Attivare la
bufferizzazione permette però di poter gestire assai più elegantemente il possibile output,
intervenendo più volte su di esso. Di default l'output è spedito non appena generato, per
questo non è più possibile intervenire su di esso. Se il codice ha postato la stringa "Ciao" al
client non potrà più tornare sui propri passi cancellando quanto fatto: quando il codice tenterà
di farlo probabilmente l'utente starà già visualizzando il messaggio sul proprio monitor. Con il
buffer attivato, invece, un comando di scrittura su output non implica automaticamente l'invio
del medesimo al client. Sarà dunque possibile intervenire a più riprese su header e corpo
dell'output, con alcuni metodi speciali che hanno significato sono in questo ambito:

Response.Clear - ripulisce l'output accumulato nel buffer

Response.Flush - invia all'istante i dati accumulati nel buffer al client, senza attendere
la fine dell'esecuzione
Alcuni metodi di uso comune in presenza di un output bufferizzato si comportano in maniera
leggermente diversa dall'usuale:

Response.End - oltre ad interrompere l'esecuzione in questo caso invia al client
l'output cumulato nel buffer

Response.Redirect - può essere utilizzato anche dopo aver già scritto tutti gli header
nell'output. In questo caso ripulirà il buffer e inserirà il comando di redirect al posto dei
dati cumulati
3.5 - Le proprietà Status e ContentType
Status e ContentType sono altre due proprietà che vanno ad influenzare l'header della risposta
inviata dal server al client. Per quanto possibile si è cercato di non addentrarsi nei meandri del
protocollo HTTP, la cui conoscenza faciliterebbe la comprensione del capitolo, tuttavia ora
diviene necessaria una piccola immersione nelle sue specifiche. Non se ne risentano i lettori più
attenti, si sta cercando solamente di essere semplici e facilmente comprensibili.
Il protocollo HTTP altro non è che un insieme di regole che descrive come debba avvenire un
dialogo tra un client ed un server. L'esempio più sfruttato da manuali e guide che tentano di
illustrare le funzionalità di tale protocollo è quello della telefonata, e qui non si vuole venir
meno a questa consolidata tradizione. Quando si telefona ad un amico o ad un conoscente si
ricorre involontariamente all'uso di un certo "protocollo", o meglio, all'uso di un insieme di
regole. Ad esempio chi risponde dice sempre "pronto" per segnalare la sua presenza e la sua
disponibilità dall'altra parte del cavo, e immediatamente chi ha effettuato la chiamata
risponderà salutando e dichiarando la propria identità. Solo dopo queste operazioni preliminari
si passerà al colloquio, ossia al vero scopo della telefonata. Il protocollo descritto per la
"telefonata tipo" non è comunque rigido, le cose tra uomini potrebbero svolgersi assai
differentemente e probabilmente ci si continuerebbe a capire lo stesso. Sui computer,
naturalmente, non esiste flessibilità, per cui tutti i server e tutti i client devono rispondere
secondo le regole del protocollo HTTP, che definisce appunto il funzionamento di una
"chiamata". Se un client o un server dovessero "rispondere male" la controparte in rete
riaggancierebbe subito la linea. Così come per la telefonata tra persone, dove il colloquio vero
e proprio viene fatto precedere da un header introduttivo, il protocollo HTTP prevede uno
scambio di informazioni iniziali tra il client ed il server. Con le pagine ASP ci si sta occupando
della gestione del server, quindi si dovrà fare la parte di colui che risponde. Il client
solitamente si collega ad un server mediante protocollo HTTP indicando un URL di suo
interesse. Il compito sel server è quello di recuperare, se esiste, il file puntato dall'URL e di
spedirlo al client, facendolo però precedere da una serie di informazioni inerenti l'esito della
richiesta e la natura del file. Status e ContentType controllano proprio questi ultimi due aspetti
citati.
Status inserisce nell'header una riga che specifica l'esito della richiesta. Nel protocollo HTTP
questo viene identificato da una serie di tre numeri. Ad esempio 200 significa "ok, ho trovato il
file, adesso arriva", mentre 404 significa "mi dispiace, il file specificato dall'URL che mi hai
passato non esiste". Con Status è possibile alterare questa parte dell'header:
Response.Status = "401 Unauthorized";
Questa riga dice al client che non si ha l'autorizzazione per ricevere il file richiesto. In questa
sede non saranno trattati tutti i possibili status di un header, per chi fosse interessato
all'argomento si consiglia di cercare in rete le specifiche del protocollo HTTP.
ContentType definisce la natura del file che si sta per inviare al client. I browser non sono
soliti riconoscere i file in base asll'estensione che porta il loro nome, quanto piuttosto rispetto
alla descrizione fornita dal server su di essi. ContentType permette di controllare questa
descrizione. Con ASP, infatti, non si realizzano solo ed esclusivamente semplici output HTML,
ma con l'ausilio di giusti ActiveX esterni è possibilissimo mandare in output ad esempio delle
immagini (tipico caso dei contatori ad immagini), dei suoni o comunque altri tipi di output
differenti dal classico "text/html" di default. Si provi ad eseguire la seguente pagina ASP:
<%@ LANGUAGE = JScript %>
<% Response.ContentType = "text/plain"; %>
<html>
<head>
<title>E' una pagina HTML?</title>
</head>
<body>
Cosa vedi?
</body>
</html>
Come è possibile vedere l'HTML compreso nell'output invece di venire interpretato dal browser
verrà stampato all'interno della finestra! Questo perchè abbiamo fatto dire al client dal server
che la pagina che seguiva era "text/plain", ossia semplice testo, e il browser il semplice testo
lo prende e lo stampa senza svolgere ulteriori compiti. Utilizzando Microsoft Internet Explorer
non sempre avviene ciò, questo perchè la Microsoft ha scelto di far parlare al suo browser un
linguaggio diverso dal resto di Internet, ma non scendiamo in inutili polemiche.
Se si voleva lanciare come output un JavaScript era possibile farlo utilizzando la dicitura
"application/x-javascript". Sono davvero tanti i possibili MIME TYPE (così vengono chiamate
queste stringhe che indicano la tipologia del file) che un browser può riconoscere o che ASP,
con le necessarie estensioni, può generare. Qui ne citiamo solo alcuni che potranno tornare
utili nelle applicazioni più comuni:
Testuali
text/html
Settato di default, è il comune MIME TYPE di una pagian
HTML
text/plain
File di testo semplice
text/rtf
File in formato Rich Text Format
text/xml
File in formato XML
Immagini
image/gif
Immagine GIF
image/jpeg
Immagine JPEG
image/png
Immagine PNG
Audio
audio/midi
File MIDI
audio/x-wav
File audio in formato WAVE
audio/x-realaudio
File audio in formato RealAudio
Altro
application/xjavascript
File JavaScript
application/zip
File WinZip
Capitolo 4 - utilizzo degli oggetti ActiveX
4.0 - Introduzione agli ActiveX
Nelle pagine precedenti si è già accennato agli oggetti basati sulla tecnologia ActiveX (cfr.
Capitolo 2). In questo capitolo si intende introdurre il lettore all'utilizzo e alla comprensione di
questi oggetti. Alcuni ActiveX, scelti tra i più diffusi e standardizzati, verranno presi in esame
all'interno della guida stessa.
Come già detto ASP, insieme ai suoi linguaggi di scripting, non fornisce nativamente funzioni
avanzate. Con puro codice JScript, infatti, non è possibile accedere ad un database, oppure
scrivere un file di testo, oppure ancora inviare una e mail attraverso un server di posta
elettronica o collegarsi ad un altro PC sparso per il mondo alla ricerca di informazioni. Per
questo motivo molto di ASP è basato sui suoi componenti esterni, l'utilizzo dei quali rende
possibile tutte le cose appena citate. Sostanzialmente sotto il nome di ActiveX vanno una serie
di tecnologie che permettono ad un software o ad una sua parte di interagire con altre parti
esterne, indipendentemente dal linguaggio con il quale esse sono state realizzate. ActiveX, a
sua volta, è un sottoinsieme delle classi COM (Component Object Model).
In questa guida non sarà illustrata la creazione di un componente ActiveX. Se si è interessati a
ciò è bene far riferimento a manuali avanzati di un qualche linguaggio appartenente alla
famiglia "Visual", sempre di Microsoft. Gli ActiveX più efficienti sono solitamente realizzati con
Visual C++. Qui ci si limiterà solo all'uso di essi all'interno dei documenti ASP.
4.1 - Utilizzo di un ActiveX
Con JScript richiamare un ActiveX è estremamente semplice, mediante l'utilizzo dell'oggetto
ActiveXObject. Il compito di questo oggetto è quello di attivare e restituire un riferimento
proprio ad un componente ActiveX. La sintassi necessaria ricalca la seguente:
var rif_oggetto = new ActiveXObject("nomeserver.nometipo"[,"posizione"]);
I parametri passati all'oggetto vanno così interpretati:

nomeserver è il nome dell'applicazione che fornisce l'oggetto. Questo parametro è
obbligatorio

nometipo è il nome della classe dell'oggetto da attivare. Anche questo parametro è
obbligatorio

posizione indica il nome del server nella rete in cui l'oggetto deve venire creato. Questo
parametro è facoltativo
Una volta creato un oggetto mediante l'utilizzo di ActiveXObject sarà possibile usufruire di esso
utilizzando il riferimento restituito:
rif_oggetto.proprietà
rif_oggetto.metodo(eventuali parametri)
4.2 - Scripting.FileSystemObject
Il primo oggetto ActiveX al quale rivolgeremo la nostra attenzione sarà
Scripting.FileSystemObject, molto probabilmente l'oggetto ActiveX più diffuso ed utilizzato
nelle pagine ASP insieme a quelli della famiglia ADODB.
Scripting.FileSystemObject estende il linguaggio di scripting utlizzato fornendolo di una
semplice interfaccia utile per interagire con il File System del server. In poche parole grazie a
questo ActiveX è possibile leggere e scrivere file sull'hard disk del server, creare cartelle,
eliminare file che non servono più, ottenere la lista di tutti i file contenuti in una cartella,
gestire file di testo o binari e tanto altro. Inoltre a questo oggetto principale solo collegate
anche altre classi di oggetti utili per descrivere i file, le cartelle, i drive e gli stream. Nel corso
della guida ci troveremo più volte davanti al loro utilizzo e, dunque, avremo occasione di
approfondire l'argomento.
Davvero innumerevoli sono i metodi messi a disposizione da Scripting.FileSystemObject:
Metodi
BuildPath
Aggiunge un nome alla fine di un percorso esistente
CopyFile
Copia uno o più file da una posizione ad un'altra
CopyFolder
Copia una cartella da una posizione ad un'altra
CreateFolder
Genera una nuova cartella
CreateTextFile
Crea un file di testo e restituisce un oggetto TextStream
utile per lavorare con il file creato
DeleteFile
Cancella un file
DeleteFolder
Cancella una cartella
DriveExists
Restituisce un booleano che indica se il drive specificato
esiste
FileExists
Restituisce un booleano che indica se il file specificato
esiste
FolderExists
Restituisce un booleano che indica se la cartella specificata
esiste
GetAbsolutePathName Restituisce un percorso completo a partire da uno ambiguo
GetBaseName
Restituisce il nome di un file privo dell'estensione
GetDrive
Restituisce un oggetto Drive relativo all'unità di un
percorso specificato
GetDriveName
Restituisce una stringa contenente il nome dell'unità di un
percorso specificato
GetExtensionName
Restituisce una stringa contenente l'estensione di un file
specificato
GetFile
Restituisce un oggetto File relativo al percorso specificato
GetFileName
Restituisce l'ultimo componente del percorso specificato che
non fa parte della specifica di unità.
GetFolder
Restituisce un oggetto Folder relativo al percorso
specificato
GetParentFolderName
Restituisce una stringa contenente il nome della cartella
principale dell'ultimo componente del percorso specificato.
GetSpecialFolder
Restituisce la cartella speciale specificata. Cartelle speciali
sono ad esempio quelle contenenti file temporanei, file di
sistema ecc.
GetTempName
Restituisce un nome di file o cartella generato in modo
casuale, utile per eseguire operazioni che richiedono un file
o una cartella temporanea
MoveFile
Sposta uno o più file in una diversa posizione
MoveFolder
Sposta una cartella in una diversa posizione
OpenTextFile
Apre il file specificato e restituisce un oggetto TextStream
che può essere utilizzato per leggere il file, scrivere nel file
o aggiungere testo alla fine del file
Proprietà
Drives
Un insieme di oggetti Drive disponibili nel computer locale
4.3 - Un esempio di utilizzo del File System
Vediamo ora come utilizzare praticamente Scripting.FileSystemObject attraverso un primo
semplice esempio:
<%@ LANGUAGE = JScript %>
<%
var fo = new ActiveXObject("Scripting.FileSystemObject");
var text_stream = fo.CreateTextFile(Server.MapPath("prova.txt"));
text_stream.WriteLine("Prova di scrittura su file di testo");
text_stream.Close();
%>
<a href="prova.txt">Ho appena creato un file di testo! Clicca!</a>
Forse non tutti i passaggi effettuati risulteranno chiari sin da subito, in quanto ancora non sono
stati visitati l'oggetto TextStream e l'oggetto built-in Server. L'esempio in questione,
comunque, utilizza l'ActiveX discusso per generare un file di testo nominato prova.txt che
viene salvato nella stessa cartella dove risiede lo pagina ASP. Il prossimo capitolo si occuperà
di approfondire la tematica della lettura/scrittura dei file di testo, intimamente legata con il File
System.
Capitolo 5 - Interazione con i file di testo
5.0 - ASP e i file di testo
Grazie all'ActiveX Scripting.FileSystemObject e all'oggetto TextStream ad esso correlato ASP
permette la lettura/scrittura di file di testo residenti sul disco rigido del server che mette in
esecuzione le pagine realizzate. Questa funzione risulta molto importante ed utile in tantissimi
servizi, di pari passo con la possibilità di accesso ai database che sarà analizzata in seguito.
Grazie ai semplici file di testo, infatti, è possibile realizzare piccole banche dati per conservare
dati ed informazioni, oppure è possibile conservare in maniera disaccoppiata i contenuti e
l'impostazione grafica delle pagine di un sito, in maniera tale da rendere più agevole un
intervento di qualsiasi tipo su di essi. Sapendo manipolare i file di testo diventa poi un gioco da
ragazzi realizzare tipici e diffusi servizi quali i sondaggi, i libri per gli ospiti, i forum e molto
altro. Tutto ciò sarà analizzato nel dettaglio nello svolgimento del capitolo prima e del resto
della guida poi.
5.1 - OpenTextFile, CreateTextFile e Server.MapPath
I due particolari metodi OpenTextFile e CreateTextFile dell'ActiveX Scripting.FileSystemObject,
come visto nel corso del capitolo precedente, restituiscono un riferimento ad un oggetto di tipo
TextStream, utile per la gestione sequenziale dei file di testo. Prima di passare all'analisi di tale
oggetto, quindi, scopriamo anzitutto come utilizzare in maniera corretta questi due metodi.
riferimento = oggetto.OpenTextFile(nomefile[, modoIO[, creazione[, formato]]]);
Questa è la sintassi "modello" per l'utilizzo di OpenTextFile. Sondiamone i parametri.
nomefile - parametro obbligatorio, si tratta di una stringa che identifica in maniera completa il
percorso del file di testo da aprire. Il più delle volte capita che non si conosca a priori il
persorso completo di un file. Si metta il caso, ad esempio, che si voglia aprire il file test.txt
posto nella stessa medesima cartella che contiene la pagina ASP in lavorazione. Poichè non è
possibile impostare percorsi relativi è altrettanto impossibile scrivere semplicemente
oggetto.OpenTextFile("test.txt"), visto che la stringa "test.txt" è priva di significato per il
metodo in questione. La sintassi giusta richiederebbe perciò un'espressione del tipo
"C:\inetpub\wwwroot\nomecartella\test.txt". Un tipo di espressione del genere, comunque,
sarebbe una vera e propria mina per la portabilità del codice: spostando la pagina ASP da una
cartella ad un'altra diverrebbe necessario cambiare ogni riferimento al file, e lo stesso
avverrebbe spostandosi da un server ad un altro, ad esempio dal proprio PWS di casa ad un
IIS su Internet. Per porre rimedio a ciò verrà usato un particolare metodo dell'oggetto built-in
Server, che ancora dobbiamo considerare nel suo insieme. Per ora, quindi, basti sapere che
Server.MapPath("test.txt") è in grado di restituire il persorso completo del file test.txt partendo
da un riferimento relativo. Per questo la sintassi corretta per aprire il nostro file di testo
diviene oggetto.OpenTextFile(Server.MapPath("test.txt")).
modoIO - parametro facoltativo, si tratta di un intero (o di una equivalente forma mnemonica
costante) in grado di specificare la modalità di apertura del file. Il valore 1 (ForReading) indica
che il file di testo viene aperto in sola lettura, il valore 2 (ForWriting) indica che il file di testo
viene aperto in sola scrittura, il valore 8 (ForAppending) indica che il file di testo può essere
scritto appendendo le nuove righe dettate in coda a quelle già presenti in esso (situazione
molto sfruttata nei file di log). Se non specificato è automaticamente 1.
creazione - parametro facoltativo, si tratta di un booleano (true o false) che indica come il
metodo debba comportarsi qualora il file di testo specificato non risulti esistente. Se il valore è
true il file di testo verrà generato automaticamente.
formato - parametro facoltativo, si tratta di una forma mnemonica costante in grado di
specificare il formato da utilizzare nella lettura/scrittura del file di testo. I valori ammessi sono:
TristateTrue, che indica l'utilizzo del formato Unicode, TristateFalse, che indica l'utilizzo del
formato ASCII e TristateUseDefault, che apre il file utilizzando l'impostazione predefinita di
sistema. Se omesso il file verrà aperto in modalità ASCII.
Non ci si deve comunque spaventare davanti a questa immane mole di dati: il più delle volte si
ricorre all'utilizzo del solo parametro obbligatorio nomefile.
Assai simile è il comportamento di CreateTextFile, che si utilizza secondo il modello sotto
fornito:
riferimento = oggetto.CreateTextFile(nomefile[, sovrascrittura[, unicode]]);
nomefile - parametro obbligatorio, del tutto equivalente a quanto detto rispetto all'omonimo
parametro obbligatorio di OpenTextFile.
sovrascrittura - parametro facoltativo, si tratta di un valore booleano che indica se è
possibile sovrascrivere file di testo già esistenti.
unicode - parametro facoltativo, si tratta di un valore booleano che stabilisce il formato di
codifica da utilizzare: true sta per il formato Unicode, mentre false per il formato ASCII. Se
omesso il file verrà codificato secondo il formato ASCII.
5.2 - L'oggetto TextStream
Ambo i metodi appena visti restituiscono un oggetto di tipo TextStream in grado di permettere
al meglio l'accesso sequenziale ad un testo (in inglese stream, appunto). Tramite i metodi e le
proprietà fornite da questo oggetto sarà possibile leggere, scrivere e muoversi dentro un file di
testo. La seguente tabella rappresenta schematicamente quanto offerto:
Metodi
Close
Read
Chiude e conclude l'accesso sequenziale ad un file
Accetta un parametro intero n
Legge e restituisce quindi n caratteri dal file di testo manovrato •
dall'oggetto di invocazione
ReadAll
Legge l'intero contenuto del file e lo restituisce come stringa
•
ReadLine
Legge un'intera riga del file fino al carattere di "nuova riga"
escluso e la restituisce come stringa
•
Skip
Accetta un parametro intero n
Durante la lettura di un file salta n caratteri e posiziona lo
stream sul carattere immediatamente successivo all'ultimo
ignorato
•
SkipLine
Salta ed ignora un'intera riga del file
•
Write
Accetta un parametro intero str
Stampa quindi il contenuto di str all'interno del file
•
WriteBlankLines
Accetta un parametro intero n
Scrive quindi nel file n righe vuote
•
WriteLine
Accetta un parametro intero str
Stampa quindi il contenuto di str all'interno del file e lo fa
seguire da un carattere di "nuova riga"
•
Proprietà
AtEndOfLine
AtEndOfStream
Restituisce true se il puntatore dello stream è posizionato
immediatamente prima dell'indicatore di fine di riga, restituisce •
false in caso contrario
Restituisce true se il puntatore dello stream è posizionato
immediatamente prima dell'indicatore di fine file, restituisce
•
false in caso contrario
Column
Restituisce il numero di colonna della posizione di carattere
corrente del file
•
Line
Restituisce il numero di linea della posizione di carattere
corrente del file
•
• METODO O PROPRIETA' UTILIZZABILE SOLO IN FASE DI LETTURA DEL FILE DI TESTO
• METODO O PROPRIETA' UTILIZZABILE SOLO IN FASE DI SCRITTURA DEL FILE DI TESTO
5.3 - Lettura di un archivio basato su file di testo
Una volta a conoscenza degli "ingredienti" messi a disposizione dall'oggetto TextStream è ora
necessario imparare a realizzare delle buone miscele in grado di produrre utili risultati.
L'esempio che segue utilizza un file di testo per memorizzare una classica rubrica ed un file
ASP per la lettura e l'intabellamento su output HTML di essa.
Per prima cosa si realizzi il file di testo rubrica.txt:
[email protected]$http://www.pinco.it
[email protected]$http://utenti.tripod.it/pallino
[email protected]$http://go.to/caio
[email protected]$http://www.tizio.net
Questo file di testo reca al suo interno quattro righe, che possono essere aumentate o
diminuite a proprio piacimento, ognuna delle quali riporta tre informazioni relative ad un
singolo utente, secondo la seguente sintassi:
nome_utente$email_utente$homepage_utente
La seguente pagina ASP attinge i dati da questo archivio e li propone all'interno di una
semplice tabella HTML, con tanto di link ipertestuale relativamente alle ultime due informazioni
di ogni riga:
<%@ LANGUAGE = JScript %>
<html>
<head>
<title>Rubrica in ASP su file di testo</title>
</head>
<body>
<%
// Richiama Scripting.FileSystemObject
var fso = new ActiveXObject("Scripting.FileSystemObject");
// Apr il file di testo in lettura
var stream = fso.OpenTextFile(Server.MapPath("rubrica.txt"));
%>
<table border="1">
<tr>
<td><b>NOME</b></td>
<td><b>E MAIL</b></td>
<td><b>HOME PAGE</b></td>
</tr>
<%
while (!stream.AtEndOfStream) {
// Legge una singola riga
aux = stream.ReadLine();
// Divide la riga ad ogni occorrenza di $
arr = aux.split("$");
%>
<tr>
<td><%=arr[0]%></td>
<td><a href="mailto:<%=arr[1]%>"><%=arr[1]%></a></td>
<td><a href="<%=arr[2]%>" target="blank"><%=arr[2]%></a></td>
</tr>
<%
}
%>
</table>
<%
// Chiude lo stream aperto in precedenza
stream.Close();
%>
</body>
</html>
I passi essenziali da comprendere sono 5:
1. Il file di testo rubrica.txt viene aperto ed un riferimento ad esso viene tenuto nella variabile
stream
2. Grazie all'utilizzo della proprietà AtEndOfStream (true solo quando il file è stato interamente
letto) il contenuto del ciclo while viene ripetuto tante volte quante sono le righe presenti nel
file di testo
3. Ogni riga viene sequenzialmente letta e divisa in un Array di tre stringhe grazie al metodo
Split("$") dell'oggetto String di JScript
4. I risultati vengono inviati su output HTML
5. Lo stream viene chiuso ricorrendo al metodo Close
Qualsiasi intervento si faccia ora nei confronti del file di testo rubrica.txt si vedrà il cambio
riflesso nella pagina HTML prodotta dal codice ASP. Gestire un archivio su file di testo è molto
più semplice che amministrare le stesse identiche informazioni già intabellate in un file HTML.
Sul file rubrica.txt, infatti, si potrebbero ipotizzare con un po' di sapienza delle aggiunte
automatiche o delle ricerche altrimenti impossibili o comunque complesse su un file HTML.
Torneremo su questi argomenti nei capitoli successivi.
Come esercitazione si provi per il momento ad utilizzare i metodi CreateTextFile e WriteLine
per generare automaticamente da pagina ASP un file di testo sul proprio hard disk.
Capitolo 6 - L'oggetto built-in Request
6.0 - Scopo e utilizzo dell'oggetto Request
L'oggeto built-in Request, il secondo di questo tipo sul percorso della guida, è il componente
dedicato al recupero delle informazioni di input messe a disposizione di una pagina ASP.
Maneggiando questo oggetto è possibile in particolar modo recuperare i contenuti dei moduli
HTML inviati in allegato alla richiesta della pagina dal client, leggere il contenuto dei cookie
precedentemente impostati, recuperare delle informazioni relative al tipo di connessione
remota ed altro. Insomma, con Request è possibile ottenere in fretta tutte quelle informazioni
fornite da un utente o relative ad un utente. Alla lettura/scrittura dei cookie sarà dedicato un
capitolo a parte, quindi in questo momento si istruirà il lettore all'utilizzo delle altre
caratteristiche elencate. La conoscenza e la capacità di buon uso dei form HTML sono
prerequisiti richiesti per la comprensione di molte delle cose che verranno dette.
Come per il resto degli oggetti bulit-in, l'utilizzo di Request come oggetto precostituito è
intuitivo e basato sulla classica nozione puntata:
Request.nomeProprietà
Request.nomeMetodo(parametro1,parametro2,...)
Request.nomeCollezione("chiave")
Schematicamente ecco i controlli messi a disposizione dall'oggetto Request:
Proprietà
TotalBytes
Specifica il numero totale di byte inviati dal client nel corpo della
richiesta. Di sola lettura
Metodi
BinaryRead
Restituisce i dati inviati dal client come parte della richiesta POST
Collezioni
ClientCertificate
Collezione dei certificati inviati dal client
Cookies
Collezione dei valori relativi ai cookie di libero accesso da parte
della pagina
Form
Collezione che riporta i valori degli elementi di un form inviato
insieme alla richiesta HTTP con utilizzo del metodo POST
QueryString
Collezione che riporta i valori delle variabili rappresentat nella
query string
ServerVariables
Collezione che riporta i valori di alcune predefinite variabili
d'ambiente
6.1 - La collezione Request.Form
Le pagine ASP sono in grado di leggere i dati a loro inviati dall'utente con metodo POST
mediante i semplici form HTML. Si consideri il seguente modulo:
<form action="recupera.asp" method="POST">
<input type="text" name="text1" value="">
<input type="submit" value="invia il testo">
</form>
Nella pagina di destinazione dei dati, recupera.asp nel caso dell'esempio, i dati possono essere
recuperati mediante la scansione della collezione Request.Form. Ciò avviene in maniera molto
semplice: per leggere il testo inserito nel campo di nome text1 è sufficiente chiamare
var testo = Request.Form("text1");
utilizzando quindi come chiave di lettura della collezione lo stesso nome scelto per il
corrispondente campo presente nel form della pagina HTML di partenza. Una tecnica utilizzata
per individuare se si è ricevuto o meno un certo dato è basata sulla conversione in stringa del
dato che si ricerca. Un Request.Form assente convertito in tale forma darà origine al valore di
testo undefined:
var testo = String(Request.Form("text1"));
if (testo=="undefined") // non è stato inviato alcun campo nominato text1!
Tutti i campi restituiscono solitamente la stringa value a loro associata.
Nei campi di tipo text il valore è direttamente immesso dall'utente, mentre ad esempio in quelli
di tipo hidden è scelto direttamente dall'autore della pagina e non più modificabile. In presenza
di bottoni di tipo radio viene restituito il value associato alla scelta fatta. Ad es.
<input type="radio" name="r1" value="primo">
<input type="radio" name="r1" value="secondo">
<input type="radio" name="r1" value="terzo">
Nel caso l'utente abbia selezionato il secondo tasto il contenuto di Request.Form("r1") sarà
proprio "secondo", similmente negli altri casi.
Interessante è il comportamento dei checkbox. A differenza dei pulsanti radio, infatti, i
checkbox ammettono la selezione multipla. Qualora l'utente selezionasse un solo tasto tra tutti
quelli aventi lo stesso nome si recupererebbe in forma di stringa il value ad esso associato.
Qualora invece i pulsanti selezionati fossero due o più si otterrebbe indietro un Array di
stringhe di tanti elementi quante sono le caselle spuntate. Ogni stringa dell'Array
rappresenterà il value di un differente pulsante tra quelli scelti. Comportamenti simili
avvengono per i campi select a selezione multipla.
Basta sapere un po' maneggiare i form HTML per esplorare in breve tutte le possibili
combinazioni. Nel capitolo successivo si metteranno fattivamente in pratica tali nozioni.
6.2 - La collezione Request.QueryString
La collezione Request.QueryString è per molti versi analoga a Request.Form, con la particolare
differenza che i dati passati alla pagina devono essere appesi in una query string (cioè
accoppiati alla richiesta dell'URL e divisi da esso per mezzo di un punto di domanda "?"). Un
tipico esempio di una query string appesa ad un URL è il seguente:
http://www.dominio.com/dir1/pagina.asp?testo=ciao%20a%20tutti
I browser generano automaticamente dati in query string inviando dei moduli con metodo GET.
Si prenda a modello il segiente form:
<form action="recupera.asp" method="GET">
<input type="hidden" name="h1" value="testo di prova">
<input type="submit">
</form>
L'invio di tale modulo appende all'URL di recupera.asp la seguente query string:
?h1=testo%20di%20prova
Il recupero dei dati avviene poi in maniera naturale e simile a quella analizzata con
Request.Form:
var testo = Request.QueryString("h1");
// testo sarà uguale a "testo di prova"
La convenienza dell'utilizzo di una query string consiste nel fatto che si possono concedere alla
pagina di destinazione dei parametri anche senza dover necessariamente ricorrere all'ausilio
dei form. Possono benissimo essere creati, infatti, dei collegamenti ipertestuali già impostati
con una query string:
<a href="apri.asp?id=56">Messaggio numero 56</a>
Si pensi un momento alla potenza di questa tecnica che permette di gestire automaticamente
grossi archivi di dati in continua modifica con un insieme di poche pagine che compiono azioni
differenti a seconda della query string a loro passata. Nel caso di un forum per discussioni, ad
esempio, ogni singolo messaggio potrebbe essere memorizzato in un database o in un file di
testo in maniera tale che ad esso corrisponda univocamente in qualche modo un identificatore
numerico. Si potrebbe realizzare così un'unica pagina ASP che mostri il contenuto del
messaggio il cui identificatore è proprio quello specificato dalla query string. Si eviterebbe così
di dover creare un differente documento HTML per ogni messaggio, con notevole risparmio di
risorse. Gli aspetti particolari derivanti da questa tecnica saranno esaminati a livello pratico nei
tutorial che si incontreranno nel percorso di questa guida.
Per ora però si tenga presente un aspetto fondamentale. Nelle query string molti caratteri
considerati non ammissibili in una richiesta di URL (come gli spazi bianchi o le lettere
accentate) devono essere sostituiti con il loro corrispondenti codici ASCII nel formato %xx,
dove xx è un valore esadecimale. I browser effettuano automaticamente la conversione dei
dati verso questo formato se essi sono veicolati attraverso un form inviato con metodo GET.
Per questo motivo il value dell'esempio precedente "testo di prova" diventava
automaticamente in query string "testo%20di%20prova". I dati restituiti dalla collezione
Request.QueryString subiscono automaticamente la conversione inversa. Nel caso in cui si
voglia però appendere "manualmente" una query string ad un URL, come nell'esempio del link
ipertestuale di poche righe sopra, bisogna porre attenzione a questo aspetto, in quanto il
compito di effettuare la conversione non ricadrebbe più sul browser. JScript, relativamente a
tale questione, fornisce due funzioni native per la conversione delle stringhe: escape(stringa) e
unescape(stringa). L'utilizzo è semplice e lo si deduce dal seguente esempio:
var testo1 = "testo di prova";
var testo2 = escape(testo1);
Response.Write(testo2);
// stampa: testo%20di%20prova
var testo3 = unescape(testo2);
Response.Write(testo3);
// stampa: testo di prova
6.3 - La collezione Request.ServerVariables
La collezione Request.ServerVariables si occupa del fornire delle informazioni relative all'utente
connesso ad una pagina o del restituire i valori di alcune variabili d'ambiente. L'utilizzo è
classico, basato sulla nozione puntata e sulle chiavi:
var variabile = Request.ServerVariables("nome_chiave");
Le possibili chiavi sono molteplici ed alcune di esse dipendono strettamente dal server che
elabora la pagina. In questa sede si farà una cernita solo di quelle più ricorrenti e di quelle più
frequentemente utilizzate:
Chiave
Utilizzo
ALL_HTTP
Restituisce tutti gli header HTTP associati alla richiesta del
client
CONTENT_LENGTH
Restituisce la lunghezza dei contenuti inviati dal client
CONTENT_TYPE
Restituisce il tipo di dato dei contenuti inviati dal client. Si
utilizza con le richieste che trasmettono informazioni a partire
dal client, come con i form HTML inviati con metodo POST o
GET
HTTP_<HEADER>
Sostituendo <HEADER> con il nome di uno specifico header
appartenente alla richiesta HTTP è possibile conoscerne il
contenuto
HTTPS
Restituisce ON se la richiesta arriva attraverso canali sicuri
(SSL), OFF in caso contrario
LOCAL_ADDR
Restituisce l'indirizzo del Server
QUERY_STRING
Restituisce l'intera query string associata all'URL
REMOTE_ADDR
Restituisce l'indirizzo IP dell'host che ha inviato la richiesta
REMOTE_HOST
Restituisce il nome dell'host che ha inviato la richiesta
SCRIPT_NAME
Restituisce il percorso virtuale dello script in esecuzione
SERVER_NAME
Restituisce il nome del server, il suo alias DNS o il suo
indirizzo IP
SERVER_PORT
Restituisce il numero della porta alla quale la richiesta è stata
inviata
SERVER_SOFTWARE
Restituisce il nome e la versione del server software che ha
risposto alla richiesta
6.4 - Esercitazione sull'oggetto Request
Il seguente esempio è utile per mettere meglio a fuoco quanto appreso nei paragrafi
precedenti. Verrà realizzato un modulo HTML per l'invio dei dati attraverso metodo GET e una
pagina ASP in grado di riceverli, interpretarli e maneggiarli al fine di produrre un output
dimostrativo delle potenzialità dell'oggetto Request.
Si cominci allora proprio dal modulo, che può essere racchiuso in una semplice pagina HTML
chiamata form.htm:
<html>
<head>
<title>Esercitazione sull'oggetto Request</title>
</head>
<body>
Compila ed invia il seguente modulo:<br>
<form action="elabora.asp" method="get">
Introduci il tuo nome:<br>
<input type="text" name="nome" value=""><br>
Introduci il tuo cognome:<br>
<input type="text" name="cognome" value=""><br><br>
Specifica il tuo sesso:<br>
<input type="radio" name="sesso" value="M"> M
<input type="radio" name="sesso" value="F"> F
<br><br>
<input type="submit" value="Invia il modulo">
</form>
</body>
</html>
La pagina ASP in grado di ricevere i dati deve ora essere posizionata nella medesima cartella di
form.htm e deve essere nominata elabora.asp:
<%@ LANGUAGE = JScript %>
<%
// Recupero del modulo
var nome = Request.QueryString("nome");
var cognome = Request.QueryString("cognome");
var sesso = Request.QueryString("sesso");
var dizione = (sesso=="F") ? "una femminuccia" : "un maschietto";
var SCRIPT = Request.ServerVariables("SCRIPT_NAME");
var SERVER_NAME = Request.ServerVariables("SERVER_NAME");
var QUERY_STRING = Request.ServerVariables("QUERY_STRING");
var IP = Request.ServerVariables("REMOTE_ADDR");
%>
<html>
<head>
<title>Esercitazione sull'oggetto Request</title>
</head>
<body>
<b>GRAZIE PER AVER SPEDITO IL MODULO!</b><br><br>
Ecco i dati che mi hai inviato:<br><br>
Nome: <b><%=nome%></b><br>
Cognome: <b><%=cognome%></b><br><br>
Stando a quanto dici dovresti essere <%=dizione%><br><br>
In più io aggiungo:<br><br>
Nome di questa pagina: <b><%=SCRIPT%></b><br>
Nome del server: <b><%=SERVER_NAME%></b><br>
Tuo indirizzo IP: <b><%=IP%></b><br><br>
L'intera query string che mi hai passato è: <b><%=QUERY_STRING%></b><br><br>
</body>
</html>
Come ulteriore esercitazione si provi a produrre un secondo form utilizzando però il metodo
POST per il suo invio, ed implementando magari l'uso di campi di input quali checkbox,
textarea e select.
Capitolo 7 - Un libro degli ospiti in ASP
7.0 - Introduzione al tutorial
E' finalmente giunto il momento di mettere a frutto le conoscenze acquisite nel corso dei
capitoli precedenti, realizzando una prima applicazione ASP di una certa entità. Lavorando con
JScript, oggetti built-in, oggetti ActiveX e accesso al File System realizzeremo un semplice
guestbook, in Italia meglio noto come "libro degli ospiti". Si tratta sostanzialmente di una
pagina Web all'interno della quale un visitatore può inserire un messaggio, in maniera abbinata
ad alcune informazioni, quali il proprio nome, l'indirizzo e mail e l'URL della propria Home
Page. Insomma, il classico servizio che molti siti amatoriali ospitano per dare voce ai propri
visitatori. Il codice e le regole che esamineremo, comunque, avranno ancora una volta valore
generale, per cui con un po' di maestria sarà possibile ottenere tantissime varianti sul tema,
realizzando ad esempio delle bacheche per scambio annunci e altri servizi basati
fondamentalmente sullo stesso meccanismo del guestbook.
Il progetto sarà costituito da 4 files distinti in rapporto tra di loro, lo schema sarà quello
rappresentato qui di seguito:
I FILE DEL PROGETTO "LIBRO DEGLI OSPITI"
1.
messaggi.txt - file di testo utilizzato per conservare i messaggi degli utenti. Da
questo file verranno letti i messaggi da visualizzare, e in questo file sarà introdotto ogni
nuovo messaggio.
2.
guestbook.asp - pagina ASP in grado di prelevare i messaggi contenuti in
messaggi.txt e di formattarli in un opportuno output HTML da inviare al browser del
visitatore.
3.
nuovo_messaggio.htm - pagina di semplice HTML contenente un form da utilizzare
per l'invio di un nuovo messaggio alla pagina inserisci_messaggio.asp, con metodo POST
4.
inserisci_messaggio.asp - pagina ASP che raccoglie i dati inviati dall'utente, li
controlla, li formatta e li inserisce in cima a messaggi.txt.
E' bene stabilire ora la struttura di un singolo messaggio, analizzando quali campi possano di
fatto costituirlo. Selezioneremo ora alcuni campi esemplificativi, alcuni dei quali dovranno
essere inseriti obbligatoriamente, mentre altri potranno essere anche tralasciati. Ecco uno
schema relativo alla struttura di un singolo messaggio:
NOME IDENTIFICATIVO (campo obbligatorio)
E MAIL (campo facoltativo)
HOMEPAGE URL (campo facoltativo)
CORPO DEL MESSAGGIO (campo obbligatorio)
Nei paragrafi successivi vedremo come trasformare questa concettualizzazione del problema in
una forma appetibile per un computer, attraverso appunto la tecnologia ASP.
7.1 - Conservazione e lettura dei messaggi
Il fulcro dell'intero progetto è rappresentato da messaggi.txt, il semplice file di testo che
utilizzeremo come supporto e come contenitore dei messaggi inseriti dagli utenti. Vediamo
allora come organizzare, o per meglio dire formattare, questo file affinché possa ospitare una
rappresentazione del messaggio teorizzato nel paragrafo precedente. La cosa più ovvia che ci
viene in mente è quella di inserire ogni campo su righe distinte, ed è una soluzione piuttosto
accettabile per questo nostro primo e semplice tutorial. Potrebbe poi darsi che in certe
occasioni ci venga utile agire manualmente sul file di testo, quindi per pura chiarezza in fase di
lettura da parte dell'uomo divideremo ogni messaggio con un invio, ossia con una riga bianca.
Ecco un esempio di un file di testo in grado di soddisfare le regole appena annunciate:
--- INIZIO --Sauron
[email protected]
http://www.sauronsoftware.it
Ciao a tutti i lettori!
Tizio
Io non ho messo e mail e url... tanto sono facoltativi!
--- FINE --Questo piccolo esempio presenta un file di testo contenente due messaggi. Le scritte INIZIO e
FINE sono da considerarsi escluse e sono state utilizzate solo per mettere in risalto come,
anche alla fine del secondo ed ultimo messaggio, sia stata introdotta comunque una riga
bianca.
Una volta in possesso di questi dati realizzare la pagina guestbook.asp rimane abbastanza
semplice. Si realizzi per prima cosa il file messaggi.txt inserendo al suo interno i due messaggi
di poche righe sopra, in modo da predisporlo a divenire "cavia" della pagina ASP che stiamo
per andare a creare. Ecco cosa dovremo dire al server tramite la pagina guestbook.asp:
1.
tramite l'oggetto ActiveX denominato Scripting.FileSystemObject apri uno stream
verso messaggi.txt
2.
se non sei alla fine dello stream (raggiungimento dell'ultima riga del file di testo)
procedi al passo 3, altrimenti chiudi lo stream e concludi
3.
leggi una riga, questo è il nome di un messaggio, stampalo nell'output
4.
leggi una riga, questa è l'e mail. Il campo è facoltativo. In caso dovesse essere vuoto
non fare nulla, altrimenti stampa l'indirizzo e mail nell'output HTML generando un
collegamento ipertestuale di tipo mailto: ad esso
5.
leggi una riga, questo è l'url per la home page. Il campo è facoltativo. In caso
dovesse essere vuoto non fare nulla, altrimenti stampa l'indirizzo nell'output HTML
generando un collegamento ipertestuale
6.
leggi una riga, questo è il messaggio, stampalo nell'output
7.
salta una riga nello stream
8.
torna al passo 2 e reitera il ciclo a partire da lì
Questi 8 passi descrivono alla perfezione l'algoritmo che abbiamo intenzione di utilizzare. Nella
concettualizzazione di esso, ovviamente, si è esclusa ogni forma di ritocco grafico e ci si è
concentrati solo sul meccanismo principale. Questa è una maniera molto efficiente di
procedere, le aggiunte grafiche possono poi essere fatte indipendentemente in un secondo
momento. Ecco come viene ora formulata la pagina guestbook.asp:
<%@ LANGUAGE = JScript %>
<%
// Apertura dello stream verso il file di testo
var fso = new ActiveXObject("Scripting.FileSystemObject");
var stream = fso.OpenTextFile(Server.MapPath("messaggi.txt"));
%>
<html>
<head><title>Libro degli ospiti</title></head>
<body>
<h1>Libro degli ospiti</h1>
<h4><a href="nuovo_messaggio.htm">Firmalo anche tu!</a></h4>
<%
while (!stream.AtEndOfStream) {
Response.Write("<hr>");
aux = stream.ReadLine();
Response.Write("<b>Nome:</b> " + aux + "<br>");
aux = stream.ReadLine();
if (aux!="") Response.Write("<b>E mail:</b> <a href='mailto:" + aux + "'>" + aux + "</a><br>");
aux = stream.ReadLine();
if (aux!="") Response.Write("<b>Home Page:</b> <a href='" + aux + "'>" + aux + "</a><br>");
aux = stream.ReadLine();
Response.Write("<b>Messaggio:</b><br>" + aux + "<br>");
stream.SkipLine();
}
stream.Close();
%>
<hr>
<p><font size="2">Powered by "ASP: la guida introduttiva"</font></p>
</body>
</html>
Con questo codice già è possibile sperimentare la prima parte del libro degli ospiti. Qualora
dovesse insorgere un errore il consiglio è di controllare attentamente ciò che è stato inserito in
messaggi.txt, in particolare la presenza della sola ed unica riga vuota finale.
Svuotando completamente messaggi.txt potrete ottenere un guestbook totalmente "vergine".
7.2 - Form di invio del messaggio
La seconda parte del progetto è costituita da quei due file che permettono l'inserimento di un
nuovo messaggio nell'archivio. Si parte dalla pagina HTML nuovo_messaggio.htm, contenente
il form da compilare ed inviare per portare a compimento l'operazione. Non ci si soffermerà più
di tanto su di esso, data la banalità della cosa:
<html>
<head><title>Firma il libro degli ospiti</title></head>
<body>
<h1>Libro degli ospiti</h1>
Introduci il tuo messaggio ed invia il modulo
<hr>
<form action="inserisci_messaggio.asp" method="post">
<b>Nome: (*)</b><br>
<input type="text" name="nome"><br>
<b>E mail:</b><br>
<input type="text" name="email"><br>
<b>Home Page:</b><br>
<input type="text" name="homepage" value="http://"><br>
<b>Messaggio: (*)</b><br>
<textarea rows="5" cols="30" name="messaggio"></textarea>
<br>
<input type="submit" value="Invia">
<input type="reset" value="Cancella">
</form>
<hr>
I campi contrassegnati da (*) sono <b>obbligatori</b>
</body>
</html>
Come è possibile vedere i dati sono inviati con metodo POST alla pagina
inserisci_messaggio.asp, oggetto di studio del sucessivo paragrafo.
7.3 - Inserimento di un nuovo messaggio
Si è finalmente giunti alla parte conclusiva, ma anche più laboriosa, del progetto. Il file
inserisci_messaggio.asp dovrà agire in base ai seguenti passi:
1.
2.
3.
recupera i dati inviati tramite il form
controlla questi dati, in caso di problemi interrompi la procedura e dai notifica
dell'accaduto
esamina i dati, correggili e ottimizzali per la loro archiviazione
4.
apri uno stream in lettura verso messaggi.txt
5.
leggi e conserva l'intero contenuto di messaggi.txt
6.
chiudi lo stream in lettura
7.
crea un nuovo messaggi.txt vuoto sovrascrivendo quello precedente e instaura uno
stream in scrittura con esso
8.
inserisci in esso il nuovo messaggio
9.
inserisci in esso la lista dei messaggi precedenti
10.
chiudi lo stream in scrittura
Questo algoritmo illustra quindi le operazioni necessarie. I singoli passi sono stati raggruppati
in tre diverse classi, in modo da rendere ancora più chiare le tre operazioni base: leggere e
validare il form, prelevare il contenuto del file di testo, generare una nuova lista di messaggi
contenente in cima ad essa quanto appena inviato dall'utente. Questa doppia operazione di
lettura/scrittura di messaggi.txt è dettata dall'esigenza di inserire in testa alla lista il nuovo
messaggio, in modo da tale che accedendo a guestbook.asp questo risulti il primo ad essere
letto. Qualora si fosse invece optato per un inserimento in coda alla lista sarebbe allora bastato
accedere a messaggi.txt una sola volta, in modalità ForAppending. Il messaggio, in questo
caso, verrebbe però visualizzato come l'ultimo della lista.
Il codice del documento può essere ora presentato:
<%@ LANGUAGE = JScript %>
<%
function togliInvio(str) {
var cars;
var str2 = "";
for (c=0;c<str.length;c++) {
cars = str.charCodeAt(c);
if (cars!=13 && cars!=10) {
str2 += str.charAt(c);
} else {
if (cars==13) str2 += " ";
}
}
return str2;
}
// Ricezione del form
var nome = String(Request.Form("nome"));
if (nome=="undefined") nome = "";
var email = String(Request.Form("email"));
if (email=="undefined") email = "";
var homepage = String(Request.Form("homepage"));
if (homepage=="undefined" || homepage=="http://") homepage = "";
var messaggio = String(Request.Form("messaggio"));
if (messaggio=="undefined") messaggio = "";
// Controllo campi obbligatori
if (nome=="" || messaggio=="") {
Response.Write("Non hai compilato tutti i campi obbligatori! Torna indietro e correggi.");
Response.End;
}
// Elimina eventuali "invii" dai vari campi
// Evita così che un campo possa andare su più di una riga
nome = togliInvio(nome);
email = togliInvio(email);
homepage = togliInvio(homepage);
messaggio = togliInvio(messaggio);
var fso = new ActiveXObject("Scripting.FileSystemObject");
// Legge i messaggi vecchi
var in_stream = fso.OpenTextFile(Server.MapPath("messaggi.txt"));
var vecchi = in_stream.ReadAll();
in_stream.Close();
// Crea il nuovo file
var out_stream = fso.CreateTextFile(Server.MapPath("messaggi.txt"));
out_stream.WriteLine(nome);
out_stream.WriteLine(email);
out_stream.WriteLine(homepage);
out_stream.WriteLine(messaggio);
out_stream.WriteLine();
out_stream.Write(vecchi);
out_stream.Close();
Response.Redirect("guestbook.asp");
%>
La particolare funzione togliInvio fornita nel codice si occupa di eliminare ogni eventuale
andata a capo presente nei vari campi. Un invio, infatti, verrebbe riportato come tale
all'interno dello stesso file di testo e perciò il nostro modello di messaggio non verrebbe più
rispettato. Davanti alle "conquiste" fatte nel corso dei capitoli precedenti la comprensione del
codice dovrebbe risultare piuttosto agevole. In ogni caso i blocchi di codice sono commentati e
divisi tra di loro seguendo la stessa logica utilizzata per raggruppare i diversi passi
dell'algoritmo utilizzato.
Si lascia ora al lettore la facoltà di migliorare e di arricchire il progetto secondo la propria
fantasia. Ecco alcune possibili tracce di lavoro sulle quali esercitarsi:

disabilitare la possibilità da parte dell'utente di inserire codice HTML nei messaggi

creare una console di amministrazione del guestbook, attraverso la quale sia possibile
cancellare messaggi indesiderati

limitare il numero di messaggi presenti nel file di testo ad un numero finito, ad esempio
facendo in modo che all'inserimento del ventunesimo messaggio venga
automaticamente eliminato il primo
Buon lavoro!
Capitolo 8 - L'oggetto built-in Server
8.0 - Proprietà e metodi di Server
L'oggetto built-in Server, il terzo oggetto di questo tipo presentato nel corso della guida, si
occupa di fornire il controllo di certi metodi e proprietà del server, alcuni dei quali davvero utili
nella realizzazione di un'applicazione ASP. L'interfaccia dell'oggetto built-in Server è costituita
da quattro metodi ed una proprietà.
Metodi
CreateObject
Genera un'istanza di una componente del server
HTMLEncode
Applica le regole della codifica HTML ad una stringa
MapPath
Trasforma un percorso relativo nel corrispondente percorso assoluto
URLEncode
Applica le regole della codifica URL ad una stringa
Proprietà
ScriptTimeout
Specifica in secondi la durata massima di esecuzione dello script
8.1 - Il metodo Server.CreateObject
Il metodo CreateObject(progID) genera un'istanza della componente installata sul server
identificata tramite la stringa progID. Per certi versi l'uso di questo metodo può risultare
equivalente al costruttore della classe ActiveXObject, fornita nativamente da JScript. Negli
esempi incontrati sinora, infatti, ogni oggetto ActiveX è stato istanziato mediante la sintassi
var riferimentoOggetto = new ActiveXObject(idOggetto);
E' dunque possibile fare altrettanto attraverso la sintassi
var riferimentoOggetto = Server.CreateObject(idOggetto);
Ad esempio si potrebbe istanziare Scripting.FileSystemObject alla seguente maniera:
var fso = Server.CreateObject("Scripting.FileSystemObject");
Gli oggetti istanziati in questa maniera godono di quello che è generalmente definito il "page
scope", ovvero restano in vita tanto quanto dura il processo di interpretazione e di esecuzione
della pagina che li dichiara, al termine del quale vengono distrutti. ASP, comunque, permette
anche di istanziare anche oggetti con scope differenti, ad esempio con un ciclo di vita
equivalente alla durata della sessione di un utente, oppure della durata dell'intera applicazione.
Si tornerà su questo argomento nei capitoli a venire, quando saranno presi in esami i due
rimanenti oggetti built-in: Session e Application.
Molte componenti vengono distribuite insieme al Web Server stesso, per questo alcune di esse
sono considerate standard e praticamente parte integrante di ASP.
I componenti più diffusi per ASP
MSWC.AdRotator
Oggetto utilizzato per la gestione automatica dei
banner in rotazione sulle pagine Web
MSWC.BrowserType
Oggetto in grado di fornire allo script delle
informazioni sul browser del client
Scripting.FileSystemObject
Oggetto che fornisce accesso al file system
ADODB.Connection
Sfruttatissimo oggetto che fornisce accesso ai
database
MSWC.NextLink
Oggetto utilizzato per indicizzare diversi URL e gestirli
come si trattasse di un libro
MSWC.Tools
Oggetto che raccoglie alcune funzioni di piccola utilità
MSWC.Counters
Oggetto utilizzato per gestire dei contatori di accesso
Prima di far ricorso ad una qualsiasi componente esterna, comunque, è sempre bene verificare
che essa sia installata all'interno del Web Server che dovrà ospitare il lavoro una volta
completato.
8.2 - Il metodo Server.HTMLEncode
Il metodo HTMLEncode(stringa) applica ad una stringa la codifica HTML. Si tratta di un metodo
che può tornare utile in certe occasioni, ad esempio quando si vogliono disabilitare dei
marcatori HTML. Il comando
Response.Write(Server.HTMLEncode("<b>grassetto</b>"));
restituisce un output come il seguente
<b>grassetto</b>
I due tag così convertiti non vengono più riconosciuti tali dal parser del browser, eppure < e
> vengono mostrati a video esattamente come i caratteri di minore e maggiore.
8.3 - Il metodo Server.MapPath
Il metodo MapPath(stringa), che già è stato presentato al lettore nel corso dei capitoli
precedenti, si occupa del desumere un percorso assoluto per un file o una cartella a partire dal
suo percorso relativo. Poichè molte componenti necessitano di percorsi assoluti per interagire
con il file system il ruolo ricoperto dal metodo Server.MapPath è di vitale importanza, e
permette sostanzialmente la creazione di applicazioni indipendenti dall'organizzazione delle
cartelle del server che le ospita. Tanto per portare un esempio, se la root del server fosse
c:\inetpub\wwwroot il codice
Response.Write(Server.MapPath("file.txt"));
se incluso in un documento ASP posto esattamente in tale cartella produrrebbe l'output
c:\inetpub\wwwroot\file.txt
8.4 - Il metodo Server.URLEncode
Il metodo URLEncode(stringa), applica ad una stringa le regole della codifica utilizzata nella
formulazione degli URL. Sostanzialmente questa stessa funzione è già offerta nativamente da
JScript, tramite il comando escape(). JScript, inoltre, è in grado di effettuare anche la codifica
inversa, attraverso l'uso di unescape(). Il codice
Response.Write("cerca.asp?words=" + Server.URLEncode(risorse per il webmaster));
fornirebbe l'output
cerca.asp?words=risorse%20per%20il%20webmaster
8.5 - La proprietà Server.ScriptTimeout
Il valore dell'intero ScriptTimeout può essere modificato per determinare il tempo di
esecuzione massimo concesso ad uno script. Può infatti capitare che, per svariati fattori, una
pagina impegni troppo a lungo il server, senza riuscire a completare la propria esecuzione. Per
questo esiste un "tetto massimo" di secondi concesso ad uno script entro il quale tutti i compiti
devono essere ultimati. Superato questo limite l'interpretazione della pagina viene bloccata e
al client viene restituito un errore. Il valore di default di questa proprietà è settato solitamente
a 90 secondi. E' possibile quindi modificare tale arco di tempo in base alle proprie necessità,
soprattutto per prolungarlo quando di sa a priori che la pagina che si sta realizzando
necessiterà di un tempo superiore al limite imposto. La modifica è semplice:
Server.ScriptTimeout = 120;
Questo esempio porta il tempo massimo di esecuzione a due minuti (120 secondi) per la
pagina corrente.
Capitolo 9 - ASP e database
9.0 - Introduzione
Uno delle peculiarità che ha portato ASP ad una vasta diffusione consiste nella facilità con la
quale è possibile interfacciare uno script ad una fonte di dati che rispetti lo standard detto
ODBC (Open Database Connectivity). La connettività verso fonti di dati ODBC permette di
realizzare con il minimo sforzo un innumerevole quantitativo di applicazioni, che spazia dal
semplice guestbook fino ad arrivare a potenti interfacce di ricerca su cataloghi online. In
pratica grazie ai database diventa semplice la catalogazione di lunghe moli di dati e facile
l'accesso ad essi grazie all'oggetto ActiveX Data Object (abbreviato ADO), che fornisce
connettività verso tutti i tipi di database che supportino ODBC, mediante un driver apposito. In
ASP, a livello elementare, è praticamente prassi l'utilizzo dei database di Access, non a caso
prodotto sempre da Microsoft. Per addentrarsi nello studio della connettività verso fonti di dati
ODBC non è ora richiesto alcun prerequisito particolare, anche se magari una praticità già
acquisita con i database manager e con SQL potranno certamente aiutare nella comprensione
dei paragrafi e dei capitoli successivi dedicati all'argomento. Tutti gli esempi riportati in questa
sede si baseranno, per amore dello standard di ASP, sui database di Access. Sarà dunque
necessario disporre di una versione abbastanza recente di questo software (97 o
preferibilmente 2000) per procedere.
9.1 - Primo approccio ad ADO
Come appena detto ADO è il tramite necessario per operare, sia in lettura che in scrittura, su
una fonte di dati ODBC. Il primo oggetto che si incontra analizzando questo componente è
Connection, che, come il nome lascia ad intuire, si occupa di fornire la connettività verso un
database. L'oggetto Connection va dichiarato come un qualsiasi ActiveX, mediante la seguente
sintassi:
var ADOConn = new ActiveXObject("ADODB.Connection");
Tramite il riferimento ADOConn è ora possibile accedere a metodi e proprietà di tale oggetto.
Prima di procedere oltre, al fine di seguire gli esempi presentati, è necessario ora realizzare un
piccolo database Access di prova. Si vada ad avviare la propria copia di Access, si scelga
dall'interfaccia iniziale l'operazione di creazione di un nuovo database vuoto e lo si vada a
riporre, con un nome arbitrario, ad esempio prova.mdb, nella cartella che ospiterà i codici ASP
analizzati di seguito. Da Access ora si crei una nuova tabella in visualizzazione struttura ed in
essa si vadano ad inserire i seguenti campi:

id, di tipo contatore, da selezionare come chiave primaria mediante click sull'iconcina a
forma di chiave (la riga di questo record nella visualizzazione struttura deve essere
selezionata mentre si clicca sull'icona)

nome, di tipo testo

cognome, di tipo testo

e_mail, di tipo testo
Chiudendo la finestra di visualizzazione struttura si scelga di salvare tali aggiunte e si assegni
un nome alla tabella appena creata, ad esempio nominativi. Tornando alla schermata
precedente sarà ora possibile visualizzare nell'elenco delle tabelle quella appena creata. Si
esegua un doppio click sul suo nome per accedere alla finestra che permetterà di immettere
dati all'interno della tabella. Per testare gli esempi si generino due rige (record) con dati
arbitrari e fantasiosi, ad esempio Mario Rossi [email protected] e Giuseppe Bianchi
[email protected]. Come è possibile notare il campo id, che è un contatore nonché la chiave
primaria, si incrementa da solo ad ogni inserimento di record, generando una relazione univoca
tra l'insieme dei record inseriti e i numeri naturali. A questo punto si salvi tutto e si chiuda
pure Access, il database di esempio è pronto.
Si utilizzi ora in maniera dogmatica il seguente codice, tanto per vedere in pratica sin dall'inizio
il risultato cercato:
<%@ LANGUAGE = JScript %>
<%
// Istanzio l'oggetto Connection
var ADOConn = new ActiveXObject("ADODB.Connection");
// Creo la STRINGA DI CONNESSIONE
var strConn = "";
strConn += "driver={Microsoft Access Driver (*.mdb)};";
strConn += "dbq=" + Server.MapPath("prova.mdb");
// Apro la connessione al database selezionato
ADOConn.Open(strConn);
// Interrogo il database
var sql = "SELECT * FROM nominativi";
var ris = ADOConn.Execute(sql);
// Stampo i dati contenuti nel database
while (!ris.EOF) {
Response.Write("Record n° " + ris("id") + "<br>");
Response.Write("Nome: " + ris("nome") + "<br>");
Response.Write("Cognome: " + ris("cognome") + "<br>");
Response.Write("E mail: " + ris("e_mail") + "<br><br>");
ris.MoveNext();
}
// Chiudo la connessione
ADOConn.Close();
%>
Non si meravigli il lettore se l'esempio non dovesse risultare da subito di facile compresione,
questo stesso codice servirà ora come appoggio per analizzare nel dettaglio le diverse fasi
dell'accesso ad un database.
9.2 - Metodi e proprietà di Connection
All'interno dell'esempio di poche righe sopra si è fatto ricorso a tre distinti metodi dell'oggetto
ADODB.Connection: Open, Execute e Close. In buona sostanza già questi soli tre metodi, il cui
utilizzo però non è immediato, permettono l'accesso ad una fonte di dati ODBC. Ad essi si
aggiungono poi altri elementi di interfaccia meno sfruttati, ma comunque buoni da conoscere.
Per questo è presentata anzitutto una cernita dell'interfaccia base messa a disposizione
dall'oggetto Connection. Si tenga presente che anche in questo caso non sono riportati né tutti
i metodi né tutte le proprietà dell'oggetto, ma si è scelto scartando i meno utili
nell'apprendimento iniziale dell'argomento. Qualunque approfondimento può essere svolto
documentandosi su http://www.microsoft.com/data/ado/
Metodi
Cancel
Annulla l'esecuzione di una chiamata dei metodi Execute e
Open in attesa
Close
Chiude un oggetto aperto e i relativi oggetti dipendenti
Execute
Esegue la particolare istruzione SQL passata al metodo
mediante un parametro stringa
Open
Apre una connessione a una fonte dati
Proprietà
ConnectionString
Contiene le informazioni utilizzate per stabilire una connessione
a una fonte dati
ConnectionTimeout
Indica in secondi il tempo di attesa da considerare durante il
tentativo di stabilire una connessione prima di terminare il
tentativo e generare un errore
CursorLocation
Imposta o restituisce la posizione del motore del cursore
Version
Indica il numero di versione di ADO
Il particolare metodo Open si occupa dell'aprire una connessione verso la fonte di dati
specificata in una stringa, detta stringa di connessione, passata al metodo sotto forma di
parametro. Le stringhe di connessione sono basate su una particolare sintassi. Ogni stringa di
connessione è costituita da una serie di coppie chiave-valore, separate tra di loro con un punto
e virgola. Le chiavi sono invece separate dai rispettivi valori mediante il simbolo di uguale. Nel
più tipico dei casi è necessario inserire in tale stringa almeno due coppie, atte ad indicare il
formato di database utilizzato e la sua locazione fisica nel file system del server. Ad esempio:
var strConn = "";
strConn += "driver={Microsoft Access Driver (*.mdb)};";
strConn += "dbq=C:\\Inetpub\\wwwroot\\dati\\database.mdb;";
Di solito il valore associato alla chiave dbq è ricavato mediante l'utilizzo del metodo
Server.MapPath, in modo da ottenere automaticamente la conversione di un percorso relativo
in un percorso assoluto. Così è stato fatto nell'esempio precedente.
All'interno della stringa di connessione possono poi essere specificati ulteriori dati, ad esempio
un'opzionale password associata al database (senza la quale non sarebbe possibile l'accesso ai
dati in esso contenuto), alla seguente maniera:
strConn += "psw=vgh564;";
Una volta stabilita la stringa di connessione è dunque possibile avviare l'accesso alla fonte di
dati specificata:
ADOConn.Open(strConn);
Da questo momento in poi sarà possibile dunque operare sul database specificato attraverso i
metodi di Connection e degli oggetti ad esso collegati. Una volta concluse tutte le operazioni
preventivate è buona norma chiudere la connessione, utilizzando il metodo Close:
ADOConn.Close();
9.3 - Il metodo Execute e le istruzioni SQL
Tipicamente il fulcro del lavoro viene svolto dal metodo Execute, a cui è data la possibilità di
eseguire ricerce, effettuare modifiche o cancellare dei record presenti in una tabella. Tale
metodo accetta un parametro di tipo stringa, contenente un'istruzione SQL da eseguire. In
buona sostanza all'interno di questa stringa viene detto esplicitamente al metodo Execute cosa
fare. SQL (Structured Query Language, si dovrebbe pronunciare sequel, ma quasi tutti in Italia
dicono esseccuelle) è un linguaggio che rappresenta lo standard per la definizione,
l'interrogazione e la modifica dei database. Per sfruttare la connettività ai database fornita da
ADO non occorre essere dei guru di SQL, tuttavia è necessario apprendere la sintassi e l'uso di
base di questo linguaggio. Nonostante un piccolo sforzo iniziale ci si accorgerà presto di quanto
la struttura di SQL sia facile da apprendere e persino vicina al linguaggio naturale, e per questo
semplice o addirittura piacevole da sfruttare. Si torni all'ora all'esempio già presentato:
var sql = "SELECT * FROM nominativi";
var ris = ADOConn.Execute(sql);
Si incotra qui per la prima volta nel corso di questa guida un'istruzione SQL. Le parole chiave
utilizzate sono state enfatizzate mediante il grassetto. Letteralmente "SELECT * FROM
nominativi" può essere tradotto in "SELEZIONA ogni campo DALLA TABELLA nominativi". Il
metodo Execute, eseguendo questo comando, restituisce un riferimento ad un oggetto di tipo
Recordset. Come ancora una volta il nome lascia intuire, un oggetto di tipo Recordset
rappresenta una collezione di record, estratti dal datadase dal metodo Execute rispettando ciò
che è stato domandato dall'istruzione SQL. In questo caso, infatti, l'oggetto restituito
colleziona tutti i campi di tutti i record presenti nella tabella nominativi del database. Un
diverso comando SQL avrebbe ritornato un diverso Recordset, ad esempio eseguendo una
selezione basata su qualche particolare regola o ispezione dei campi di ogni singolo record.
Una volta estratti i dati di proprio interesse dal database, tramite l'utilizzo della sintassi di SQL
e dell'oggetto Connection, è quindi possibile gestirli attraverso l'interfaccia pubblica
dell'oggetto Recordset. Andando avanti nel corso di questa guida saranno approfonditi gli
aspetti relativi sia ad SQL che all'utilizzo dei Recordset. Per il momento si è voluto
esclusivamente avvicinare il lettore all'ideologia che permea l'accesso alle fonti di dati ODBC.
Le applicazioni pratiche e l'approfondimento delle tematiche saranno dunque presentati passo
passo da qui in poi.
9.4 - Approccio agli oggetti Recordset
Il Recordset ricavato dal codice dell'esempio contiene dunque tutti i record che costituiscono la
tabella nominativi del database utilizzato. E' ora possibile muoversi all'interno dei dati acquisiti
semplicemente sfruttando i metodi e le proprietà degli oggetti Recordset, qui presentati in
maniera non ufficiale. In particolar modo, per ottenere una stampa a video di tutti i dati
ricevuti dalla fonte, è stato messo in atto un ciclo while basato sulla proprietà EOF e sul
metodo MoveNext di Recordset.
I Recordset possono essere immaginati come vere e proprie tabelle di dati, dove ogni riga
corrisponde ad un record, un po' come nella rappresentazione visuale di Access. Tale
ideologica tabella può essere letta mediante l'utilizzo di un cursore. Appena l'oggetto Recordset
è stato creato il cursore è posizionato all'inizio della prima riga di tale tabella. In questa
situazione i singoli campi del record evidenziato dal cursore sono leggibili tramite l'utilizzo della
sintassi
ris("nome_campo")
ed infatti si osservi questa porzione estratta dall'esempio:
Response.Write("Record n° " + ris("id") + "<br>");
Response.Write("Nome: " + ris("nome") + "<br>");
Response.Write("Cognome: " + ris("cognome") + "<br>");
Response.Write("E mail: " + ris("e_mail") + "<br><br>");
Il contenuto dei quattro campi disponibili nella tabella nominativi viene inviato all'output
utilizzando proprio la sintassi di accesso ai campi appena mostrata. Il cursore può inoltre
essere spostato in avanti lungo i vari record posseduti mediante il metodo MoveNext(). Una
chiamata a tale metodo, insomma, causa l'avanzamento alla riga successiva dell'ideale tabella
dei dati ricavati dal database. Naturalmente il numero di record che costituisce un Recordset è
per forza di cose finito, e non sarà possibile spostarsi in avanti su di esso per più di tot volte.
Se si tenta un avanzamento del cursore quando questo ha già scavalcato l'ultimo record della
collezione, infatti, viene lanciato un errore e l'esecuzione dell'intero script viene interrotta. E'
qui che torna dunque utile il valore booleano della proprietà EOF dell'oggetto Recordset. EOF è
true solo quando il cursore è posizionato al termine dell'ultima riga disponibile, false altrimenti.
Inglobando dunque gli strumenti acquisiti in un ciclo while diventa possibile il controllo e la
scansione del Recordset ricavato, indipendentemente dalla lunghezza di esso. Il tipo di
interfaccia utilizzato da Recordset è piuttosto diffusa in diversi ambienti, Java su tutti, ed è
semplice da assimilare proprio per una somiglianza con la naturale maniera di pensare
dell'uomo. La parte conclusiva di questo script, infatti, può essere così tradotta in linguaggio
naturale:
fino a quando non raggiungi la fine della tabella dei dati {
stampa i dati presenti nella riga attuale;
avanza di una riga;
}
L'interfaccia di Recordset, comunque, non termina qui e quella esaminata non è che una delle
tante pratiche nelle quali è possibile sfruttare un Recordset. Altre situzioni simili o meno a
questa verranno presentate nel corso dei capitoli successivi.
Capitolo 10 - Interagire con i database
10.0 - SQL per eseguire ricerche
Nel corso del capitolo precedente sono state approcciate le tecniche di accesso ad una fonte di
dati ODBC tramite il componente denominato ADO. Si è perciò notato quale importante ruolo
giochi la sintassi SQL anche nelle più basilari operazioni di ricerca. Come già è stato ribadito la
conoscenza di SQL esula dagli scopi di questa guida, tuttavia ci si occupa in questo capitolo
dell'analisi delle più elementari e necessarie tipologie di ricerche che è possibile effettuare su
un database. Tutto questo al fine di rendere produttivo lo studio compiuto sinora relativamente
all'accesso alle fonti ODBC.
Si riprenda allora l'esempio più recente riportato dalla guida rispetto ad SQL (cfr. Capitolo 9):
SELECT * FROM nominativi
Si tratta con tutta probabilità dell'esempio di più semplice formulazione possibile. Traducendo
in maniera letterale in italiano l'istruzione suonerebbe come:
SELEZIONA tutti i campi DALLA TABELLA nominativi
La prima cosa che si nota, perciò, è come siano determinanti in una ricerca le due istruzioni
SELECT e FROM, ambedue seguite da un parametro. La prima indica quali campi dei record
estratti debbano essere restituiti. Il particolare simbolo * sta ad indicare la restituzione di tutti
i campi appartenenti ai singoli record. La clausula FROM, invece, è necessaria per determinare
da dove estrarre tali record, e per questo viene fatta seguire dal nome della tabella che si
intende considerare. Come già è stato visto questo tipo di ricerca restituisce un oggetto
Recordset rappresentante tutti i record, comprensivi di tutti i campi, appartenenti alla tabella
nominativi. Di certo non si tratta di una ricerca troppo utile. Lo scopo di una ricerca, infatti, è
spesso quello di utilizzare un certo criterio per restringere il campo dei risultati. Supponiamo
allora di voler estrarre dalla tabella nominativi un solo record, ossia quello con id pari a 3. Ecco
come fare:
SELECT * FROM nominativi WHERE id = 3
In linguaggio naturale, oramai dovrebbe essere facile capirlo, l'istruzione si formula come:
SELEZIONA tutti i campi DALLA TABELLA nominativi DOVE il campo id è pari a 3
E se non dovesse essere presente un record che soddisfi tale condizione? Semplicemente il
valore della proprietà EOF dell'oggetto Recordset ricavato sarà true da subito. Da questo punto
di vista la sintassi per eseguire ricerche risulta piuttosto di facile intuizione. Se si fosse voluto
ricercare quei record con id maggiore a 3 sarebbe bastato scrivere:
SELECT * FROM nominativi WHERE id > 3
Si tralascia la trasposizione in linguaggio naturale. E' anche possibile utilizzare degli operatori
per specificare delle condizioni più complesse. Ad esempio ecco come si ottengono i record con
id compreso tra 3 e 15, estremi esclusi:
SELECT * FROM nominativi WHERE id > 3 AND id < 15
L'uso di tali operatori è ancora una volta intuitivo, e del tutto analogo alle espressioni booleane
di JScript. Gli operatori principalmente usati per combinare più espressioni sono ovviamente
AND e OR.
Fino ad ora ci si è limitati a specificare condizioni di ricerca sul campo id, ossia un contatore,
un campo numerico. Le ricerche più interessanti avvengono però sui campi contenenti del
testo, come nome o cognome nell'esempio del capitolo precedente. Come delimitatore di
stringhe SQL usa, in maniera analoga al Pascal, il singolo apice '. Se si volessero allora estrarre
dalla tabella nominativi quei record corrispondenti a tutti le persone aventi nome Carlo la
giusta istruzione sarebbe:
SELECT * FROM nominativi WHERE nome = 'Carlo'
Anche qui naturalmente si può ricorrere agli operatori AND ed OR per la costruzione di query
più complesse. Le ricerche su campi di testo, comunque, presentano problematiche molto più
estese rispetto alle più banali ricerche su campi numerici. Una questione piuttosto importante,
ad esempio, riguarda le ricerche case-insensitive, ossia quelle ricerce eseguite su campi di
testo senza considerare le differenze che corrono tra lettere minuscole e lettere maiuscole.
Secondo l'esempio precedente, infatti, è possibile ottenere tutti i record con campo nome pari
esattamente a 'Carlo'. Se nella tabella dovesse presentarsi un record con campo nome pari a
'carlo' questo non verrebbe restituito. Con l'operatore =, infatti, è possibile eseguire ricerche
solo ed esclusivamente case-sensitive. SQL fornisce perciò un altro operatore di confronto utile
per lo scopo: l'operatore LIKE. Si consideri la seguente riga:
SELECT * FROM nominativi WHERE nome LIKE 'carlo'
Questo comando restituirebbe tutti i record con campo nome pari a 'Carlo', 'carlo', e anche
volendo 'cArlo' o 'CARLO' e così via. I "prodigi" di SQL, comunque, non si fermano qui. Grazie
ad una serie di caratteri speciali, infatti, è anche possibile esprimere in maniera semplice delle
ricerche su campi testuali molto più complesse. Potrebbe infatti essere necessario fare delle
ricerche su campi che devono contenere una certa parola, e non per forza equivalere ad essa.
Si supponga che nella tabella nominativi sia presente anche una persona chiamata Giancarlo, e
si supponga di voler ottenere con una semplice ricerca anche questo record.
SELECT * FROM nominativi WHERE nome LIKE '%carlo'
Il particolare carattere %, quindi, rappresenta una qualsiasi combinazione di caratteri. Nel caso
specifico, quindi, vengono restituiti tutti quei record di cui il campo nome termini con la
sequenza di lettere 'carlo', e quindi anche 'Giancarlo'. Il simbolo % può essere utilizzato perciò
in svariati modi. Si potrebbe pensare di farsi restituire tutti quei record con il campo nome che
cominci per 'carlo'.
SELECT * FROM nominativi WHERE nome LIKE 'carlo%'
Se dovesse esistere un record con campo nome pari a 'Carlo Ambrogio' verrebbe in questo
caso incluso tra i risultati restituiti dalla query. Una ricerca molto interessante è la seguente:
SELECT * FROM nominativi WHERE nome LIKE '%carlo%'
Tradotta in linguaggio naturale verrebbe:
SELEZIONA tutti i campi DALLA TABELLA nominativi DOVE il campo nome CONTIENE la
parola 'carlo'
Con questa ricerca verrebbero perciò restituiti tutti i Carlo, ma anche tutti gli eventuali
composti del nome, tra cui Giancarlo e Carlo Ambrogio degli esempi precedenti.
SQL permette inoltre di mettere in ordine i risultati restituiti in base ad un particolare criterio,
grazie alla clausula SORT BY. Si immagini di voler ordinare la ricerca appena esaminata
secondo l'ordine alfabetico dei cognomi. La cosa è semplice:
SELECT * FROM nominativi WHERE nome LIKE '%carlo%' ORDER BY cognome
In questo caso l'oggetto Recordset restituito può essere sfogliato in maniera ordinata. Nel caso
fossero presenti solo due record in grado di soddisfare la ricerca, ad esempio corrispondenti a
Carlo Aquilini e a Carlo Rossi, si otterrebbe che il primo record restituito sarà certamente
quello di Carlo Aquilini, indipendentemente dall'ordine di inserimento nel database. SQL
permette anche l'ordinamento in senso inverso:
SELECT * FROM nominativi WHERE nome LIKE '%carlo%' ORDER BY cognome DESC
SORT BY può essere utilizzato rispetto ad ogni tipo di campo. Nel caso di un ordinamento
svolto in base ad un campo testuale questo verrà eseguito sul normale ordine alfabetico, nel
caso di campi numerici avverrà in base alla normale successione dei numeri. Molto
interessante e sfruttato anche l'ordinamento svolto in base ai campi data/ora.
10.1 - Aggiornamento dei record
Formulare delle query per eseguire delle ricerche e delle estrazioni ragionate da una fonte di
dati non è l'unica cosa resa possibile da SQL. Sempre mediante il metodo Execute dell'oggetto
Connection di ADO, e sempre attraverso un comando SQL passato a tale metodo sotto forme
di normale stringa di JScript, è anche possibile aggiornare il contenuto di una tabella e dei suoi
record. Anche in questo caso si partirà da un esempio concreto. Supponiamo di aver
indicizzato all'interno della tabella nominativi il seguente record:
id = non ci interessa saperlo
nome = Carlo
cognome = Rossi
e_mail = [email protected]
Come ci si dovrebbe comportare un domani se Carlo Rossi ci comunicasse di aver cambiato
indirizzo e mail? La risposta è semplice, ed è contenuta tutta nel seguente comando. Nel caso
la nuova casella e mail da registrare fosse [email protected] basterebbe formulare quanto segue:
UPDATE nominativi SET e_mail = '[email protected]' WHERE nome = 'Carlo' AND cognome =
'Rossi'
In linguaggio naturale:
AGGIORNA la tabella nominativi PONENDO e_mail uguale a '[email protected]' IN QUEI
RECORD DOVE il nome è 'Carlo' E il cognome è 'Rossi'
Si fa qui la conoscenza di due nuove istruzioni SQL: UPDATE e SET, che vengono usate in
maniera concomitante. La prima specifica quale tabella verrà coinvolta dall'aggiornamento
specificato dalla seconda. La clausula WHERE rimane del tutto identica all'utilizzo che se ne è
fatto precedentemente. Questa volta però i record inquadrati dalla ricerca verranno aggiornati
secondo quanto specificato da SET, anziché essere semplicemente restituiti. E possibile
aggiornare in un colpo solo anche più di un campo, utilizzando la virgola come separatore. Nel
caso si fosse inserito nel database di esempio anche un campo contenente l'url della home
page degli utenti registrati sarebbe ora possibile aggiornare anche questo dato:
UPDATE nominativi SET e_mail = '[email protected]', home_page = 'http://www.rossi.it' WHERE
nome = 'Carlo' AND cognome = 'Rossi'
10.2 - Inserimento di un nuovo record
Un po' più laboriosa risulta invece la creazione di un nuovo record da aggiungere ad una
tabella esistente. Invece di ricorrere alla sola sintassi SQL e alla sua esecuzione tramite
Connection.Execute conviene sfruttare l'interfaccia messa a disposizione dall'oggetto
Recordset, sempre appartenente alla famiglia di ADO. Si supponga di dover realizzare una
pagina che consenta ad un utente di inserire il proprio nominativo all'interno del database in
questione. Per prima cosa è meglio disporre una semplice pagina HTML necessaria per l'invio
dei dati, tramite un form:
<html>
<head><title>Inserisci il tuo nome!</title></head>
<body>
<form action="inserisci.asp" method="post">
Nome:<br>
<input type="text" name="nome"><br>
Cognome:<br>
<input type="text" name="cognome"><br>
E mail:<br>
<input type="text" name="e_mail"><br><br>
<input type="submit" value="Inserisci">
</form>
</body>
</html>
Come è possibile vedere dal codice i dati inseriti vengono passati alla pagina inserisci.asp, il cui
codice è il seguente:
<%@ LANGUAGE = JScript %>
<%
// Ricevo i dati dal form
var nome = Request.Form("nome");
var cognome = Request.Form("cognome");
var e_mail = Request.Form("e_mail");
// Istanzio l'oggetto Connection
var ADOConn = new ActiveXObject("ADODB.Connection");
// Creo la STRINGA DI CONNESSIONE
var strConn = "";
strConn += "driver={Microsoft Access Driver (*.mdb)};";
strConn += "dbq=" + Server.MapPath("prova.mdb");
// Apro la connessione al database selezionato
ADOConn.Open(strConn);
// Istanzio Recordset
var RecSet = new ActiveXObject("ADODB.Recordset");
// Collego Recordset alla connessione aperta
RecSet.Open("nominativi",ADOConn,3,3);
// Formulo il nuovo record
RecSet.AddNew();
RecSet("nome") = nome;
RecSet("cognome") = cognome;
RecSet("e_mail") = e_mail;
// Aggiorno la tabella
RecSet.Update();
// Chiudo Recordset
RecSet.Close();
// Chiudo la connessione
ADOConn.Close();
%>
<html>
<head><title>Inserimento effettuato</title></head>
<body>
Inserimento effettuato!
</body>
</html>
Si focalizzi ora la propria attenzione sulle seguenti righe estratte dal codice:
RecSet = new ActiveXObject("ADODB.Recordset");
RecSet.Open("nominativi",ADOConn,3,3);
RecSet.AddNew();
RecSet("nome") = nome;
RecSet("cognome") = cognome;
RecSet("e_mail") = e_mail;
RecSet.Update();
RecSet.Close();
E' tramite questa serie di operazioni che l'inserimento di un nuovo record nella tabella
nominativi è reso possibile. Una volta istanziato l'oggetto Recordset viene avviata una
connessione alla tabella nominativi della fonte di dati giù puntata da ADOConn. Il metodo
AddNew() inserisce un nuovo record, inizialmente vuoto, all'interno del Recordset. E'
sufficiente quindi riempire uno ad uno i singoli campi del record, ad esclusione del contatore
chiave primaria id, che viene incrementato automaticamente. Una volta riempiti i singoli campi
è sufficiente lanciare il metodo Update() per far sì che l'aggiunta si rifletta sulla fonte di dati
specificata. A questo punto la connessione può concludersi, in quanto l'operazione desiderata è
stata portata a termine.
10.3 - Conclusioni
Come già ribadito più volte non si può credere che la comprensione di questi paragrafi
esaurisca la conoscenza di SQL. L'argomento è anzi così vasto da fornire sufficienti spunti per
la realizzazione di intere guide dedicate, alcune delle quali tranquillamente reperibili in rete.
Quello che si è visto tra queste righe è solo la base necessaria ed indispensabile per poter
comprendere gli esempi futuri e per poter maneggiare con un minimo di indipendenza le
proprie applicazioni basate sull'accesso ODBC. I prossimi capitoli chiariranno in tal senso
quanto descritto sinora, attraverso tutorial e pratici esempi.
Capitolo 11 - Un motore di ricerca con ASP (Parte 1)
11.0 - Introduzione al tutorial
Con questo capitolo, e con il successivo, si intende guidare il lettore alla realizzazione di un
motore di ricerca, mettendo a frutto le nozioni acquisite nel corso delle lezioni precedenti, ed in
particolare dei capitoli 9 e 10 relativi all'utilizzo delle fonti di dati di Access tramite ADO e SQL.
Per seguire e realizzare quanto suggerito da questo tutorial è necessario disporre, oltre della
solita postazione windows munita di un server come IIS o PWS, di una copia di Access,
preferibilmente la versione 2000, anche se le versioni come la 97 o la 98 sono ugualmente
valide.
Quello che si intende realizzare è un servizio di ricerca costruito sulla consultazione di una base
di dati (ossia un database) all'interno della quale sono indicizzate una serie di pagine Web
consultabili tramite un comune browser. Non si intende certo analizzare le tecniche di
realizzazione dei più complessi motori di ricerca, come quelli sfruttati quotidianamente in rete
dagli utenti di internet, che indicizzato milioni e milioni di pagine. Si intende invece riprodurre
quel servizio che va comunemente sotto il nome di "motore di ricerca interno", utilizzato
soprattutto da siti dagli ampi contenuti per aiutare il visitatore nel localizzare velocemente
mediante l'utilizzo di alcune parole chiave la risorsa cercata. Si tratta perciò di un servizio
efficiente finché le pagine memorizzate nel database non superino diverse migliaia, numero più
che sufficiente anche per un sito di ampie dimensioni!
11.1 - Concettualizzazione del problema
Come al solito è necessario partire chiarendo le proprie idee sul da farsi, concettualizzando il
problema proposto e predisponendo una schema per la successiva soluzione del medesimo.
Come già annunciato l'intero servizio fa perno sul fatto di aver precedentemente indicizzato
una serie di pagine Web. Ma come realizzare tale opera? L'utilizzo di un database risulta in casi
come questo estremamente utile, sia per l'efficienza del servizio stesso, sia per rendere più
facile il compito dello sviluppatore. Stabilito quindi che l'archiviazione dei dati avverrà tramite
un database resta da decidere una volta per tutte in che maniera questo dovrà avvenire.
Quello che interessa nel problema è la possibilità di ricercare nell'intero archivio una serie di
pagine attinenti con i concetti espressi dall'utente mediante l'utilizzo di alcune parole chiave.
Sarà quindi necessario dotare la tabella creata allo scopo all'interno del database almeno dei
seguenti quattro campi:

Titolo della pagina Web

URL della pagina Web

Descrizione dei contenuti della pagina Web

Una serie di parole chiave associate alla pagina Web
Successivamente è necessario realizzare uno script, integrato ovviamente in una o più pagine
ASP, in grado di accedere alla fonte di dati al fine di estrarne delle informazioni che soddisfino
la richiesta dell'utente. Come visto nei due capitoli precedenti questo diventa molto semplice
grazie all'utilizzo di ADO (ActiveX Data Object), oggetto ActiveX utilizzabile per accedere con
semplicità a database di varia natura. Per coerenza con lo standard di ASP il database
utilizzato per l'esempio sarà generato con Access 2000.
11.2 - Costruzione del database
Una volta chiarito quanto detto sinora è possibile procedere alla realizzazione del servizio,
partendo ovviamente dall'allestimento del database necessario. Si apra la propria copia di
Access e dal menù iniziale si selezioni la voce "Database di Access vuoto". Nella finestra
seguente verrà chiesto dove si intende posizionare il file e che nome dargli. Si scelga la propria
cartella di lavoro interna al Web Server utilizzato, e si nomini il database come archivio.mdb.
Fatto ciò il lettore disporrà di un database completamente vuoto, con la possibilità di interagire
con esso tramite gli strumenti visuali messi a disposizione da Access. A questo software va
dato il merito dell'estrema semplicità d'uso. Bastano pochi tentativi per acquisire la pratica
necessaria alla creazione di database anche piuttosto complessi. Per prima cosa bisogna
procedere ora inserendo nel database una nuova tabella. Le tabelle sono elementi cardine nei
database come quelli prodotti da Access, ed è al loro interno che tutte le informazioni vengono
archiviate. Si selezioni allora l'opzione "Crea una tabella in visualizzazione struttura", uno dei
tool più efficaci di questo software. Da qui è ora possibile gestire i campi che faranno parte
della tabella, specificandone il nome ed il tipo. Come primo campo si inserisca il campo id, di
tipo contatore. Questo tipo di dato permette di associare in maniera automatica un numero
intero positivo ad ogni record della tabella. Successivamente selezionare il campo id come
chiave primaria. In Access è semplice selezionare un campo come chiave primaria: basta
evidenziare in visualizzazione struttura il campo stesso e quindi utilizzare l'icona raffigurante
una chiave presente nella barra degli strumenti del programma. A questo punto è possibile
dotare la tabella dei quattro campi sopra discussi, seguendo questo schema:

campo titolo, di tipo testo. Tra i valori del riquadro sottostante regolare la voce
"Dimensione campo" su 100. Questo farà sì che i titoli delle pagine Web che
indicizzeremo in seguito possano avere lunghezza compresa tra 1 e 100 caratteri.

campo url, di tipo testo. Si regoli anche qui su 100 la dimensione del campo. In questa
maniera si è previdenti nei confronti di URL molto lunghi ed articolati.

campo descrizione, di tipo memo. Il tipo memo consente l'archiviazione di dati dalla
lunghezza non determinata a priori. In questo caso, perciò, le descrizioni che
associeremo alle singole pagine Web potranno avere lunghezza variabile.

campo keywords, di tipo memo. Vale lo stesso discorso di sopra. Grazie al tipo memo
sarà possibile inserire quante parole chiave si ritiene necesario.
Adesso è necessario salvare quanto fatto. Access chiederà di assegnare un nome alla tabella.
Per questo esempio si scelga il nome pagineweb. Il lavoro di costruzione del database è
completo. E' anche possibile chiudere la finestra di visualizzazione struttura. Si inseriscano ora,
allo scopo di effettuare dei test in seguito, una serie di pagine Web dimostrative. Per farlo
basta un doppio click sul nome della tabella nella finestra principale del database. Ad esempio:

titolo: Sauron Software - Risorse per il webmaster

url: http://www.sauronsoftware.it

descrizione: Una serie di raccolte utili a tutti i tipi di webmaster

keywords: risorse webmaster HTML ASP JavaScript icone fonts gif animate mailing list
Come è possibile osservare il campo keywords è rappresentato da una serie di parole separate
tra di loro da uno spazio. Questa sono le famose parole chiave associate alla pagina Web. Ciò
significa che se un utente digiterà in fase di ricerca la parola "HTML" vedrà certamente tra i
risultati la pagina "Sauron Software - Risorse per il webmaster", mentre non vedrà altre
eventuali pagine archiviate che non contengano nel campo keywords questa parola chiave.
Seguendo il modello proposto si inseriscano perciò una serie di pagine Web per testare poi
l'esempio. Dopo aver salvato nuovamente il lavoro è anche possibile chiudere Access. Si passa
ora alla realizzazione della pagina di consultazione della fonte di dati realizzata.
11.3 - Eseguire la ricerca
Dopo aver predisposto il database che si intende utilizzare è necessario implementare uno
script in grado di fare da tramite tra l'utente e i dati immagazzinati nell'archivio. Poiché tale
script per funzionare correttamente necessita di una serie di dati in input da parte dell'utente
bisognerà senz'altro predisporre un form HTML. Questo è piuttosto semplice da realizzare. Le
interrogazioni al database, quindi, partiranno dal seguente documento Web, che per praticità
può essere battezzato cerca.htm:
<html>
<head>
<title>Motore di ricerca</title>
</head>
<body>
<form action="risultati.asp" method="POST">
Parole chiave:<br>
<input type="text" name="keywords">
<input type="submit" value="CERCA">
</form>
</body>
</html>
Le parole inserite dall'utente vengono perciò inviate, con metodo POST, al documento
risultati.asp. Come è facile intuire sarà questa pagina a dover inglobare tanto lo script in grado
di eseguire la ricerca, quanto quello in grado di produrre il giusto output corrispondente. Ci si
dedicherà perciò ora alla realizzazione di questo importante file.
Come prima cosa è necessario recuperare le parole inviate tramite il form di cerca.htm:
var testo = String(Request.Form("keywords"));
if (testo=="" || testo=="undefined") Response.Redirect("cerca.htm");
Nel caso l'utente avesse inviato il contenuto del form senza inserire alcuna parola chiave allora
il processo di ricerca nemmeno sarà avviato, e l'utente sarà rimandato direttamente a
cerca.htm, in maniera a lui invisibile.
Tutte le parole chiave inserite saranno naturalmente suddivise tra di loro mediante uno o più
spazi. E' allora necessario separare tra di loro le singole parole inviate, facendo ricorso al
metodo split dell'oggetto String di JScript:
var keywords = testo.split(" ");
keywords sarà dunque un'Array dove ogni elemento rappresenterà una parola distinta. Fatto
ciò è possibile aprire la connessione verso la fonte di dati che si intende consultare:
var stringaDiConnessione = "driver={Microsoft Access Driver (*.mdb)};dbq=";
stringaDiConnessione += Server.MapPath("archivio.mdb");
var conn = new ActiveXObject("ADODB.Connection");
conn.Open(stringaDiConnessione);
Il passo successivo consiste nel formulare correttamente una query, ossia una richiesta, basata
sulla sintassi di SQL, da sottoporre alla fonte di dati verso la quale la connessione è stata
aperta. Si faccia particolare attenzione alla seguente porzione di codice:
var sql = "SELECT * FROM pagineweb WHERE";
for (i=0;i<keywords.length;i++) {
if (i>0) sql += " AND";
sql += "keywords LIKE '%" + keywords[i] + "%'";
}
Se ad esempio l'utente avesse digitato la coppia di parole "gif animate" la query risultante
sarebbe:
SELECT * FROM pagineweb WHERE keywords LIKE '%gif%' AND keywords LIKE '%animate%'
Formulata correttamente la query sarà possibile chiedere tramite ADO la restituzione dei
record competenti:
var ris = conn.Execute(sql);
La variabile ris, quindi, conterrà un riferimento ad un oggetto Recordset rappresentante tutti i
record ospitati dalla tabella pagineweb in grado di soddisfare la richiesta fatta. Non resta che
scorrere tale Recordset inserendo nell'output una rappresentazione HTML dei suoi contenuti:
if (ris.EOF) Response.Write("<b>NESSUN DOCUMENTO TROVATO</b>");
else while (!ris.EOF) {
Response.Write("<p><a href='" + ris("url") + "'>");
Response.Write(ris("titolo"));
Response.Write("</a><br>");
Response.Write(ris("descrizione"));
Response.Write("</p>");
ris.MoveNext();
}
La connessione al database può quindi essere chiusa, la pagina ASP è completa.
conn.Close();
Il codice che ne risulta è il seguente:
<%@ LANGUAGE = JScript %>
<%
var testo = String(Request.Form("keywords"));
if (testo=="" || testo=="undefined") Response.Redirect("cerca.htm");
var keywords = testo.split(" ");
var stringaDiConnessione = "driver={Microsoft Access Driver (*.mdb)};dbq=";
stringaDiConnessione += Server.MapPath("archivio.mdb");
var conn = new ActiveXObject("ADODB.Connection");
conn.Open(stringaDiConnessione);
var sql = "SELECT * FROM pagineweb WHERE";
for (i=0;i<keywords.length;i++) {
if (i>0) sql += " AND";
sql += " keywords LIKE '%" + keywords[i] + "%'";
}
var ris = conn.Execute(sql);
if (ris.EOF) Response.Write("<b>NESSUN DOCUMENTO TROVATO</b>");
else while (!ris.EOF) {
Response.Write("<p><a href='" + ris("url") + "'>");
Response.Write(ris("titolo"));
Response.Write("</a><br>");
Response.Write(ris("descrizione"));
Response.Write("</p>");
ris.MoveNext();
}
conn.Close();
%>
Questo breve e conciso script è in grado di eseguire la basilare ricerca sul database che si
voleva soddisfare. Al solito si tratta di un puro esempio, che lascia spazio ad ampi margini di
miglioramento. Si propongono quindi al lettore delle possibili migliorie, da eseguire come
esercizio:

possibilità da parte dell'utente di stabilire diverse modalità di ricerca, come "tutte le
parole", "almeno una parola" e "frase esatta". L'esempio appena visto soddisferebbe il
caso "tutte le parole". Basta modificare la parte di codice che assembla la query, in
base alla scelta fatta dall'utente. Nel caso "almeno una parola" basterebbe infatti
sostituire nella query l'operatore OR all'operatore AND. Nel caso "frase esatta", invece,
non occorre far ricorso ad alcun operatore, basta specificare nella quary keywords LIKE
'%parola1 parola2 parola3%'. Neanche lo split sarebbe più necessario.

aumentare la robustezza dello script. Qualora l'utente inserisse infatti un carattere di
apostrofo nel campo di ricerca lo script restituirebbe un errore. Questo perchè il
carattere apostrofo ha particolare significato nella sintassi SQL. Bisogna quindi
accertarsi di sostituire ogni carattere di apostrofo ' con un doppio apostrofo ''. E' questo
lo stratagemma usato da SQL per inserire nei campi stringa delle query il carattere
riservato.

fondere le pagine cerca.htm e ricerca.asp in un unico documento, per maggiore
praticità.

il metodo split, per quanto di facile utilizzo, non ha la potenza, ad esempio, di uno
StringTokenizer di Java, o degli equivalenti metodi di Perl. Se l'utente dovesse inserire
consecutivamente due spazi, infatti, il metodo split inserirebbe un elemento totalmente
vuoto nell'Array risultante e la ricerca sarebbe in questo caso falsata. E' però possibile
lavorare intorno a split per ottenere un Array più accurato e privo di questo
inconveniente.
Il prossimo capitolo concluderà il tutorial, proponendo la realizzazione di un pannello di
controllo per l'inserimento via interfaccia Web di nuove pagine all'interno dell'archivio.
Capitolo 12 - Un motore di ricerca con ASP (Parte 2)
12.0 - Sui pannelli di controllo...
Dopo aver realizzato un basilare servizio di archiviazione, indicizzazione e ricerca di pagine
Web (cfr. Capitolo 11) si conclude in questa seconda parte il tutorial relativo
all'approfondimento delle tecniche di utilizzo del componente ADO (ActiveX Data Object). Nel
corso del capitolo precedente, infatti, sono state prese in esame le tematiche relative alla
possibilità di eseguire ricerce tra le informazioni archiviate all'interno di una comune fonte di
dati di Access, e lo studio era legato soprattutto all'utilizzo del particolare oggetto di ADO
denominato Connection, in grado appunto di stabilire connessioni con un database e di estrarre
da esso, seguendo un criterio dettato mediante SQL, una serie di dati ritenuti interessanti per
l'applicazione. In questo capitolo lo studio si incentrerà maggiormente sull'oggetto Recordset,
sempre di ADO, utile per effettuare inserimenti di nuove informazioni all'interno della stessa
fonte di dati utilizzata per l'esempio precedente. In parole più spicciole quello che sarà
realizzato nei paragrafi successivi altro non sarà che un pannello di controllo, funzionante su
interfaccia Web, utile all'ipotetico amministratore del motore di ricerca per aggiornare i record
presenti nell'archivio precedentemente creato. Si tratta di una funzionalità molto importante
per i servizi di questo genere, in quanto permette di aggiornare i dati originariamente
specificati in maniera remota, ossia senza dover disporre di una copia del database sulla
propria macchina di lavoro, interagendo perciò direttamente con quanto disponibile in rete.
Senza tale pannello di controllo l'aggiunta di una nuova pagina all'interno del database
utilizzato risulterebbe un'operazione piuttosto laboriosa: bisognerebbe infatti inserire il nuovo
record agendo direttamente con Access su una copia locale di archivio.mdb, per poi
sovrascrivere la copia in rete tramite, ad esempio, un accesso FTP. Si tratta di un metodo
altamente sconveniente, in quanto per aggiungere pochi caratteri è necessario modificare,
trasportare e sovrascrivere un archivio che potenzialmente può avere dimensioni rilevanti,
causando anche dei momentanei malfunzionamenti del servizio nel momento in cui si
sovrascrive la copia in rete. Un pannello di controllo via Web, invece, rende notevolmente più
semplice questa serie di operazioni. L'inserimento di un nuovo record, in questo caso, può
essere eseguito semplicemente collegandosi ad una pagina Web, tramite un normale browser,
specificando quindi mediante un form HTML le specifiche del nuovo record e quindi
aggiornando in tempo reale direttamente la copia disponibile in rete, senza causare disagi,
senza dover utilizzare altri software aggiuntivi, e senza che siano richieste particolari
conoscenze tecniche. Il più delle volte, infatti, capita che chi gestisca un servizio non sia la
stessa persona che lo ha realizzato. Si immagini ad esempio che il motore di ricerca oggetto di
questo studio stia venendo realizzando per un ipotetico committente, che di programmazione
Web, di ASP, di ADO e di tutte le tematiche collegate è completamente digiuno. Tramite un
pannello di controllo il cliente potrà aggiornare il database realizzato dallo sviluppatore senza
doversi rivolgere allo sviluppatore stesso. Inoltre un pannello di controllo Web permette
l'aggiornamento dei dati da una qualsiasi postazione che abbia accesso ad Internet e disponga
di un comune Web browser, mentre l'aggiornamento in locale richiede almeno l'utilizzo di una
copia di Access e di un client FTP, strumenti solitamente non in mano all'utente medio. Per
concludere: quando si sviluppa un'applicazione Web basata sull'accesso ad una fonte di dati
che necessiti di continui aggiornamenti la realizzazione di un pannello di controllo via Web è
quasi un passo obbligato.
12.1 - Il form HTML
Premesso quanto detto nel paragrafo precedente ci si accinge ora allo sviluppo di un basilare
pannello di controllo in grado di aggiornare archivio.mdb aggiungendo in esso un nuovo record
alla volta, specificato dal gestore del servizio attraverso una semplice pagina Web. Si torni
quindi a lavorare nella stessa cartella utilizzata per testare i codici presentati nel Capitolo 11,
in quanto in questo momento si sta completando l'esempio precedente, e non si sta
realizzando perciò un codice di per sé indipendente. Come già visto più volte un utente è in
grado di passare dei dati ad una pagina Web sfruttando il costrutto FORM messo a disposizione
da HTML. Come prima cosa, quindi, sarà necessario realizzare un semplice modulo in grado di
consentire all'amministratore del servizio di specificare i dati che insieme costituiscono il record
che si intende accodare a quelli già presenti nell'archivio. Si realizzi quindi la pagina
pannello.htm come segue:
<html>
<head>
<title>Pannello di controllo</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body bgcolor="#FFFFFF">
<div align="center">
<p><b><font size="2" face="Verdana, Arial, Helvetica, sans-serif">BENVENUTO
NEL PANNELLO DI CONTROLLO!</font></b></p>
<p><font face="Verdana, Arial, Helvetica, sans-serif" size="2">Grazie a questo
pannello è possibile inserire nuovi documenti all'interno del motore
di ricerca. Utilizza il modulo sottostante.</font></p>
<p> </p>
<form name="" method="post" action="inserisci_record.asp">
<table width="350" border="0" cellspacing="1" cellpadding="0">
<tr>
<td align="left"><font face="Verdana, Arial, Helvetica, sans-serif" size="2"><b>Password:</b></font></td>
<td align="right"> <font face="Verdana, Arial, Helvetica, sans-serif" size="2">
<input type="password" name="password" size="30">
</font></td>
</tr>
<tr>
<td> </td>
<td> </td>
</tr>
<tr>
<td align="left"><font face="Verdana, Arial, Helvetica, sans-serif" size="2"><b>Titolo:</b></font></td>
<td align="right"> <font face="Verdana, Arial, Helvetica, sans-serif" size="2">
<input type="text" name="titolo" size="30">
</font></td>
</tr>
<tr>
<td align="left"><font face="Verdana, Arial, Helvetica, sans-serif" size="2"><b>URL:</b></font></td>
<td align="right"> <font face="Verdana, Arial, Helvetica, sans-serif" size="2">
<input type="text" name="url" size="30" value="http://">
</font></td>
</tr>
<tr>
<td align="left"><font face="Verdana, Arial, Helvetica, sans-serif" size="2"><b>Descrizione:</b></font></td>
<td align="right"> <font face="Verdana, Arial, Helvetica, sans-serif" size="2">
<input type="text" name="descrizione" size="30">
</font></td>
</tr>
<tr>
<td align="left"><font face="Verdana, Arial, Helvetica, sans-serif" size="2"><b>Keywords*:</b></font></td>
<td align="right"> <font face="Verdana, Arial, Helvetica, sans-serif" size="2">
<input type="text" name="keywords" size="30">
</font></td>
</tr>
<tr>
<td> </td>
<td> </td>
</tr>
<tr align="center">
<td colspan="2"> <font face="Verdana, Arial, Helvetica, sans-serif" size="2">
<input type="submit" value="AGGIUNGI IL RECORD">
</font></td>
</tr>
</table>
</form>
<p><font face="Verdana, Arial, Helvetica, sans-serif" size="2"><b>(*)</b> le
parole chiave devono essere separate tra di loro tramite uno spazio</font></p>
</div>
</body>
</html>
Analizzando il codice sopra riportato è possibile estrarre le seguenti informazioni relativamente
al form proposto.

action="inserisci_record.asp" - questo significa che i dati inseriti verranno passati al
documento inserisci_record.asp, del quale ci si occuperà nel prossimo paragrafo

method="post" - il metodo utilizzato per l'invio dei dati è POST. All'interno di
inserisci_record.asp, quindi, potranno essere recuperati utilizzando la sintassi
Request.Form("nomeCampo")

campo password - naturalmente il pannello di controllo non deve essere sfruttato da
tutti, ma solo dalle persone autorizzate. Per questo è prevista l'autentificazione
mediante passoword.

campo titolo - qui andrà specificato il titolo della pagina Web da indicizzare

campo url - qui andrà specificato l'url della pagina Web da indicizzare

campo descrizione - qui andrà specificata una breve descrizione per la pagina Web da
indicizzare

campo keywords - qui andrà specificata la lista delle parole chiave da associare alla
pagina Web che si deve indicizzare
Si osservi inoltre come gli ultimi quattro campi descritti (titolo, url, descrizione e keywords)
corrispondano esattamente ai quattro campi scelti per rappresentare una pagina Web
all'interno del database. Come è facile intendere il contenuto di questi campi verrà dunque
prelevato ed inserito così come è all'interno dell'archivio.
12.2 - Inserimento del nuovo record
I compiti di inserisci_record.asp, a questo punto, possono essere riassunti nei seguenti passi:

recupero dei dati presentati nel form

validazione della password inserita

validazione dei quattro campi che costituiscono il nuovo record da inserire

nel caso i precedenti controlli abbiano dato buon esito, inserimento del nuovo record
all'interno dell'archivio

visualizzazione di un responso dell'operazione nell'output
Tenendo a mente quanto detto nel capitoli precedenti relativamente all'utilizzo di ADO se ne
ricava il seguente codice:
<%@ LANGUAGE = JScript %>
<%
// SPECIFICA QUI LA PASSWORD
var password_di_accesso = "pannello";
// RECUPERO DEL CONTENUTO DEL FORM
var password = String(Request.Form("password"));
var titolo = String(Request.Form("titolo"));
var url = String(Request.Form("url"));
var descrizione = String(Request.Form("descrizione"));
var keywords = String(Request.Form("keywords"));
// VARIABILI DI UTILITA'
var errore = 0;
var fatto = false;
// CONTROLLO PASSWORD
if (password!=password_di_accesso) errore = 1;
else {
// CONTROLLO CAMPI
if (titolo.length<1 || titolo.length>100) errore = 2;
if (url.length<1 || url.length>100) errore = 2;
if (descrizione.length<1) errore = 2;
if (keywords.length<1) errore = 2;
if (errore==0) {
// INSERIMENTO DEL NUOVO RECORD
stringaDiConnessione = "driver={Microsoft Access Driver (*.mdb)};dbq=";
stringaDiConnessione += Server.MapPath("archivio.mdb");
conn = new ActiveXObject("ADODB.Connection");
conn.Open(stringaDiConnessione);
rs = new ActiveXObject("ADODB.Recordset");
rs.Open("pagineweb",conn,3,3);
rs.AddNew();
rs("titolo") = titolo;
rs("url") = url;
rs("descrizione") = descrizione;
rs("keywords") = keywords;
rs.Update();
rs.Close();
conn.Close();
fatto = true;
}
}
%>
<html>
<head>
<title>Inserimento di un nuovo record</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body bgcolor="#FFFFFF">
<div align="center">
<% if (errore==1) { %>
<p><font face="Verdana, Arial, Helvetica, sansserif" size="2" color="#660000"><b>ERRORE #1</b>: PASSWORD NON CORRETTA</font></p>
<% } %>
<% if (errore==2) { %>
<p><font face="Verdana, Arial, Helvetica, sansserif" size="2" color="#660000"><b>ERRORE #2</b>: CAMPI NON VALIDI</font></p>
<p><font face="Verdana, Arial, Helvetica, sans-serif" size="2">Si ricordano le lunghezze massime previste:</font></p>
<p><font face="Verdana, Arial, Helvetica, sans-serif" size="2">
<b>TITOLO</b>: compreso tra 1 e 100 caratteri<br>
<b>URL</b>: compreso tra 1 e 100 caratteri<br>
<b>DESCRIZIONE</b>: almeno un carattere<br>
<b>KEYWORDS</b>: almeno un carattere
</font></p>
<% } %>
<% if (errore!=0) { %>
<p><font face="Verdana, Arial, Helvetica, sansserif" size="2">< <a href="javascript:history.back()">TORNA INDIETRO</a> ></font></p>
<% } %>
<% if (fatto) { %>
<p><font face="Verdana, Arial, Helvetica, sansserif" size="2" color="#006600"><b>OPERAZIONE ESEGUITA CON SUCCESSO!</b></font></p>
<p><font face="Verdana, Arial, Helvetica, sansserif" size="2">< <a href="pannello.htm">INSERISCI ALTRO RECORD</a> ></font></p>
<% } %>
</div>
</body>
</html>
Tralasciando i commenti relativi alla forma scelta per realizzare il documento e alle tecniche
sfruttate per eseguire i controlli si passi all'analisi dei due punti chiave di inserisci_record.asp.
Il primo è relativo all'autentificazione della password. Si è scelta una via molto semplice: la
password corretta è stata memorizzata direttamente all'interno di una variabile JScript
(password_di_accesso) facente parte del codice stesso. Si tratta di una soluzione poco pratica
nei termini del servizio (se l'amministratore volesse modificare la propria password dovrebbe
mettere mano al codice, con risultati spesso catastrofici!), ma comunque conveniente per
l'esempio in fase di studio, il cui obiettivo non è quello di descrivere come si realizzino zone ad
accesso limitato. Un tema del genere sarà argomento di studio nei capitoli 15 e 16. La seconda
parte interessante è quella relativa all'accesso al database e all'inserimento del nuovo record:
stringaDiConnessione = "driver={Microsoft Access Driver (*.mdb)};dbq=";
stringaDiConnessione += Server.MapPath("archivio.mdb");
conn = new ActiveXObject("ADODB.Connection");
conn.Open(stringaDiConnessione);
rs = new ActiveXObject("ADODB.Recordset");
rs.Open("pagineweb",conn,3,3);
rs.AddNew();
rs("titolo") = titolo;
rs("url") = url;
rs("descrizione") = descrizione;
rs("keywords") = keywords;
rs.Update();
rs.Close();
conn.Close();
La parte evidenziata in verde è identica a quella già descritta nel Capitolo 11, il cui scopo è
quello di utilizzare l'oggetto ADODB.Connection per connettersi ad archivio.mdb. La parte in
arancione, invece, evidenzia l'inserimento del nuovo record all'interno della tabella pagineweb,
mediante l'utilizzo dell'oggetto ADODB.Recordset.
12.3 - Conclusioni e spunti
Si conclude qui questo tutorial dedicato all'approfondimento delle tecniche di accesso alle fonti
di dati supportate da ADO. E' stato anche fornito un approccio alla realizzazione dei pannelli di
controllo per interfaccia Web, strumenti da sempre apprezzati dai clienti delle applicazioni. Si
invita il lettore ad approfondire l'argomento completando il pannello proposto con l'aggiunta di
altri servizi, quali ad esempio la possibilità di cancellare record precedentemente inseriti
(comando DELETE di SQL) e quella di modificare i contenuti di un record già esistente
(comando UPDATE di SQL).
Capitolo 13 - L'oggetto built-in Session
13.0 - Scopo e utilizzo dell'oggetto Session
Session, il quarto oggetto built-in sul percorso di questa guida, consente di gestire le
informazioni relative alla sessione di un utente. Si supponga di utilizzare una normale pagina
ASP contenente la seguente riga di codice JScript:
var stringa = "Ciao, come stai?";
La variabile stringa appena dichiarata ha un ciclo di vita che termina nell'esatto momento in cui
l'interprete raggiunge la fine del codice scritto. In poche parole questo significa che, tendando
di richiamare la variabile citata all'interno di un secondo documento, si otterrà un errore, in
quanto risulterà non definita. Questo perchè ciascuno script non è in grado di vedere le
variabili utilizzate da altri script, e quindi nemmeno di condividerle con essi.
Molto spesso, però, alcuni particolari tipi di applicazione Web richiedono che alcuni dati
possano essere trasportati da una pagina ad un'altra. Si immagini, ad esempio, il classico
"carrello della spesa" utilizzato in tutti i negozi virtuali. L'utente che stabilisce di acquistare un
prodotto lo inserisce in maniera figurata all'interno del proprio carrello e quindi abbandona la
pagina utilizzata allo scopo, magari per andare alla ricerca di altri prodotti. Quando il suo giro
sarà completo, l'utente accederà al documento utile per ordinare quanto scelto
precedentemente. Come fare a questo punto per sapere cosa sia stato inserito pochi minuti
prima all'interno del carrello? Proprio a questo tipo di problema risponde l'oggetto Session, che
si fa carico di trasportare dei dati, unici e diversi per ogni utente, da una pagina ASP ad
un'altra. Si faccia una prova creando il documento session1.asp come segue:
<%@ LANGUAGE = JScript %>
<% Session("stringa") = "Ciao, come stai?"; %>
<html>
<head>
<title>Prova variabili Session, pag. 1</title>
</head>
<body>
<a href="session2.asp">Pagina successiva</a>
</body>
</html>
Si carichi il documento con il proprio browser, sfruttando il Web Server locale, e quindi si
acceda a session2.asp, il cui codice deve essere:
<%@ LANGUAGE = JScript %>
<html>
<head>
<title>Prova variabili Session, pag. 2</title>
</head>
<body>
<%=Session("stringa")%>
</body>
</html>
Il messaggio "Ciao, come stai?" verrà stampato nell'output inviato al browser. Se ne deduce
che la variabile Session("stringa") è ancora viva e vegeta, e il suo contenuto non è stato
distrutto al termine dell'esecuzione della pagina precedente. E' anche possibile evincere quale
tipo di sintassi debba essere utilizzata per sfruttare in maniera basilare questo tipo di oggetto.
E' possibile accedere, in lettura o in scrittura, ad una particolare variabile Session semplicente
usando il riferimento:
Session("nomeVariabile")
L'oggetto built-in Session, inoltre, dispone anche della seguente serie di proprietà, metodi e
collezioni:
Proprietà
CodePage
Un intero che specifica quale codifica sia applicata al testo (ad
esempio il codice corrispondente alla codifica ANSI è 1252).
LCID
Sta per "locale identifier", si tratta di una sigla che indica quale
identificatore locale debba essere usato per visualizzare le pagine. Le
sigle nazionali sono le stesse adoperate negli standard internazionali.
SessionID
Proprietà di sola lettura, restituisce l'id assegnato alla sessione dal
Web Server.
Timeout
Indica, in minuti, quanto tempo di inattività debba trascorrere prima
che il Server distrugga automaticamente l'oggetto Session.
Metodi
Abandon
Distrugge l'oggetto Session su cui viene richiamato, e libera le risorse
occupate.
Collezioni
Contents
Collezione di tutte le variabili Session attive per un dato utente.
StaticObjects
Collezione di tutti gli oggetti che hanno ricevuto lo scope "session".
13.1 - Funzionamento interno di un oggetto Session
Prima di proseguire oltre è necessario comprendere quale meccanismo si celi dietro al
funzionamento di Session. Quando un oggetto di questo tipo viene creato, il server si fa carico
di associargli automaticamente un codice identificativo (solitamente costituito da una lunga
serie di numeri e caratteri), unico e distinto per ogni istanza ottenuta. Tale ID, oltre ad essere
conservato nella memoria del server, viene contemporaneamente inviato anche al browser del
visitatore che, se abilitato alla ricezione dei cookie, è in grado di ricordarlo per tutta la durata
della visita al sito. Ad ogni nuova richiesta HTTP inviata dallo stesso utente allo stesso host, il
Web Server può riconoscere il visitatore proprio attraverso tale ID posizionato
precedentemente nel suo browser. Quando in una particolare pagina ASP viene creata una
variabile Session, quindi, questa viene conservata dal server in coppia con il codice
identificativo assegnato al visitatore. Nel corso di richieste successive è perciò possibile
riaccostare al determinato utente le variabili Session definite precedentemente. La proprietà
Session.SessionID permette proprio di venire a conoscenza del codice identificativo associato
ad un utente. Il lettore può divertirsi ad utilizzarla per scoprire in che maniera sono strutturati
gli ID del proprio Web Server.
13.2 - La proprietà Session.Timeout
Le variabili Session, benchè sopravvivano alla morte delle singole pagine, non sono comunque
eterne, ed hanno anche loro durata temporanea. Solitamente un oggetto Session viene
distrutto (e quindi il suo contenuto perduto) dopo 20 minuti dall'ultima richiesta HTTP
effettuata dal visitatore al server. Tale misura di tempo può essere modificata in diversi modi.
Prima di tutto è in grado di farlo l'amministratore del server attraverso il proprio pannello di
controllo. Tale modifica riguarderà tutti gli oggetti Session creati da quel momento in poi, ma è
anche possibile variare la durata di una singola istanza dell'oggetto direttamente tramite
codice ASP, senza essere perciò amministratori del server. La proprietà utile per far ciò è
Session.Timeout, che rappresenta in minuti il tempo di inattività massimo consentito prima che
una variabile Session muoia. Ponendo il timeout pari a 10, ad esempio, accadrà che l'oggetto
Session associato verrà distrutto trascorsi 10 minuti dal momento in cui l'utente interessato
dalla modifica abbia richiesto al server per l'ultima volta una pagina appartenente al sito. Se si
lavora con pagine dai contenuti lunghi, che solitamente vengono lette in maniera approfondita,
è quindi d'uopo prolungare la durata della sessione. Viceversa, se il proprio sito contiene pochi
elementi per pagina e se si ha la certezza che il visitatore passi velocemente da un documento
ad un altro, allora si può pensare di diminuire il timeout in modo tale da rilasciare le risorse del
server in maniera più veloce e pratica.
13.3 - Il metodo Session.Abandon
In molti casi può essere utile distruggere un oggetto Session anzitempo rispetto a quanto
previsto dal valore di timeout impostato. Per questo è stato fornito il metodo Session.Abandon,
che termina all'istante una sessione. Questa funzione trova particolare utilità quando ad
esempio bisogna compiere delle operazione di "logout". Si pensi ad un'area ad accesso
riservata, che utilizza una variabile Session per tenere traccia del fatto che l'utente abbia
eseguito o meno l'operazione di login. Nel momento in cui l'utente decide di abbandonare
l'area attraverso l'apposita opzione, è necessario distruggere all'istante le variabili Session
utilizzate. In tale maniera si è certi che per riaccedere all'area sarà necessario eseguire
nuovamente il login. In questa maniera, inoltre, si mantiene anche un occhio di riguardo per il
server, che viene liberato all'istante dal peso di informazioni che si conoscono per certo come
non più utili. Per testare il metodo si crei la pagina session3.asp:
<%@ LANGUAGE = JScript %>
<% Session.Abandon(); %>
<html>
<head>
<title>Prova variabili Session, pag. 3</title>
</head>
<body>
<a href="session2.asp">Pagina precedente</a>
</body>
</html>
A questo punto si visitino i documenti creati nel corso di questo capitolo nel seguente ordine:
session1.asp, session2.asp, session3.asp, session2.asp. Il risultato sarà che la prima volta che
session2.asp viene visitata verrà mostrato il messaggio "Ciao, come stai?", mentre quando si
ripasserà per la seconda volta su di essa si scorgerà la scritta "undefined". Questo perché
session3.asp, tramite il metodo Abandon, ha chiuso la sessione e demolito ogni suo contenuto.
Per tornare a rivedere il messaggio "Ciao, come stai?" è pertanto necessario transitare
nuovamente su session1.asp.
Attenzione: per questo esperimento non debbono essere usati i tasti "indietro" ed "avanti" del
browser, in quanto in questo caso i documenti non vengono richiesti nuovamente al server, ma
vengono pescati dalla cache. Per provare in maniera corretta il tutto bisogna aggiornare
effettivamente ogni documento richiesto.
Capitolo 14 - Costruire un'area ad accesso riservato
14.0 - Presentazione del tutorial
Le conoscenze acquisite in materia di ActiveX, accesso ai database e oggetti built-in verranno
ora messe a frutto realizzando una semplice, quanto funzionale, area ad accesso riservato. Con
questo termine si è soliti indicare una sezione di un sito Web la cui visualizzazione, per diversi
motivi, non è pubblica, ma riservata ad un'utenza più ristretta. Ogni utente che vanti il
permesso di poter accedere a sezioni di questo tipo dispone solitamente di una coppia di valori
username-password, che può utilizzare per farsi riconoscere dal sistema e per farsi garantire
l'accesso alla porzione protetta del Web. I motivi che possono spingere alla realizzazione di un
servizio del genere sono molteplici, distinti da situazione in situazione. Almeno due sono
comunque i casi più tipici. Il primo di questi è rappresentato da alcune aziende che necessitino
di mettere a disposizione dei propri dipendenti delle informazioni riservate, comodamente
accessibili via Web, anche quando si è a diversi chilometri di distanza dalla sede. Il secondo
caso, invece, riguarda quei siti che richiedono una certa garanzia dalla propria utenza per
rendere loro un servizio migliore, o comunque per fornire un'applicazione la cui funzionalità sia
basata proprio sul fatto di saper distinguere un utente da un altro. La guida farà riferimento a
questo secondo caso, realizzando una pagina di iscrizione pubblica, grazie alla quale chiunque
potrà ottenere la propria coppia di valori username-password per accedere all'area protetta.
Nulla toglie, comunque, che l'esempio resti valido anche nel primo caso. Basterebbe infatti
eliminare la pagina di iscrizione pubblica e affidare la gestione e lo smistamento dell'utenza ad
un amministratore appositamente designato. In ogni caso il tutorial, come al solito, non vuole
rendere al lettore un servizio bello e pronto, quanto piuttosto guidarlo alla scoperta dei
meccanismi che devono essere appresi per affrontare un problema del genere, in maniera tale
che ognuno, in base alle differenti esigenze, possa poi essere in grado di realizzare
un'applicazione analoga, pensata però in maniera specifica per un particolare caso.
14.1 - Creazione del database
Dovendo fornire a numerosi utenti delle distinte coppie username-password, è facile
comprendere come sussista l'esigenza di memorizzare ciascuna di queste coppie all'interno del
Web server, in maniera riservata, affinché il programma di validazione dati sia in grado di
analizzare i dati immessi dall'utente e determinare in base ad essi se dover concedere o meno
l'accesso all'area protetta. La prima mossa necessaria consiste quindi nello stabilire in quale
maniera tali valori debbano essere memorizzati. Per l'esempio qui presentato si adotterà un
database di Access, protetto tramite password. Creare il database necessario per
l'esercitazione risulta abbasta semplice:
1. Si apra la propria copia di Access.
2. Si selezioni dal menù iniziale la voce "Database di Access vuoto".
3. Si nomini il nuovo database come utenza.mdb e lo si vada a posizionare all'interno della
cartella che si intende utilizzare per l'esperimento.
4. Dalla finestra di gestione del database si selezioni la voce "Crea una tabella in
visualizzazione Struttura".
5. Si definiscano ora i 3 seguenti campi: id, di tipo "Contatore"; username, di tipo "Testo",
password, di tipo "Testo" (opzionalmente nella maschera di input è possibile specificare la
dicitura "Password", per ottenere una visualizzazione in asterischi del campo quando il
database viene aperto in Access).
6. Si selezioni la riga corrispondende al campo id e si utilizzi il bottone raffigurante una chiave
per definire questo campo come chiave primaria.
7. Si salvi la tabella nominandola utenti.
Il database risulta già in questa forma adatto per l'utilizzo all'interno dell'esempio. Tuttavia è
bene proteggere con una password ciascun database che possa contenere informazioni
riservate, come nel caso in analisi. Tenendo aperto il database all'interno di Access, è possibile
compiere tale operazione alla seguente maniera:
1.
2.
3.
4.
Dal menù si selezioni la voce "Strumenti > Protezione > Imposta password database...".
Si digiti la password desiderata, ad esempio "prova".
Si digiti la stessa password nel campo "Verifica".
Si confermi con il tasto "Ok" l'intera operazione.
Da questo momento in poi il database utenza.mdb risulterà protetto da password. Chiudendo
Access e tentando di riaprire il file, infatti, se tutto è andato a buon fine, si dovrebbe ricevere
la richiesta di inserimento della password.
14.2 - La pagina di iscrizione
A questo punto è necessario realizzare anzitutto uno script in grado di consentire agli utenti di
registrarsi, ottenendo una coppia di valori username-password utilizzabili per accedere
successivamente all'area protetta. Servirà anzitutto una comune pagina HTML (iscrizione.htm)
contenente il modulo da compilare per richiedere l'adesione al servizio:
<html>
<head>
<title>Iscrizione al servizio</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body bgcolor="#FFFFFF" text="#000000">
<div align="center">
<p><b><font face="Verdana, Arial, Helvetica, sans-serif" size="2">ISCRIZIONE AL SERVIZIO</font></b></p>
<form method="post" action="iscrivimi.asp">
<p>
<font face="Verdana, Arial, Helvetica, sans-serif" size="2">USERNAME:</font><br>
<input type="text" name="username" maxlength="20" size="25">
</p>
<p>
<font face="Verdana, Arial, Helvetica, sans-serif" size="2">PASSWORD:</font><br>
<input type="password" name="password1" size="25" maxlength="20">
</p>
<p>
<font face="Verdana, Arial, Helvetica, sans-serif" size="2">CONFERMA PASSWORD:</font><br>
<input type="password" name="password2" size="25" maxlength="20">
</p>
<p><input type="submit" value="ISCRIVIMI"></p>
</form>
</div>
</body>
</html>
A ricevere e processare i dati ci penserà quindi iscrivimi.asp, come mostrato di seguito:
<%@ LANGUAGE = JScript %>
<%
function visualizzaErrore(messaggioDiErrore) {
// Mostra il messaggio di errore e interrompe il programma
Response.Write('<html>\n');
Response.Write('<head>\n');
Response.Write('<title>Iscrizione al servizio</title>\n');
Response.Write('<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">\n');
Response.Write('</head>\n');
Response.Write('<body bgcolor="#FFFFFF" text="#000000">\n');
Response.Write('<div align="center">\n');
Response.Write('<p> </p>\n');
Response.Write('<p><b><font face="Verdana, Arial, Helvetica, sans-serif" size="2" color="#660000">');
Response.Write('ERRORE: ' + messaggioDiErrore);
Response.Write('</font></b></p>\n');
Response.Write('<p><font face="Verdana, Arial, Helvetica, sansserif" size="2">[<a href="#" onClick="history.back()">TORNA INDIETRO</a>]</font></p>\n');
Response.Write('</div>\n');
Response.Write('</body>\n');
Response.Write('</html>\n');
Response.End;
}
// PASSO 1: si riceve e si valida il modulo
username = String(Request.Form("username"));
password1 = String(Request.Form("password1"));
password2 = String(Request.Form("password2"));
if (username=="" || username=="undefined") visualizzaErrore("SPECIFICARE LO USERNAME");
if (username.charAt(0)==" ") visualizzaErrore("HAI INSERITO UNO SPAZIO DI TROPPO PRIMA DELLO USERNAME");
if (username.charAt(username.length1)==" ") visualizzaErrore("HAI INSERITO UNO SPAZIO DI TROPPO DOPO LO USERNAME");
if (username.length<3 || username.length>20) visualizzaErrore("LO USERNAME DEVE ESSERE COMPRESO TRA 3 E 20 CARAT
TERI");
if (password1=="" || password1=="undefined") visualizzaErrore("SPECIFICARE LA PASSWORD");
if (password1!=password2) visualizzaErrore("LE DUE PASSWORD NON COINCIDONO");
if (password1.length<3 || password1.length>20) visualizzaErrore("LA PASSWORD DEVE ESSERE COMPRESA TRA 3 E 20 CARA
TTERI");
// PASSO 2: viene aperta la connessione verso la fonte di dati
ADOConn = new ActiveXObject("ADODB.Connection");
strConn = "";
strConn += "driver={Microsoft Access Driver (*.mdb)};";
strConn += "dbq=" + Server.MapPath("utenza.mdb") + ";";
strConn += "pwd=prova"; // SELEZIONARE QUI LA PASSWORD DI ACCESSO AL DATABASE SCELTA!
ADOConn.Open(strConn);
// PASSO 3: VIENE VERIFICATO CHE LO USERNAME NON SIA GIA' IN USO
tempUsername = username.replace(/'/g,"''");
sql = "SELECT * FROM utenti WHERE username LIKE '" + tempUsername + "'";
ris = ADOConn.Execute(sql);
if (!ris.EOF) {
ris.Close();
ADOConn.Close();
visualizzaErrore("LO USERNAME SPECIFICATO GIA' RISULTA IN USO. MODIFICARE LA PROPRIA SCELTA.");
}
ris.Close();
// PASSO 4: IL NUOVO UTENTE VIENE REGISTRATO
RecSet = new ActiveXObject("ADODB.Recordset");
RecSet.Open("utenti",ADOConn,3,3);
RecSet.AddNew();
RecSet("username") = username;
RecSet("password") = password1;
RecSet.Update();
RecSet.Close();
ADOConn.Close();
%>
<html>
<head>
<title>Iscrizione al servizio riuscita!</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body bgcolor="#FFFFFF" text="#000000">
<div align="center">
<p><b><font face="Verdana, Arial, Helvetica, sans-serif" size="2">COMPLIMENTI, ORA SEI ISCRITTO!</font></b></p>
</div>
</body>
</html>
I passaggi utilizzati dal codice non sono di difficile comprensione. E' stata realizzata la funzione
visualizzaErrore, utile per mandare in output ogni eventuale messaggio di errore e per
interrompere l'esecuzione dello script. Il modulo viene ricevuto e validato in base a dei criteri
piuttosto generici, quindi la connessione al database viene stabilita. Per prima cosa si verifica
che lo username specificato non sia già stato registrato da un altro utente, mediante una
ricerca case-insensitive (operatore LIKE). Una volta superato tale controllo un nuovo record,
costituito dai dati appena ricevuti, viene inserito nella tabella utilizzata. Se tali operazioni non
dovessero risultare di immediata comprensione si torni allora a rivedere i Capitoli dal 9 al 12,
interamente dedicati all'utilizzo dei database all'interno delle pagine ASP.
14.3 - La pagina di "login"
Per accedere all'area riservata è ora necessario realizzare una pagina che consenta all'utente di
farsi riconoscere. Ancora una volta il processo non risulta complicato. Viene qui presentato il
codice di login.asp:
<%@ LANGUAGE = JScript %>
<%
errore = false;
username = String(Request.Form("username"));
password = String(Request.Form("password"));
from = String(Request.QueryString("from"));
if (username!="undefined" && username!="") {
from = Request.Form("from");
ADOConn = new ActiveXObject("ADODB.Connection");
strConn = "";
strConn += "driver={Microsoft Access Driver (*.mdb)};";
strConn += "dbq=" + Server.MapPath("utenza.mdb") + ";";
strConn += "pwd=prova"; // SELEZIONARE QUI LA PASSWORD DI ACCESSO AL DATABASE SCELTA!
ADOConn.Open(strConn);
tempUsername = username.replace(/'/g,"''");
tempPassword = password.replace(/'/g,"''");
sql = "SELECT * FROM utenti WHERE username LIKE '" +
tempUsername + "' AND password = '" + tempPassword + "'";
ris = ADOConn.Execute(sql);
if (ris.EOF) errore = true;
else {
Session.Timeout = 15;
Session("username") = String(ris("username"));
ris.Close();
ADOConn.Close();
if (from!="undefined") Response.Redirect(from);
else Response.Redirect("main.asp");
}
ris.Close();
ADOConn.Close();
}
%>
<html>
<head>
<title>Accesso all'area riservata</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body bgcolor="#FFFFFF" text="#000000">
<div align="center">
<p><b><font face="Verdana, Arial, Helvetica, sans-serif" size="2">
ATTENZIONE! STAI PER ACCEDERE AD UN'AREA RISERVATA. DEVI AUTENTIFICARTI.</font></b></p>
<% if (errore) { %>
<p><b><font face="Verdana, Arial, Helvetica, sansserif" size="2" color="#FF0000">DATI NON TROVATI, ACCESSO NEGATO</font></b></p>
<% } %>
<form method="post" action="login.asp">
<input type="hidden" name="from" value="<%=from%>">
<p>
<font face="Verdana, Arial, Helvetica, sans-serif" size="2">USERNAME:</font><br>
<input type="text" name="username" maxlength="20" size="25">
</p>
<p>
<font face="Verdana, Arial, Helvetica, sans-serif" size="2">PASSWORD:</font><br>
<input type="password" name="password" size="25" maxlength="20">
</p>
<p><input type="submit" value="ACCEDI"></p>
</form>
</div>
</body>
</html>
Si osservi il meccanismo utilizzato. Qualora il processo di autentificazione dovesse andare a
buon fine, lo script si curerebbe allora di creare la variabile Session("username"), inserendo al
suo interno lo username utilizzato dall'utente. Il perché dell'utilizzo dell'oggetto built-in Session
verrà illustrato nel corso del paragrafo successivo.
Ci si soffermi ora ad osservare invece l'utilizzo della particolare variabile from e del
meccanismo di reindirizzamento ad essa collegato. In buona sostanza l'utente può finire su
login.asp in due distinti casi: quando vi accede direttamente con il proprio browser e quando
tenta di accedere ad un documento riservato senza essersi prima autentificato. Il primo caso è
il più semplice. Nessuna querystring viene appesa all'URL della pagina, pertanto la variabile
from perde il suo significato. Una volta effettuato il login l'utente verrà reindirizzato alla pagina
main.asp, qui non trattata, che dovrebbe ipoteticamente rappresentare l'home page dell'intera
area ad accesso limitato. Quando l'utente tenterà invece di accedere direttamente ad uno dei
documenti protetti dal metodo che tra poco sarà descritto, senza essersi però autentificato in
precedenza, allora verrà rimandato a login.asp, con l'aggiunta dell'indirizzo della pagina di
provenienza appeso in querystring. In questa maniera, una volta autentificato, l'utente verrà
rispedito direttamente alla pagina alla quale aveva tentato originariamente di accedere.
Ovviamente essendosi autentificato potrà in questo caso visualizzarne il contenuto senza
problemi. Si tratta di un'ottima funzionalità aggiuntiva!
14.4 - I documenti riservati
Far appartenere un particolare documento all'area riservata significa, di fatto, configurarlo in
maniera adeguata secondo le seguenti indicazioni.
1. Il documento dovrà avere sempre e comunque estensione .asp, in quanto includerà il
controllo necessario sotto forma di script.
2. Le prime righe del documento dovranno necessariamente essere:
<%@ LANGUAGE = JScript %>
<%
if (Session("username")==null) {
from = escape(String(Request.ServerVariables("SCRIPT_NAME")));
Response.Redirect("login.asp?from=" + from);
}
%>
L'oggetto built-in Session è stato descritto nel Capitolo 13.
Nel caso le pagine dell'area non fossero tutte contenute nella stessa cartella di login.asp, allora
sarà necessario adattare di volta in volta l'istruzione Response.Redirect, in modo da puntare
sempre alla giusta locazione della pagina utile per il proprio riconoscimento.
Per testare la funzionalità dello script si realizzi la pagina prova.asp:
<%@ LANGUAGE = JScript %>
<%
if (Session("username")==null) {
from = escape(String(Request.ServerVariables("SCRIPT_NAME")));
Response.Redirect("login.asp?from=" + from);
}
%>
<html>
<head>
<title>Pagina interna all'area riservata</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body bgcolor="#FFFFFF" text="#000000">
<div align="center">
<p><b><font face="Verdana, Arial, Helvetica, sans-serif" size="2">BENVENUTO
<%=Session("username")%>!</font></b></p>
</div>
</body>
</html>
Si registri quindi uno username passando per iscrizione.htm, quindi si inserisca l'URL di
prova.asp senza prima essersi autentificati.
14.5 - La pagina di "logout"
Una volta che l'utente si è autentificato gli sono concessi, secondo l'esempio utilizzato, 15
minuti di inattività prima che la sua sessione scada. Tuttavia in diversi casi è necessario che la
sessione, su comando dell'utente stesso, possa concludersi senza dover attendere lo scorrere
dei 15 minuti dall'ultima operazione eseguita. Pertanto è oppurtuno inserire all'interno di
ognuno dei documenti protetti un collegamento verso logout.asp:
<%@ LANGUAGE = JScript %>
<%
Session.Abandon();
%>
<html>
<head>
<title>Uscita dall'area riservata</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body bgcolor="#FFFFFF" text="#000000">
<div align="center">
<p><b><font face="Verdana, Arial, Helvetica, sans-serif" size="2">ARRIVEDERCI!</font></b></p>
</div>
</body>
</html>
14.6 - Conclusioni
Si conclude qui questo tutorial dedicato ai metodi di realizzazione, tramite ASP, di un'area ad
accesso riservato. Si lascia ora alla fantasia e alle esigenze del lettore il compito di adattare
quanto appreso ai più disparati casi pratici.
Capitolo 15 - L'oggetto built-in Application
15.0 - Scopo e utilizzo dell'oggetto Application
Application, quinto ed ultimo degli oggetti built-in di ASP, permette di gestire informazioni
condivise tra tutti gli utenti connessi ad un sito. La comprensione dell'utilizzo delle variabili
Application è molto semplice se già si ha manualità con le controparti Session (cfr. Capitolo
13). La sintassi che consente l'attivazione di una variabile di applicazione, infatti, è identica a
quella che si utilizza di norma per le variabili di sessione o variabili utente:
Application("nomevariabile") = contenuto;
Anche il recupero del valore memorizzato conserva la stessa semplicità del caso precedente:
<%=Application("nomevariabile")%>
I dati che possono essere memorizzati in variabili di questo tipo, inoltre, sono i tipi semplici
supportati dai linguaggi di scripting, primi fra tutti quindi i valori numerici e le stringhe. Non
possono essere assegnati a delle variabili di applicazione i riferimenti ad oggetti propri del
linguaggio di scripting usato (come d'altronde non è possibile farlo per le variabili Session), in
quanto tali oggetti vengono distrutti automaticamente al termine dell'esecuzione del codice.
Tuttavia possono essere inseriti in Application riferimenti ad oggetti ActiveX di varia natura, tra
i quali spicca ad esempio ADO. Non sempre è conveniente, però, ricorrere a questa tecnica.
Per il momento ci limiteremo ad osservare l'uso dell'oggetto Application rispetto ai tipi di dati
primitivi.
Si è osservato quale grande analogia corra tra gli oggetti Session e Application, ma non si è
ancora precisato in cosa differiscano. Le variabili di applicazione permettono la condivisione di
dati tra gli utenti, pertanto là dove le variabili Session sono distinte e separate per ogni utente
connesso, le variabili Application sono uniche per tutti. Questa peculiarità rende le variabili di
applicazione utilissime ogni qual volta un'informazione debba essere velocemente condivisa tra
tutte le persone collegate ad un sito. E' il caso dei famosi contatori di utenti connessi, oppure
quello delle chat per interfaccia Web, dove i messaggi postati dai partecipanti vengono inseriti
in variabili di questo tipo per divenire fruibili pubblicamente. In poche parole inserire il valore
"ciao" all'interno di una variabile Application è come inserirlo, ad esempio, in un file di testo. Il
file di testo, infatti, è unico, e qualsiasi utente vorrà leggerlo in un dato momento vi
individuerà lo stesso contenuto. La differenza sostanziale tra le variabili Application e i dati
conservati fisicamente sul disco è comunque lampante, e quello fornito vuole essere solo un
aiuto alla comprensione. Per prima cosa l'accesso ai contenuti di una variabile di questo tipo è
molto più veloce, e non si basa sull'utilizzo di componenti esterni da inizializzare ogni volta.
Inoltre i file di testo (o i database gestiti da ADO) conservano i dati perennemente, almeno fino
a quando non si comunica esplicitamente la loro eliminazione, mentre le variabili Application
sono mantenute in memoria dal Web server, e per questo motivo il loro contenuto va perso
ogni volta che un reset viene eseguito. Un'altra differenza essenziale che corre tra Session e
Application è che le variabili di quest'ultimo tipo non scadono dopo un determinato tempo di
inattività, ed il loro ciclo vitale si conclude solo quando vengono esplicitamente distrutte, o con
il reset del server, come già è stato detto. In definitiva, quindi, l'oggetto built-in Application è
la maniera migliore per condividere dati semplici ed immediati, la cui conservazione perenne
non sia comunque richiesta.
15.1 - Metodi e collezioni dell'oggetto Application
L'oggetto built-in Application mette a disposizione dello sviluppatore due metodi e due
collezioni, che verranno ora passati velocemente in rassegna. I paragrafi successivi saranno
destinati ad approfondimenti ed esempi in merito.
Metodi
Lock
Impedisce ad altri script di modificare il contenuto delle variabili
Application.
UnLock
Consente ad altri script di modificare il contenuto delle variabili
Application.
Collezioni
Contents
Collezione di tutte le variabili Application attive sul server.
StaticObjects
Collezione di tutti gli oggetti che hanno ricevuto lo scope
"application".
15.2 - I metodi Lock e UnLock
Il fatto che le variabili Application presentino natura globale nel confronto di tutti gli utenti
espone le applicazioni Web basate su di esse a potenziali problemi. Essendo ogni valore
condiviso è probabile che più di uno script, la cui richiesta di esecuzione è stata lanciata da
utenti differenti, tenti di modificare il contenuto di una variabile Application nello stesso
istante. Questo è fonte di grandi problemi, in quanto si rischia la perdita e la malformazione
dei dati precedentemente accumulati. Per ovviare a tale inconveniente sono stati introdotti i
metodi Lock e UnLock, grazie ai quali solo uno script alla volta ha accesso in scrittura sui valori
di applicazione. La prassi generale che si deve seguire quando si inseriscono valori condivisi,
quindi, ricalca il seguente caso:
Application.Lock();
Application("nomevariabile") = contenuto;
Application.UnLock();
Nel momento in cui viene lanciato Lock, si impedisce automaticamente ad ogni altro processo
di intervenire sul contenuto delle variabili Application. L'unico script che può scrivere
tranquillamente dei valori al loro interno resta quello che ha richiamato per primo il metodo.
Tutti gli altri processi, quindi, resteranno in attesa e, a turno, interverranno singolarmente in
scrittura nelle variabili di loro interesse. Per questo motivo, ogni volta che il codice completi il
lavoro richiesto su delle variabili di applicazione, è bene lanciare il metodo UnLock, che
permetterà agli altri processi di terminare l'attesa e compiere il loro dovere.
15.3 - Un pratico esempio
Verrà ora analizzato un esempio tanto buono dal punto di vista didattico, quanto inutile da
quello delle applicazioni concrete. Sarà realizzata una pagina ASP che permetterà ad un utente
qualsiasi l'inserimento di un messaggio di testo all'interno di Application. Ogni volta che un
nuovo messaggio sarà postato questo rimpiazzerà il precedente. In contemporanea un secondo
script mostrerà in output il messaggio correntemente accumulato nella variabile utilizzata.
Il documento (pagina1.asp) utile per l'inserimento di un messaggio è il seguente:
<%@ LANGUAGE = JScript %>
<%
var msg = String(Request.Form("msg"));
if (msg!="undefined") {
Application.Lock();
Application("messaggio") = msg;
Application.UnLock();
Response.Redirect("pagina2.asp");
}
%>
<html>
<head>
<title>Inserisci il tuo messaggio!</title>
</head>
<body>
<form method="post" action="pagina1.asp">
<input type="text" name="msg" value="">
<input type="submit" value="VAI!">
</form>
</body>
</html>
La visualizzazione sul browser (pagina2.asp) è estremamente semplice:
<%@ LANGUAGE = JScript %>
<html>
<head>
<title>Ecco il messaggio!</title>
</head>
<body>
<p>Messaggio corrente: <b><%=Application("messaggio")%></b></p>
</body>
</html>
Ogni volta che un utente qualsiasi si servirà di pagina1.asp per esternare un proprio pensiero,
tutto il resto del mondo potrà venirne a conoscenza collegandosi a pagina2.asp!
Nel corso del prossimo capitolo sarà preso in esame un uso molto più concreto ed utile delle
variabili Application.
Capitolo 16 - Il file Global.asa
16.0 - Global.asa
Il file Global.asa, nell'ambito di ASP, è un documento opzionale, che può essere utilizzato per
determinati scopi nello sviluppo di un'applicazione Web. Si tratta comunque di un documento
non convenzionale: il suo contenuto non verrà mai mostrato all'utente, ne il suo scopo sarà
produrre un certo output. Al suo interno, semplicemente, possono essere dichiarati degli
oggetti o dei gestori di evento che abbiano a che fare con l'ambito globale delle sessioni e delle
applicazioni. Global.asa, per divenire attivo, deve essere posto nella root dell'applicazione che
si sta realizzando, e ad ogni applicazione può corrispondere un solo file di questo tipo. Nel
corso delle prossime righe saranno analizzati gli scopi e la giusta maniera di utilizzo di questa
risorsa.
16.1 - Avvio e termine di una sessione
All'interno di un file Global.asa possono essere associate delle azioni ai due eventi di inizio e
fine di una sessione utente. E' possibile fare ciò definendo le due funzioni Session_OnStart e
Session_OnEnd. Si osservi l'esemplificativo codice seguente:
<script language="JScript" runat="Server">
function Session_OnStart() {
// Inizio di una nuova sessione
}
function Session_OnEnd() {
// Fine di una sessione
}
</script>
Come l'esempio lascia ad intendere, il codice contenuto all'interno di un file Global.asa deve
essere racchiuso all'interno dei tag <script>...</script>. Non può essere utilizzata la direttiva
<%@ LANGUAGE = JScript %> per dichiarare l'uso di JScript come linguaggio di scripting, in
quanto i tag <% e %> in questo caso non hanno valore. In alternativa, però, è possibile
specificare l'intenzione di scrivere codice JScript sfruttando l'attributo language del tag
<script>.
Sono state definite due funzioni: Session_OnStart e Session_OnEnd. Il codice compreso
all'interno della prima verrà eseguito ogni volta che una nuova sessione verrà istanziata dal
server, in pratica ogni volta che un nuovo utente si collegherà ad una delle pagine del sito
nella cui root sia conservato il file Global.asa corrispondente. Viceversa, la funzione
Session_OnEnd verrà eseguita al termine di ciascuna sessione, ovvero quando verrà
esplicitamente lanciato il metodo Session.Abandon, oppure quando la durata di una sessione
scadrà a causa del Timeout impostato (cfr. Capitolo 13).
16.2 - Avvio e termine di un'applicazione
Simili alle funzioni appena viste risultano Application_OnStart e Application_OnEnd:
<script language="JScript" runat="Server">
function Application_OnStart() {
// Inizio di una nuova applicazione
}
function Application_OnEnd() {
// Fine di un'applicazione
}
</script>
In questo caso, però, ad essere gestita è l'intera applicazione, unica e condivisa tra tutti gli
utenti collegati. Questo significa che la funzione Application_OnStart verrà eseguita solo
all'avvio dell'intera applicazione. Nella filosofia di ASP e dei Web server che supportano questa
tecnologia, le applicazioni sono insiemi di script suddivise in cartelle differenti. Si supponga di
aver realizzato, ad esempio, un Forum di discussione, contenuto nella cartella \forum del
proprio Web server. Ponendo un file Global.asa all'interno di questa cartella, e associando del
codice all'evento Application_OnStart, sarà possibile compiere una determinata azione quando
l'applicazione prenderà il via. La funzione Application_OnStart, infatti, verrà eseguita la prima
volta che un utente qualsiasi richiamerà una delle pagine contenute all'interno della cartella
\forum. Application_OnEnd, invece, verrà eseguita immediatamente prima della chiusura
dell'applicazione, che corrisponde spesso anche alla chiusura del Web server. Questa funzione,
quindi, risulta il veicolo migliore per fare in modo che ogni risorsa usata in ambito globale
venga rilasciata (una connessione ad un database perennemente aperta, tanto per citare un
esempio, dovrebbe venire qui chiusa).
16.3 - Conteggio degli utenti connessi
Un esempio concreto è quanto di più utile possa esserci in questo momento. Verranno ora
messe a frutto le conoscenze relative al file Global.asa appena analizzate, e quelle acquisite nel
capitolo precedente rispetto l'utilizzo dell'oggetto built-in Application.
Si supponga di voler conteggiare quanti utenti siano connessi ad un sito Web in un determinato
istante, in modo da visualizzare poi il risultato di tale elaborazione sulla Home Page, o anche
all'interno di altre pagine. Tutto questo, in parole spicciole, significa conteggiare quante
sessioni (si ricordi sempre che ad ogni utente viene associata una ed una sola sessione) siano
contemporaneamente attive in ogni singolo istante. Si dovrà allora conservare un dato in
maniera globale, in modo da poterlo poi condividere universalmente tra tutti gli utenti
connessi. A permettere ciò, come deducibile, è una variabile Application. Il trucco risulta allora
semplice, e può essere illustrato in tre brevi passi:
1. All'avvio dell'applicazione (Application_OnStart) verrà definita una variabile intera
Application("q_utenti"), il cui valore iniziale sarà zero.
2. Ogni volta che una nuova sessione risulti al nastro di partenza (Session_OnStart), il valore
contenuto in Application("q_utenti") verrà incrementato di una unità.
3. Ogni volta che una sessione precedentemente creata sia giunta al termine del proprio ciclo
vitale (Session_OnEnd), il valore contenuto in Application("q_utenti") verrà decrementato di
una unità.
Tradotto in codice JScript, il ragionamento appena illustrato si trasforma nel seguente
enunciato Global.asa:
<script language="JScript" runat="Server">
function Application_OnStart() {
Application("q_utenti") = 0;
}
function Session_OnStart() {
Application("q_utenti") = Application("q_utenti") + 1;
}
function Session_OnEnd() {
Application("q_utenti") = Application("q_utenti") - 1;
}
</script>
Fatto ciò, per visualizzare il numero degli utenti connessi, è sufficiente aggiungere da qualche
parte nella Home Page del sito la seguente riga:
In questo momento ci sono <%=Application("q_utenti")%> utenti connessi
Naturalmente la Home Page deve avere estensione .asp, altrimenti la parte compresa tra i tag
<% e %> non sarà processata dal server.
16.4 - Aggiornare Global.asa
Un file Global.asa può essere aggiunto o modificato anche mentre il Web server è attivo. In
questo caso, però, una particolare politica viene adottata. Una volta salvati i cambi al file in
questione, il server continuerà a soddisfare tutte le richieste HTTP in corso, rifiutando con un
messaggio di errore tutte le altre che gli verranno contemporaneamente presentate. Conclusa
la propria attività con le richieste in corso, l'applicazione gestita dal file Global.asa verrà
resettata (e verrà pertanto eseguito il codice di Application_OnEnd incluso nel vecchio
Global.asa, se presente) e tutte le sessioni in corso verranno concluse (con l'esecuzione del
Session_OnEnd del vecchio Global.asa, come prima). A questo punto l'applicazione verrà
riavviata (con il lancio dell'eventuale nuova versione di Application_OnStart), e le richieste
HTTP verranno nuovamente accettate, stabilendo così delle nuove sessioni (con il conseguente
avvio del nuovo Session_OnStart).
Questo almeno il meccanismo dichiarato nella documentazione ufficiale, anche se durante
"prove su strada" i risultati riscontrati con le diverse versioni di IIS e del PWS sono stati
differenti. In alcuni casi, infatti, le direttive del Global.asa sono state aggiornate dopo diversi
minuti, mentre in altri casi ancora si è reso necessario resettare il Web server per ottenere il
risultato desiderato.
Capitolo 17 - Inviare e-mail con CDONTS
17.0 - Posta elettronica su macchine Windows NT/2000
Il funzionamento di un servizio di posta elettronica è garantito dalla collaborazione di tre
distinti software: un server che si occupa della consegna dei messaggi (protocollo SMTP), un
server che riceve i messaggi, li archivia e li trasmette, su richiesta, al titolare della casella
(protocollo POP3 o IMAP) e un client che, dal PC dell'utente, è in grado di inviare e ricevere email collegandosi e dialogando con le due parti server del servizio. I Web server non offrono
funzionalità di posta elettronica, tuttavia le macchine che ospitano servizi Web, spesso e
volentieri, permettono anche l'invio delle e-mail, tramite un apposito server SMTP. Su Windows
NT/2000, il server di posta elettronica più diffuso è Microsoft Exchange. Insieme ad esso viene
installata anche una libreria ActiveX, chiamata CDONTS (Collaboration Data Object for
Windows NT Server), che permette ad applicazioni esterne di collegarsi al server per inviare o
ricevere la posta elettronica. Anche una pagina ASP, quindi, può sfruttare il server SMPT
presente nel sistema, inviando e-mail generate dinamicamente.
17.1 - CDONTS.NewMail
CDONTS comprende diversi oggetti che incapsulano funzionalità legate, in un modo o nell'altro,
al mondo della posta elettronica. Una pagina ASP, salvo rare eccezioni, necessiterà al massimo
di interagire con un server SMTP per il semplice invio di una e-mail. L'oggetto utile per
compiere questa operazione si chiama NewMail.
Proprietà
Bcc
Specifica la lista dei destinatari invisibili in copia carbone. Di sola
scrittura.
Body
Specifica il corpo della missiva. Di sola scrittura.
BodyFormat
Specifica il formato del corpo della e-mail (0 = HTML, 1 = testo
semplice). Di sola scrittura.
Cc
Specifica la lista dei destinatari in copia carbone. Di sola scrittura.
ContentBase
Specifica il percorso di base degli URL che saranno contenuti nella
missiva. Di sola scrittura.
ContentLocation
Specifica il percorso di base, assoluto o relativo, degli URL che
saranno contenuti nella missiva. Di sola scrittura.
From
Specifica il mittente. Di sola scrittura.
Importance
Specifica l'importanza della missiva (0 = bassa, 1 = media, 2 =
alta). Di sola scrittura.
MailFormat
Specifica il formato della mail (0 = MIME, 1 = testo semplice). Di
sola scrittura.
Subject
Specifica l'oggetto della missiva. Di sola scrittura.
To
Specifica la lista dei destinatari. Di sola scrittura.
Version
Restituisce la versione di CDONTS. Di sola lettura.
Metodi
AttachFile
Aggiunge un allegato.
AttachURL
Aggiunge un allegato, associandogli un URL da poter utilizzare nel
corpo della mail.
Send
Invia la mail.
SetLocaleIDs
Imposta l'identificatore locale per la mail.
Collezioni
Value
Permette di impostare intestazioni aggiuntive.
L'oggetto NewMail può essere istanziato alla seguente maniera:
var mail = new ActiveXObject("CDONTS.NewMail");
Da questo momento in poi è possibile specificare i valori che costituiscono la missiva, come il
destinatario, il mittente, l'oggetto e il corpo:
mail.To = "[email protected]";
mail.From = "[email protected]";
mail.Subject = "Oggetto della missiva";
mail.Body = "Corpo della missiva";
Si può quindi procedere con l'invio:
mail.Send();
17.2 - Applicazione "form-mail"
Molti siti chiedono ai propri visitatori di esternare pareri e consigli sui servizi offerti. Di norma
questi vengono raccolti in un modulo HTML, per essere poi spediti, tramite e-mail, al
webmaster del sito. Si realizzerà ora una semplice applicazione di questo tipo, al fine di
dimostrare l'utilizzo dell'oggetto NewMail. E' necessario predisporre un modulo HTML per l'invio
dei dati:
<html>
<head>
<title>Invia il tuo parere!</title>
</head>
<body>
<form action="formmail.asp" method="post">
<b>Il tuo nome:</b><br>
<input type="text" name="nome"><br>
<b>Il tuo parere sul sito:</b><br>
<textarea name="parere" cols="40" rows="10"></textarea>
<br><br>
<input type="submit" value="Invia">
<input type="reset" value="Cancella">
</form>
</body>
</html>
I campi del modulo vengono passati al documento formmail.asp:
<%@ LANGUAGE = JScript %>
<%
var nome = new String(Request.Form("nome"));
var parere = new String(Request.Form("parere"));
var mail = new ActiveXObject("CDONTS.NewMail");
// Immettere un destinatario esistente!
mail.To = "[email protected]";
mail.From = "[email protected]";
mail.Subject = "Parere da " + nome;
mail.Body = parere;
mail.Send();
%>
<html>
<head>
<title>Grazie!</title>
</head>
<body>
Il tuo parere è stato inviato!
</body>
</html>
Si modifichi anzitutto il destinatario della missiva, introducendo il proprio indirizzo e-mail per il
test, quindi ci si colleghi al modulo e si verifichi il risultato.
17.3 - Invio di allegati
NewMail permette l'invio di allegati, purché i file da allegare siano presenti sull'hard disk del
server. Un allegato può essere aggiunto con il metodo AttachFile():
mail.AttachFile(percorso,nome);
Il parametro percorso indica, sull'hard disk del server, il percorso completo del file da allegare.
Se questo non è noto, può sempre essere ricavato da un percorso relativo con
Server.MapPath(). Il parametro nome, invece, indica il nome che il file riceverà sul client del
destinatario. Il seguente esempio invia una mail allegando il file immagine.jpg, che deve
essere inserito nella stessa cartella dello script:
<%@ LANGUAGE = JScript %>
<%
var mail = new ActiveXObject("CDONTS.NewMail");
mail.MailFormat = 0; // Mail MIME
mail.AttachFile(Server.MapPath("immagine.jpg"),"immagine.jpg");
// Immettere un destinatario esistente!
mail.To = "[email protected]";
mail.From = "[email protected]";
mail.Subject = "Con allegato...";
mail.Body = "Ti piace l'immagine?";
mail.Send();
%>
<html>
<head>
<title>Inviata!</title>
</head>
<body>
La mail con allegato è stata inviata!
</body>
</html>
Le e-mail con allegato devono essere in formato MIME.
17.4 - Conclusioni
CDONTS, oltre all'oggetto NewMail, comprende anche una lunga serie di altri componenti.
Questi, di norma, non sono utilizzati in pagine ASP, poiché le funzionalità offerte non trovano
posto in una generica applicazione Web. Inviare una mail, invece, è operazione abbastanza
semplice e comune. Si è visto velocemente quali sono le principali caratteristiche di NewMail,
confermando gli schemi tecnici con alcuni esempi pratici.
Capitolo 18 - Gestione dei cookie
18.0 - Cookie
Un cookie (biscotto) è un pacchetto di dati inviato al browser dal Web server, che viene
conservato sull'hard disk del visitatore, per essere poi restituito al mittente nel corso degli
accessi successivi. Nonostante siano stati a lungo al centro di una controversa polemica, che li
accusava di minare la privacy degli utenti, i cookie sono utili ed indispensabili per il
funzionamento di numerose applicazioni Web. E' anche vero, comunque, che alcuni programmi
di affiliazione utilizzano i cookie, con la complicità di diversi siti, per carpire i gusti dell'utente,
in modo tale da effettuare campagne pubblicitarie mirate. I browser moderni pongono un freno
al problema, riportando finalmente i cookie al loro ruolo originario.
Ciascun cookie è formato da un insieme di sei differenti valori:
Nome, il nome del cookie.
Dati, i dati conservati nel cookie.
Expires, la data di scadenza del cookie.
Domain, il dominio a cui il cookie si riferisce.
Path, il percorso a cui il cookie si riferisce.
Secure, può essere true/false, ed indica se il cookie debba essere ritenuto sicuro.
18.1 - Invio di un cookie
Il campo Response.Cookies permette di inviare un cookie al browser di un visitatore:
Response.Cookies("CookieProva")("DATO1") = "prova1";
Response.Cookies("CookieProva")("DATO2") = "prova2";
Possono essere inoltre specificate le seguenti quattro proprietà:
Response.Cookies("NomeCookie").Expires, indica la data di scadenza del cookie. Se
omessa, il cookie sarà di tipo temporaneo.
Response.Cookies("NomeCookie").Domain,indica il dominio di riferimento. Il dominio deve
essere espresso come stringa. Se omessa, il dominio di riferimento sarà lo stesso
dell'applicazione.
Response.Cookies("NomeCookie").Path, indica il percorso di riferimento. Il percorso deve
essere espresso come stringa, a partire dalla root del sito (ad esempio
"/cartella/sottocartella"). Se omessa, il percorso di riferimento sarà la radice del sito ("/").
Response.Cookies("NomeCookie").Secure, indica se il cookie è sicuro o meno. La condizione
viene espressa con un semplice valore booleano true/false. Se omessa, il cookie sarà non
sicuro.
In questa forma, la data di scadenza del cookie può essere impostata facendo riferimento ad
un oggetto Date di JScript. Per far scadere "CookieProva" nel Natale del 2010 bisogna scrivere:
var d = new Date(2010,11,25);
Response.Cookies("CookieProva").Expires = d.getVarDate();
Si ricorda che, con JScript, il conteggio dei mesi comincia da zero, che equivale a Gennaio,
mentre undici è Dicembre.
Un cookie già esistente può essere aggiornato semplicemente effettuando una sovrascrizione,
come se lo si stesse creando per la prima volta.
18.2 - Recupero di un cookie
La collezione Request.Cookies permette il recupero dei cookie precedentemente inviati al
browser, senza doversi curare della loro forma nell'intestazione HTTP ricevuta. Il recupero di
un dato è immediato:
var variabile = Request.Cookies("NomeCookie")("NomeDato");
Se il dato richiesto non esiste, il valore restituito è null. Ecco un esempio di recupero con
verifica condizionale sull'esistenza:
var mioCookie = Request.Cookies("CookieProva")("DATO1");
if (mioCookie==null) {
Response.Write("CookieProva.DATO1 non esiste!");
} else {
Response.Write("CookieProva.DATO1 esiste e vale: " + mioCookie);
}
18.3 - Rimozione di un cookie
Si può comandare la rimozione di un cookie semplicemente impostando la sua data di
scadenza su un momento già trascorso, ad esempio quello di elaborazione della pagina:
var d = new Date();
Response.Cookies("NomeCookie").Expires = d.getVarDate();