Subsections

14. Variabili e funzioni speciali

CherryPy genera e usa alcune variabili e funzioni speciali. Sono molto facili da usare, ma altrettanto potenti. In questo capitolo vedremo cosa sono queste variabili e funzioni, mentre nel prossimo impareremo ad usarle.

14.1 Variabili speciali

14.1.1 request

Questa è la variabile usata più comunemente. Contiene tutte le informazioni sulle richiesta pervenute da un client. E' un'istanza di classe che contiene alcune variabili membro che sono generate da CherryPy per ogni richiesta. Le variabili membro usate più comunemente sono:

14.1.2 response

Questa è la seconda variabile usata più comunemente (dopo request). Contiene tutte le informazioni sui responsi che saranno inviati al client. E' un'instanza di classe che contiene alcune variabili membro che sono generate da CherryPy o dal vostro programma.

14.2 Funzioni speciali

Nel vostro codice potete definire funzioni speciali che modificheranno il comportamento del server. Per definire queste funzioni, usate la sintassi Python e definitele fuori dalle CherryClass. Quando usate moduli differenti, potete definire la stessa funzione in moduli differenti. In questo caso, CherryPy concatenerà i corpi di tutte le funzioni nello stesso ordine di lettura dei file.

14.2.1 initRequest, initNonStaticRequest, initResponse e initNonStaticResponse

Ecco l'algoritmo che il server usa quando riceve una richiesta (se non specificato, l'operazione si riferisce ad ogni tipo di contenuto):

  1. arriva la richiesta
  2. Genera tutte le variabili membro di request
  3. Chiama initRequest (che può modificare request.path e request.paramMap)
  4. Determina se questa richiesta corrisponde a contenuto statico o dinamico (basato su request.path e la sezione staticContent del file di configurazione)
  5. (Solo per contenuto dinamico) Chiama initNonStaticRequest (che può modificare request.path e request.paramMap)
  6. (solo per contenuto statico) Legge il file statico e genera i valori di response.headerMap e response.body in modo appropriato
  7. (solo per contenuto dinamico) Chiama il metodo dell'istanza CherryClass, con alcuni argomenti (basati su request.path e request.paramMap) e genera i valori di response.headerMap e response.body in accordo con i risultati
  8. (solo per contenuto statico) Chiama initResponse (che può modificare response.headerMap e response.body)
  9. (solo per contenuto dinamico) Chiama initResponse (che può modificare response.headerMap e response.body)
  10. (solo per contenuto statico) Invia un responso al browser (basato su response.headerMap e response.body)
  11. (solo per contenuto dinamico) Invia un responso al browser (basato su response.headerMap e response.body)

Come potete vedere, initRequest e initNonStaticRequest possono essere usate per un aggiustamento fine dell'URL o dei parametri, o per fare qualsiasi lavoro che deve essere eseguito su ogni richiesta.

initResponse e initNonStaticResponse possono essere usate per cambiare l'intestazione della risposta o del corpo, prima che venga invita al client.

14.2.2 onError

Questa funzione è chiamata da CherryPy quando avviene un errore durante la costruzione della pagina. Guardate la prossima sezione per un esempio.

14.2.3 initThread, initProcess

Se usate un server thread-pool o process-pool, sarà chiamata la funzione corrispondente (rispettivamente initThread o initProcess) per ogni nuovo thread/processo creato.

Queste funzioni possono essere usate per esempio se volete avere per ogni thread/processo una propria connessione ad un database (l'HowTo chiamato "Sample deployment configuration for a real-world website" spiega come farlo).

initThread prende un argomento chiamato threadIndex contenente l'indice del thread che deve essere creato. Per esempio, se volete creare 10 threads, threadIndex avrà i valori da 0 a 9.

14.2.4 initProgram, initServer, initAfterBind

Il codice in initProgram viene copiato all'inizio del file generato, in modo da essere la prima cosa ad essere eseguita. Potete usare le funzioni speciali se avete bisogno di eseguire del codice prima che vengano istanziate le CherryClass. Quindi, il server crea tutte le instanze delle CherryClass e quindi chiama la funzione speciale initServer. Questo è basilarmente il posto dove mettere tutti i passi di inizializzazione necessari.

initAfterBind viene chiamata dopo che il socket si è messo in "ascolto". Per esempio, su sistemi Unix-based, dovrete far partitre CherryPy come root se volete che si metta in ascolto sulla porta 80. La funzione speciale initAfterBind può essere usata per cambiare l'utente proprietario del processo in ascolto in uno non privilegiato. (l'HowTo chiamato "Sample deployment configuration for a real-world website" spiega come farlo).

