Un riepilogo della gestione delle richieste in Go
L’elaborazione delle richieste HTTP con Go riguarda principalmente due cose: SERVEMUX e gestori.
Un ServeMux è essenzialmente un router di richiesta HTTP (o multiplexor). Confronta le richieste in arrivo con un elenco di percorsi URL predefiniti e chiama il gestore associato per il percorso ogni volta che viene trovata una corrispondenza.
I gestori sono responsabili della scrittura di intestazioni e corpi di risposta. Quasi ogni oggetto può essere un gestore, purché soddisfi l’interfacciahttp.Handler
. In parole povere, che significa semplicemente che deve avere un ServeHTTP
metodo con la seguente firma:
ServeHTTP(http.ResponseWriter, *http.Request)
Go HTTP pacchetto viene fornito con un paio di funzioni per generare gestori comuni, come ad esempio FileServer
NotFoundHandler
e RedirectHandler
. Iniziamo con un esempio semplice ma artificioso:
Passiamo rapidamente a questo:
- Nella funzione
main
usiamo la funzionehttp.NewServeMux
per creare un ServeMux vuoto. - Usiamo quindi la funzione
http.RedirectHandler
per creare un nuovo gestore. Questo gestore 307 reindirizza tutte le richieste ricevute ahttp://example.org
. - Successivamente usiamo la funzione
mux.Handle
per registrarlo con il nostro nuovo ServeMux, quindi funge da gestore per tutte le richieste in arrivo con il percorso URL/foo
. - Infine creiamo un nuovo server e iniziamo ad ascoltare le richieste in arrivo con la funzione
http.ListenAndServe
, passando nel nostro ServeMux per farlo corrispondere alle richieste.
Vai avanti ed esegui l’applicazione:
E visitahttp://localhost:3000/foo
nel tuo browser. Dovresti scoprire che la tua richiesta viene reindirizzata con successo.
L’occhio d’aquila di te potrebbe aver notato qualcosa di interessante: La firma per la funzione ListenAndServe èListenAndServe(addr string, handler Handler)
, ma abbiamo passato un ServeMux come secondo parametro.
Siamo stati in grado di farlo perché il tipo ServeMux ha anche un metodoServeHTTP
, il che significa che soddisfa anche l’interfaccia del gestore.
Per me semplifica le cose pensare a un ServeMux come ad un tipo speciale di gestore, che invece di fornire una risposta passa la richiesta a un secondo gestore. Questo non è tanto un salto quanto sembra per la prima volta – concatenare i gestori insieme è abbastanza comune in Go.
i Gestori Personalizzati
creare un gestore personalizzato che risponde con l’ora locale corrente in un dato formato:
Il codice esatto, qui non è troppo importante.
Tutto ciò che conta davvero è che abbiamo un oggetto (in questo caso è una struttura timeHandler
, ma potrebbe essere ugualmente una stringa o una funzione o qualsiasi altra cosa), e abbiamo implementato un metodo con la firma ServeHTTP(http.ResponseWriter, *http.Request)
su di esso. E ‘ tutto quello che ci serve per creare un responsabile.
Incorporiamo questo in un esempio concreto:
Nella funzione main
abbiamo inizializzato il timeHandler
esattamente nello stesso modo in cui faremmo qualsiasi struttura normale, usando il &
simbolo per produrre un puntatore. E poi, come nell’esempio precedente, usiamo la funzionemux.Handle
per registrarlo con il nostro ServeMux.
Ora quando eseguiamo l’applicazione, il ServeMux passerà qualsiasi richiesta per/time
direttamente al nostrotimeHandler.ServeHTTP
metodo.
Vai avanti e provalo: http://localhost:3000/time
.
si Noti anche che potremmo riutilizzare facilmente il timeHandler
in più percorsi:
Funzioni come Gestori di
Per i casi più semplici (come l’esempio di cui sopra) la definizione di nuovi tipi personalizzati e ServeHTTP metodi si sente un po ‘ prolisso. Diamo un’occhiata a un approccio alternativo, in cui sfruttiamo il tipo http.HandlerFunc
di Go per costringere una funzione normale a soddisfare l’interfaccia del gestore.
Qualsiasi funzione che ha la firmafunc(http.ResponseWriter, *http.Request)
può essere convertita in un tipo HandlerFunc. Ciò è utile perché gli oggetti HandleFunc sono dotati di un metodo ServeHTTP
integrato che, in modo piuttosto intelligente e conveniente, esegue il contenuto della funzione originale.
Se sembra confuso, prova a dare un’occhiata al codice sorgente pertinente. Vedrai che è un modo molto succinto di fare in modo che una funzione soddisfi l’interfaccia del gestore.
Riproduciamo l’applicazione timeHandler utilizzando questa tecnica:
In effetti, convertire una funzione in un tipo HandlerFunc e quindi aggiungerla a un ServeMux in questo modo è così comune che Go fornisce una scorciatoia: ilmux.HandleFunc
metodo.
Questo è ciò che il main()
funzione avrebbe guardato come se avessimo usato questo collegamento invece:
la Maggior parte del tempo, utilizzando una funzione come un gestore di come questo funziona bene. Ma c’è un po ‘ di una limitazione quando le cose iniziano a diventare più complesse.
Probabilmente hai notato che, a differenza del metodo precedente, abbiamo dovuto codificare il formato dell’ora nella funzionetimeHandler
. Cosa succede quando vogliamo passare informazioni o variabili da main()
a un gestore?
Un approccio pulito consiste nel mettere la nostra logica del gestore in una chiusura e chiudere le variabili che vogliamo usare:
La funzionetimeHandler
ha ora un ruolo leggermente diverso. Invece di forzare la funzione in un gestore (come abbiamo fatto in precedenza), ora lo stiamo usando per restituire un gestore. Ci sono due elementi chiave per fare questo lavoro.
Prima creafn
, una funzione anonima che accede‐ o chiude sopra – la variabileformat
formando una chiusura. Indipendentemente da ciò che facciamo con la chiusura, sarà sempre in grado di accedere alle variabili locali all’ambito in cui è stato creato, il che in questo caso significa che avrà sempre accesso alla variabile format
.
In secondo luogo la nostra chiusura ha la firmafunc(http.ResponseWriter, *http.Request)
. Come ricorderai da prima, questo significa che possiamo convertirlo in un tipo HandlerFunc (in modo che soddisfi l’interfaccia del gestore). La nostra funzionetimeHandler
restituisce quindi questa chiusura convertita.
In questo esempio abbiamo appena passato una semplice stringa a un gestore. Ma in un’applicazione del mondo reale è possibile utilizzare questo metodo per passare la connessione al database, la mappa del modello o qualsiasi altro contesto a livello di applicazione. È una buona alternativa all’utilizzo di variabili globali e ha il vantaggio di creare gestori autonomi per i test.
Potresti anche vedere questo stesso pattern scritto come:
O usando una conversione implicita al tipo HandlerFunc al ritorno:
Il DefaultServeMux
Probabilmente hai visto DefaultServeMux menzionato in molti posti, dai più semplici esempi di Hello World al codice sorgente Go.
Mi ci è voluto molto tempo per capire che non è niente di speciale. Il DefaultServeMux è solo un semplice ServeMux come abbiamo già usato, che viene istanziato di default quando viene utilizzato il pacchetto HTTP. Ecco la riga pertinente dalla fonte Go:
var DefaultServeMux = NewServeMux()
In genere non si dovrebbe usare DefaultServeMux perché rappresenta un rischio per la sicurezza.
Poiché DefaultServeMux è memorizzato in una variabile globale, qualsiasi pacchetto è in grado di accedervi e registrare un percorso, inclusi i pacchetti di terze parti importati dall’applicazione. Se uno di questi pacchetti di terze parti è compromesso, potrebbero utilizzare DefaultServeMux per esporre un gestore dannoso al Web.
Quindi, come regola generale, è una buona idea evitare DefaultServeMux e utilizzare invece il proprio ServeMux con ambito locale, come siamo stati finora. Ma se hai deciso di usarlo…
Il pacchetto HTTP fornisce un paio di scorciatoie per lavorare con DefaultServeMux: http.Gestire e http.HandleFunc. Questi fanno esattamente lo stesso delle loro funzioni omonime che abbiamo già esaminato, con la differenza che aggiungono gestori al DefaultServeMux invece di uno che hai creato.
Inoltre, ListenAndServe tornerà a utilizzare DefaultServeMux se non viene fornito alcun altro gestore (ovvero, il secondo parametro è impostato sunil
).
Quindi, come passo finale, aggiorniamo la nostra applicazione timeHandler per utilizzare invece DefaultServeMux:
Se ti è piaciuto questo post del blog, non dimenticare di dare un’occhiata al mio nuovo libro su come creare applicazioni web professionali con Go!
Seguimi su Twitter @ ajmedwards.
Tutti i frammenti di codice in questo post sono liberi di utilizzare sotto la licenza MIT.