Italiano

Un'esplorazione approfondita di Tornado, un web framework Python e una libreria di networking asincrono. Impara a creare applicazioni scalabili e ad alte prestazioni.

Documentazione di Tornado: Una Guida Completa per Sviluppatori di Tutto il Mondo

Tornado è un web framework Python e una libreria di networking asincrono, originariamente sviluppato presso FriendFeed. È particolarmente adatto per long-polling, WebSockets e altre applicazioni che richiedono una connessione di lunga durata con ogni utente. Il suo I/O di rete non bloccante lo rende estremamente scalabile e una scelta potente per la creazione di applicazioni web ad alte prestazioni. Questa guida completa ti guiderà attraverso i concetti fondamentali di Tornado e fornirà esempi pratici per iniziare.

Cos'è Tornado?

Nel suo nucleo, Tornado è un web framework e una libreria di networking asincrono. A differenza dei tradizionali web framework sincroni, Tornado utilizza un'architettura a singolo thread basata su un event loop. Ciò significa che può gestire molte connessioni concorrenti senza richiedere un thread per connessione, rendendolo più efficiente e scalabile.

Caratteristiche Principali di Tornado:

Configurare l'Ambiente Tornado

Prima di immergersi nello sviluppo con Tornado, è necessario configurare il proprio ambiente. Ecco una guida passo-passo:

  1. Installa Python: Assicurati di avere installato Python 3.6 o superiore. Puoi scaricarlo dal sito ufficiale di Python (python.org).
  2. Crea un Ambiente Virtuale (Consigliato): Usa venv o virtualenv per creare un ambiente isolato per il tuo progetto:
    python3 -m venv myenv
    source myenv/bin/activate  # Su Linux/macOS
    myenv\Scripts\activate  # Su Windows
  3. Installa Tornado: Installa Tornado usando pip:
    pip install tornado

La Tua Prima Applicazione Tornado

Creiamo una semplice applicazione "Hello, World!" con Tornado. Crea un file chiamato app.py e aggiungi il seguente codice:

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
 def get(self):
  self.write("Hello, World!")

def make_app():
 return tornado.web.Application([
  (r"/", MainHandler),
 ])

if __name__ == "__main__":
 app = make_app()
 app.listen(8888)
 tornado.ioloop.IOLoop.current().start()

Ora, esegui l'applicazione dal tuo terminale:

python app.py

Apri il tuo browser web e vai a http://localhost:8888. Dovresti vedere il messaggio "Hello, World!".

Spiegazione:

Request Handler e Routing

I gestori di richieste (request handler) sono le fondamenta delle applicazioni web Tornado. Definiscono come gestire le richieste HTTP in arrivo in base all'URL. Il routing mappa gli URL a specifici gestori di richieste.

Definire i Request Handler:

Per creare un gestore di richieste, si sottoclassa tornado.web.RequestHandler e si implementano i metodi HTTP appropriati (get, post, put, delete, ecc.).

class MyHandler(tornado.web.RequestHandler):
 def get(self):
  self.write("Questa è una richiesta GET.")

 def post(self):
  data = self.request.body.decode('utf-8')
  self.write(f"Dati POST ricevuti: {data}")

Routing:

Il routing viene configurato durante la creazione di tornado.web.Application. Si fornisce una lista di tuple, dove ogni tupla contiene un pattern di URL e il gestore di richieste corrispondente.

app = tornado.web.Application([
 (r"/", MainHandler),
 (r"/myhandler", MyHandler),
])

Pattern degli URL:

I pattern degli URL sono espressioni regolari. È possibile utilizzare gruppi di espressioni regolari per catturare parti dell'URL e passarle come argomenti ai metodi del gestore di richieste.

class UserHandler(tornado.web.RequestHandler):
 def get(self, user_id):
  self.write(f"ID Utente: {user_id}")

app = tornado.web.Application([
 (r"/user/([0-9]+)", UserHandler),
])

In questo esempio, /user/([0-9]+) corrisponde a URL come /user/123. La parte ([0-9]+) cattura una o più cifre e le passa come argomento user_id al metodo get di UserHandler.

Templating

Tornado include un motore di templating semplice ed efficiente. I template vengono utilizzati per generare HTML dinamicamente, separando la logica di presentazione dalla logica dell'applicazione.

Creare i Template:

I template sono tipicamente memorizzati in file separati (es. index.html). Ecco un semplice esempio:

<!DOCTYPE html>
<html>
<head>
 <title>Il Mio Sito Web</title>
</head>
<body>
 <h1>Benvenuto, {{ name }}!</h1>
 <p>Oggi è il {{ today }}.</p>
</body>
</html>

{{ name }} e {{ today }} sono segnaposto che verranno sostituiti con valori effettivi quando il template viene renderizzato.

Renderizzare i Template:

Per renderizzare un template, usa il metodo render() nel tuo gestore di richieste:

class TemplateHandler(tornado.web.RequestHandler):
 def get(self):
  name = "John Doe"
  today = "2023-10-27"
  self.render("index.html", name=name, today=today)