14.2.5 initRequestBeforeParse (solo per uso avanzato)

Questa funzione speciale è chiamata dal server quando riceve una richiesta di tipo POST, prima di analizzare i dati POST. Questo vi permette per esempio di dire al server di non analizzare i dati POST (settando la variabile request.parsePostData a 0) e quindi potete analizzare i dati POST voi stessi (leggendoli da request.rfile). Controllate l'HowTo chiamato "How to stream uploaded files directly to disk" per maggiori informazioni.

14.3 Esempi

14.3.1 Giochiamo con gli URL

Abbiamo detto di voler mettere su un sito per i nostri clienti. Vogliamo che i nostri clienti abbiano il proprio URL: http://host/customerName, ma che la pagina sia sempre la stessa per ogni cliente, così da non dover creare un metodo per ogni cliente.

Tutto ciò che dovete fare è usare initNonStaticRequest per convertire l'URL http://host/customerName in http://host?customer=customerName. Tutto questo sarà trasparente all'utente.

Scriviamo il seguente codice:

def initNonStaticRequest():
    if request.path:
        request.paramMap['customer']=request.path
        request.path=""
CherryClass Root:
mask:
    def index(self, customer=""):
        <html><body>
            Hello, <py-eval="customer">
        </body></html>
E questo è tutto !

Compilate il file, fate partire il server e provate gli URL, come http://localhost:8000/customer1o http://localhost:8000/world

14.3.2 Ridirezione

Per inviare un redirect al browser, tutto quello che dovete fare è inviare un codice di stato 302 (invece di 200), e generare un valore location nell'intestazione della risposta. Questo può essere fatto facilmente usando la variabile speciale response.headerMap:
CherryClass Root:
mask:
    def index(self):
        <html><body>
            <a href="loop">Click here to come back to this page</a>
        </body></html>
view:
    def loop(self):
        response.headerMap['status']=302
        response.headerMap['location']=request.base
        return "" # A view should always return a string

14.3.3 Aggiungere informazioni sul tempo di costruzione in ogni pagina

In questo esempio, aggiungeremo una linea alla fine di ogni pagina che è generata dal server. Questa linea conterrà il tempo impiegato per costruire la pagina. Naturalmente, vogliamo questa linea solo per le pagine HTML dinamiche.

Tutto quello che dobbiamo fare è usare initNonStaticRequest per memorizzare la partenza, e initNonStaticResponse per aggiungere la linea contenente il tempo impiegato per la costruzione.

Ecco il codice:

import time
def initNonStaticRequest():
    request.startTime=time.time()
def initNonStaticResponse():
    if response.headerMap['content-type']=='text/html':
        response.body+='<br>Time: %.04fs'%(time.time()-request.startTime)
CherryClass Root:
mask:
    def index(self):
        <html><body>
            Hello, world
        </body></html>

Et voila

14.3.4 Adattare i messaggi di errore

Questo può essere fatto usando la funzione speciale onError. Usate response.headerMap e response.body per fare quello che desiderate.

L'esempio che segue mostra come configurare il tutto per inviare una mail ogni volta che viene incontrato un errore:

use Mail

def onError():
    # Get the error in a string
    import traceback, StringIO
    bodyFile=StringIO.StringIO()
    traceback.print_exc(file=bodyFile)
    errorBody=bodyFile.getvalue()
    bodyFile.close()
    # Send an email with the error
    myMail.sendMail("erreur@site.com", "webmaster@site.com", "", "text/plain", "An error occured on your site", errorBody)
    # Set the body of the response
    response.body="<html><body><br><br><center>"
    response.body+="Sorry, an error occured<br>"
    response.body+="An email has been sent to the webmaster"
    response.body+="</center></body></html>"


CherryClass MyMail(Mail):
function:
    def __init__(self):
        self.smtpServer='smtp.site.com'

CherryClass Root:
mask:
    def index(self):
        <html><body>
            <a py-attr="request.base+'/generateError'" href="">
                    Click here to generate an error</a>
        </body></html>
    def generateError(self):
        <html><body>
            You'll never see this: <py-eval="1/0">
        </body></html>

Questo esempio mostra anche l'uso del modulo standard Mail compreso in CherryPy.

See About this document... for information on suggesting changes.