12. Servizi web in Python
![]() |
Gli script Python possono essere eseguiti da un server WEB. È quest’ultimo che resterà in ascolto delle richieste dei client. Dal punto di vista del client, chiamare un servizio WEB equivale a richiedere l’URL di quel servizio. Il client può essere scritto in qualsiasi linguaggio, in particolare in Python. È necessario saper «comunicare» con un servizio WEB, ovvero comprendere il protocollo di comunicazione Http tra un server Web e i suoi client. Questo è l’obiettivo dei programmi che seguono.
Gli script del servizio web saranno eseguiti dal server web Apache di WampServer. È necessario collocarli in una directory specifica: <WampServer>\bin\apache\apachex.y.z\cgi-bin, dove <WampServer> è la cartella di installazione di WampServer e x.y.z è la versione del server web Apache.
![]() |
Non basta inserire gli script Python nella cartella <cgi-bin>. È necessario che lo script indichi nella prima riga il percorso dell'interprete Python da utilizzare. Questo percorso va inserito sotto forma di commento:
Il lettore dovrà adattare questo percorso al proprio ambiente.
12.1. Applicazione client/server per data e ora
Il nostro primo servizio web sarà un servizio di data e ora: il client riceve la data e l'ora correnti.
12.1.1. Il server
#!D:\Programmi\ActivePython\Python2.7.2\python.exe
import time
# intestazioni
print "Content-Type: text/plain\n"
# invio dell'ora al cliente
# ora locale: numero di millisecondi dal 01/01/1970
# "formato di visualizzazione data-ora
# d: giorno a 2 cifre
# m: mese a 2 cifre
# y: anno a 2 cifre
# H: ora 0,23
# M: minuti
# S: secondi
print time.strftime('%d/%m/%y %H:%M:%S',time.localtime())
Note:
- riga 6: lo script deve generare autonomamente alcune delle intestazioni HTTP della risposta al client. Queste si aggiungeranno alle intestazioni HTTP generate dal server Apache stesso. L’intestazione HTTP della riga 6 indica al client che gli verrà inviata una risorsa in formato text/plain, ovvero testo non formattato. Da notare il "\n" alla fine dell’intestazione, che genererà una riga vuota dopo l’intestazione. È obbligatorio: è proprio questa riga vuota che segnala al client HTTP la fine delle intestazioni HTTP della risposta. Segue poi la risorsa richiesta dal client, in questo caso un testo non formattato;
- riga 18: la risorsa inviata al client è un testo che mostra la data e l'ora correnti.
12.1.2. Due test
Lo script precedente può essere eseguito direttamente dall’interprete Python in una finestra di comando, come abbiamo fatto finora. Ciò consente di eliminare eventuali errori di sintassi o di funzionamento. Si ottiene il seguente risultato:
Una volta testato lo script in questo modo, è possibile inserirlo nella cartella <cgi-bin> del server Apache (cfr. paragrafo 12). Avviamo l’applicazione WampServer. In questo modo vengono avviati sia un server web Apache che un SGBD MySQL. Per il momento utilizzeremo solo il server web. Quindi, con un browser, accediamo alla seguente pagina URL: http://localhost/cgi-bin/web_02.py:
![]() |
- in [1]: l’URL richiesta;
- in [2]: la risposta visualizzata dal browser;
- in [3]: il codice sorgente ricevuto dal browser web. È proprio quello inviato dallo script Python.
Con alcuni strumenti (in questo caso Firebug, un plugin del browser Firefox) è possibile accedere alle intestazioni HTTP scambiate con il server. Nell’esempio sopra riportato, il browser web ha ricevuto le seguenti intestazioni HTTP:
Nelle righe 7-8 si riconosce l’intestazione HTTP inviata dallo script Python. Quelle precedenti sono state generate dal server web Apache.
12.1.3. Un client programmato
Ora scriviamo uno script che fungerà da client del servizio web precedente. Utilizziamo le funzionalità del modulo httplib che semplifica la scrittura di client HTTP.
# -*- coding=utf-8 -*-
import httplib,re
# costanti
HOST="localhost"
URL="/cgi-bin/web_02.py"
# connessione
connexion=httplib.HTTPConnection(HOST)
# monitoraggio
connexion.set_debuglevel(1)
# invio della richiesta
connexion.request("GET", URL)
# elaborazione della risposta
reponse=connexion.getresponse()
# contenuto
contenu=reponse.read()
# chiusura della connessione
connexion.close()
print "------\n",contenu,"-----\n"
# recupero degli elementi relativi all'ora
elements=re.match(r"^(\d\d)/(\d\d)/(\d\d) (\d\d):(\d\d):(\d\d)\s*$",contenu).groups()
print "Jour=%s,Mois=%s,An=%s,Heures=%s,Minutes=%s,Secondes=%s" % (elements[0],elements[1],elements[2],elements[3],elements[4],elements[5])
Note:
- riga 3: il modulo re è necessario per le espressioni regolari, mentre il modulo httplib è necessario per le funzioni dei client HTTP;
- riga 9: viene creata una connessione HTTP con la porta 80 di HOST definita alla riga 6;
- riga 11: il monitoraggio consente di visualizzare le intestazioni HTTP della richiesta del client e della risposta del server;
- riga 13: viene richiesta la risorsa URL del servizio web. Esistono due modi per richiederla: tramite il comando HTTP GET o POST. La differenza tra i due viene spiegata più avanti. In questo caso verrà richiesta con il comando HTTP GET;
- riga 15: viene letta la risposta del server. Qui si ottiene la risposta completa: intestazioni HTTP e risorsa richiesta dal client. Nella sua risposta, il server potrebbe aver chiesto al client di reindirizzarsi. In questo caso, il client httplib esegue automaticamente il reindirizzamento. La risposta ottenuta è quindi quella risultante dal reindirizzamento;
- riga 17: la risposta è composta dalle intestazioni HTTP e dal documento richiesto dal client. Per ottenere solo le intestazioni HTTP, si utilizzerà [reponse].getHeaders(). Per ottenere il documento, si utilizza [reponse].read();
- riga 19: una volta ottenuta la risposta dal server web, la connessione con esso viene chiusa;
- si sa che il documento inviato dal server è una riga di testo del formato 15/06/11 14:56:36. Righe 22-26: si utilizza un'espressione regolare per estrarre i diversi elementi di questa riga.
12.1.4. Risultati
Note:
- riga 1: le intestazioni HTTP inviate dal client al server web;
- righe 2-6: le intestazioni HTTP della risposta del server web;
- riga 8: il documento inviato dal server;
- riga 11: il risultato della sua elaborazione;
12.2. Recupero da parte del server dei parametri inviati dal client
Nel protocollo HTTP, un client dispone di due metodi per trasmettere parametri al server web:
- richiede il servizio URL nella forma
GET url?param1=val1¶m2=val2¶m3=val3… HTTP/1.0
dove i valori vali devono essere preventivamente sottoposti a codifica affinché alcuni caratteri riservati vengano sostituiti dal loro valore esadecimale.
- richiede l’URL del servizio nella forma
quindi, tra le intestazioni HTTP inviate al server, inserisce la seguente intestazione:
Il resto delle intestazioni inviate dal client termina con una riga vuota. A questo punto, il client può inviare i propri dati nel formato
dove i valori vali devono, come per il metodo GET, essere preventivamente codificati. Il numero di caratteri inviati al server deve essere N, dove N è il valore dichiarato nell'intestazione:
12.2.1. Il servizio web
Il servizio web che segue riceve 3 parametri dal proprio client: nom, prenom, age. Li recupera da una sorta di dizionario denominato cgi.FieldStorage fornito dal modulo cgi. Il valore vali di un parametro parami si ottiene tramite vali=cgi.FieldStorage().getlist("parami"). Si ottiene un array di:
- 0 elementi se il parametro parami non è presente nella richiesta del cliente;
- 1 elemento se il parametro parami è presente una volta nella richiesta del client;
- n elementi se il parametro parami è presente n volte nella richiesta del cliente.
Una volta recuperati i parametri, lo script li restituisce al cliente.
#!D:\Programmi\ActivePython\Python2.7.2\python.exe
import cgi
# intestazioni
print "Content-Type: text/plain\n"
# recupero da parte del server delle informazioni inviate dal client
# qui nome=P&cognome=N&età=A
formulaire=cgi.FieldStorage()
# vengono rinviate al client
print "informations recues du service web [prenom=%s,nom=%s,age=%s]" % (formulaire.getlist("prenom"),formulaire.getlist("nom"),formulaire.getlist("age"))
È possibile effettuare un test con un browser web:
![]() |
In [1], l'URL del servizio web. Si noti la presenza dei tre parametri nom, prenom, age. In [2] la risposta del servizio web.
12.2.2. Il client GET
# -*- coding=utf-8 -*-
import httplib,urllib
# costanti
HOST="localhost"
URL="/cgi-bin/web_03.py"
PRENOM="Jean-Paul"
NOM="de la Huche"
AGE=42
# i parametri devono essere codificati prima di essere inviati al server
params = urllib.urlencode({'nom': NOM, 'prenom': PRENOM, 'age': AGE})
# i parametri vengono inseriti alla fine dell'URL
URL+="?"+params
# connessione
connexion=httplib.HTTPConnection(HOST)
# monitoraggio
connexion.set_debuglevel(1)
# invio della richiesta
connexion.request("GET",URL)
# elaborazione della risposta
reponse=connexion.getresponse()
# contenuto
contenu=reponse.read()
print contenu,"\n"
# chiusura della connessione
connexion.close()
Note:
- righe 8-10: i valori dei 3 parametri inviati al servizio web;
- riga 13: è necessario codificarli. Ciò avviene tramite il metodo urlencode del modulo urllib. Questo modulo viene importato alla riga 3. Il metodo accetta come parametro un dizionario {param1:val1, param2:val2, ...};
- riga 15: in un comando GET (riga 21), il client deve inserire i parametri codificati alla fine del URL del servizio web;
- le righe successive sono già state esaminate.
12.2.3. I risultati
Note:
- riga 2: notare la codifica dei parametri (cognome, nome, età);
- riga 8: la risposta del servizio web.
12.2.4. Il client POST
Il client POST è analogo al client GET, tranne per il fatto che i parametri codificati non fanno più parte del URL di destinazione. Sono stati passati come terzo argomento della richiesta POST (riga 19).
# -*- coding=utf-8 -*-
import httplib,urllib
# costanti
HOST="localhost"
URL="/cgi-bin/web_03.py"
PRENOM="Jean-Paul"
NOM="de la Huche"
AGE=42
# i parametri devono essere codificati prima di essere inviati al server
params = urllib.urlencode({'nom': NOM, 'prenom': PRENOM, 'age': AGE})
# connessione
connexion=httplib.HTTPConnection(HOST)
# monitoraggio
connexion.set_debuglevel(1)
# invio della richiesta
connexion.request("POST",URL,params)
# elaborazione della risposta
reponse=connexion.getresponse()
# contenuto
contenu=reponse.read()
print contenu,"\n"
# chiusura della connessione
connexion.close()
12.2.5. I risultati
Note:
- si noti, alla riga 2, il metodo utilizzato dal client POST per inviare i parametri codificati:
- l'intestazione HTTP Content-Length indica il numero di caratteri che verranno inviati al servizio web;
- questa intestazione HTTP è seguita da una riga vuota che indica la fine delle intestazioni HTTP;
- successivamente vengono inviati i 39 caratteri dei parametri codificati.
- riga 8: la risposta del servizio web.
12.3. Recupero delle variabili d’ambiente da un servizio web
12.3.1. Il servizio web
Lo script CGI Python viene eseguito in un ambiente di sistema dotato di attributi. I suoi attributi e i relativi valori sono disponibili in un dizionario denominato os.environ.
#!D:\Programmi\ActivePython\Python2.7.2\python.exe
import os
# intestazioni
print "Content-Type: text/plain\n"
# informazioni sull'ambiente
for (cle,valeur) in os.environ.items():
print "%s : %s" % (cle,valeur)
Note:
- riga 3: è necessario importare il modulo os per poter disporre delle variabili "di sistema".
Se si esegue direttamente lo script sopra riportato (c.a.d, come script da console e non come CGI), nella console si ottengono i seguenti risultati:
In un browser web (in questo caso viene eseguito lo script CGI), si ottengono i seguenti risultati:
![]() |
Si noti che, a seconda del contesto di esecuzione, l’ambiente ottenuto non è lo stesso.
12.3.2. Il client programmato
# -*- coding=utf-8 -*-
import httplib
# costanti
HOST="localhost"
URL="/cgi-bin/web_04.py"
# connessione
connexion=httplib.HTTPConnection(HOST)
# invio della richiesta
connexion.request("GET", URL)
# elaborazione della risposta
reponse=connexion.getresponse()
# contenuto
print reponse.read()
12.3.3. Risultati
Si noti che il client programmato non riceve esattamente la stessa risposta del browser web. Questo perché quest'ultimo ha inviato al server web delle informazioni che sono state utilizzate dal server web per creare la sua risposta. In questo caso, il client programmato non ha inviato alcuna informazione su se stesso.