Assicurati che l'impostazione template_path sia configurata correttamente nelle impostazioni della tua applicazione. Di default, Tornado cerca i template in una directory chiamata templates nella stessa directory del file della tua applicazione.

app = tornado.web.Application([
 (r"/template", TemplateHandler),
], template_path="templates")

Sintassi dei Template:

I template di Tornado supportano varie funzionalità, tra cui:

Operazioni Asincrone

La forza di Tornado risiede nelle sue capacità asincrone. Le operazioni asincrone consentono alla tua applicazione di eseguire I/O non bloccante, migliorando le prestazioni e la scalabilità. Ciò è particolarmente utile per attività che comportano l'attesa di risorse esterne, come query di database o richieste di rete.

@tornado.gen.coroutine:

Il decoratore @tornado.gen.coroutine ti consente di scrivere codice asincrono utilizzando la parola chiave yield. Questo fa sì che il codice asincrono appaia e si comporti più come codice sincrono, migliorando la leggibilità e la manutenibilità.

import tornado.gen
import tornado.httpclient

class AsyncHandler(tornado.web.RequestHandler):
 @tornado.gen.coroutine
 def get(self):
  http_client = tornado.httpclient.AsyncHTTPClient()
  response = yield http_client.fetch("http://example.com")
  self.write(response.body.decode('utf-8'))

In questo esempio, http_client.fetch() è un'operazione asincrona che restituisce un Future. La parola chiave yield sospende l'esecuzione della coroutine fino a quando il Future non viene risolto. Una volta che il Future è risolto, la coroutine riprende e il corpo della risposta viene scritto al client.

tornado.concurrent.Future:

Un Future rappresenta il risultato di un'operazione asincrona che potrebbe non essere ancora disponibile. Puoi usare oggetti Future per concatenare operazioni asincrone e gestire gli errori.

tornado.ioloop.IOLoop:

L'IOLoop è il cuore del motore asincrono di Tornado. Monitora i descrittori di file e i socket per eventi e li invia ai gestori appropriati. In genere non è necessario interagire direttamente con l'IOLoop, ma è importante comprendere il suo ruolo nella gestione delle operazioni asincrone.

WebSockets

Tornado fornisce un eccellente supporto per i WebSockets, consentendo la comunicazione in tempo reale tra il server e i client. I WebSockets sono ideali per applicazioni che richiedono una comunicazione bidirezionale a bassa latenza, come applicazioni di chat, giochi online e dashboard in tempo reale.

Creare un Gestore WebSocket:

Per creare un gestore WebSocket, si sottoclassa tornado.websocket.WebSocketHandler e si implementano i seguenti metodi:

import tornado.websocket

class WebSocketHandler(tornado.websocket.WebSocketHandler):
 def open(self):
  print("WebSocket aperto")

 def on_message(self, message):
  self.write_message(f"Hai inviato: {message}")

 def on_close(self):
  print("WebSocket chiuso")

 def check_origin(self, origin):
  return True # Abilita le connessioni WebSocket cross-origin

Integrare i WebSockets nella Tua Applicazione:

Aggiungi il gestore WebSocket alla configurazione di routing della tua applicazione:

app = tornado.web.Application([
 (r"/ws", WebSocketHandler),
])

Implementazione Lato Client:

Sul lato client, puoi usare JavaScript per stabilire una connessione WebSocket e inviare/ricevere messaggi:

const websocket = new WebSocket("ws://localhost:8888/ws");

websocket.onopen = () => {
 console.log("Connessione WebSocket stabilita");
 websocket.send("Ciao dal client!");
};

websocket.onmessage = (event) => {
 console.log("Messaggio ricevuto:", event.data);
};

websocket.onclose = () => {
 console.log("Connessione WebSocket chiusa");
};

Autenticazione e Sicurezza

La sicurezza è un aspetto critico dello sviluppo di applicazioni web. Tornado fornisce diverse funzionalità per aiutarti a proteggere le tue applicazioni, tra cui l'autenticazione, l'autorizzazione e la protezione contro le vulnerabilità web comuni.

Autenticazione:

L'autenticazione è il processo di verifica dell'identità di un utente. Tornado fornisce supporto integrato per vari schemi di autenticazione, tra cui:

Autorizzazione:

L'autorizzazione è il processo per determinare se un utente ha il permesso di accedere a una particolare risorsa. Puoi implementare la logica di autorizzazione nei tuoi gestori di richieste per limitare l'accesso in base ai ruoli o ai permessi dell'utente.

Best Practice di Sicurezza:

Deployment

Il deployment di un'applicazione Tornado comporta diversi passaggi, tra cui la configurazione di un server web, l'impostazione di un gestore di processi e l'ottimizzazione delle prestazioni.

Web Server:

Puoi eseguire il deployment di Tornado dietro un web server come Nginx o Apache. Il web server funge da reverse proxy, inoltrando le richieste in arrivo all'applicazione Tornado.

Gestore di Processi:

Un gestore di processi come Supervisor o systemd può essere utilizzato per gestire il processo Tornado, assicurando che venga riavviato automaticamente in caso di crash.

Ottimizzazione delle Prestazioni:

Internazionalizzazione (i18n) e Localizzazione (l10n)

Quando si creano applicazioni per un pubblico globale, è importante considerare l'internazionalizzazione (i18n) e la localizzazione (l10n). L'i18n è il processo di progettazione di un'applicazione in modo che possa essere adattata a varie lingue e regioni senza modifiche ingegneristiche. La l10n è il processo di adattamento di un'applicazione internazionalizzata per una lingua o regione specifica aggiungendo componenti specifici della locale e traducendo il testo.

Tornado e i18n/l10n

Tornado di per sé non ha librerie i18n/l10n integrate. Tuttavia, è possibile integrare facilmente librerie Python standard come `gettext` o framework più sofisticati come Babel per gestire i18n/l10n all'interno della tua applicazione Tornado.

Esempio usando `gettext`:

1. **Imposta le tue locali:** Crea directory per ogni lingua che vuoi supportare, contenenti i cataloghi dei messaggi (solitamente file `.mo`).

locales/
 en/LC_MESSAGES/messages.mo
 fr/LC_MESSAGES/messages.mo
 de/LC_MESSAGES/messages.mo

2. **Estrai le stringhe traducibili:** Usa uno strumento come `xgettext` per estrarre le stringhe traducibili dal tuo codice Python in un file `.po` (Portable Object). Questo file conterrà le stringhe originali e i segnaposto per le traduzioni.

xgettext -d messages -o locales/messages.po your_tornado_app.py

3. **Traduci le stringhe:** Traduci le stringhe nei file `.po` per ogni lingua.

4. **Compila le traduzioni:** Compila i file `.po` in file `.mo` (Machine Object) che vengono utilizzati da `gettext` a runtime.

msgfmt locales/fr/LC_MESSAGES/messages.po -o locales/fr/LC_MESSAGES/messages.mo

5. **Integra nella tua applicazione Tornado:**

import gettext
import locale
import os
import tornado.web

class BaseHandler(tornado.web.RequestHandler):
 def initialize(self):
  try:
  locale.setlocale(locale.LC_ALL, self.get_user_locale().code)
  except locale.Error:
  # Gestisce i casi in cui la locale non è supportata dal sistema
  print(f"La locale {self.get_user_locale().code} non è supportata")

  translation = gettext.translation('messages', 'locales', languages=[self.get_user_locale().code])
  translation.install()
  self._ = translation.gettext

 def get_current_user_locale(self):
  # Logica per determinare la locale dell'utente (es. dall'header Accept-Language, impostazioni utente, ecc.)
  # Questo è un esempio semplificato - avrai bisogno di una soluzione più robusta
  accept_language = self.request.headers.get('Accept-Language', 'en')
  return tornado.locale.get(accept_language.split(',')[0].split(';')[0])

class MainHandler(BaseHandler):
 def get(self):
  self.render("index.html", _=self._)

settings = {
 "template_path": os.path.join(os.path.dirname(__file__), "templates"),
}

app = tornado.web.Application([
 (r"/", MainHandler),
], **settings)

6. **Modifica i tuoi template:** Usa la funzione `_()` (associata a `gettext.gettext`) per contrassegnare le stringhe da tradurre nei tuoi template.

<h1>{{ _("Benvenuti nel nostro sito web!") }}</h1>
<p>{{ _("Questo è un paragrafo tradotto.") }}</p>

Considerazioni Importanti per un Pubblico Globale:

Argomenti Avanzati

Pagine di Errore Personalizzate:

Puoi personalizzare le pagine di errore che Tornado visualizza quando si verifica un errore. Ciò ti consente di fornire un'esperienza più user-friendly e di includere informazioni di debug.

Impostazioni Personalizzate:

Puoi definire impostazioni personalizzate nella configurazione della tua applicazione e accedervi nei tuoi gestori di richieste. Questo è utile per memorizzare parametri specifici dell'applicazione, come stringhe di connessione al database o chiavi API.

Testing:

Testa approfonditamente le tue applicazioni Tornado per assicurarti che funzionino correttamente e in modo sicuro. Usa test unitari, test di integrazione e test end-to-end per coprire tutti gli aspetti della tua applicazione.

Conclusione

Tornado è un web framework potente e versatile, particolarmente adatto per la creazione di applicazioni web scalabili e ad alte prestazioni. La sua architettura asincrona, il supporto per i WebSockets e un'API facile da usare lo rendono una scelta popolare per gli sviluppatori di tutto il mondo. Seguendo le linee guida e gli esempi di questa guida completa, puoi iniziare a costruire le tue applicazioni Tornado e sfruttare le sue numerose funzionalità.

Ricorda di consultare la documentazione ufficiale di Tornado per le informazioni più aggiornate e le best practice. Buona programmazione!