HTTP Encoding
Panoramica
Goa supporta una strategia di encoding e decoding estremamente flessibile, che rende possibile l’associazione di encoder e decoder arbitrari con le richieste e risposte HTTP date attraverso diversi content types. Un encoder è una struct che implementa l’interfaccia Encoder mentre un decoder implementa Decoder.
I costruttori del server generati accettano dei costruttori di encoder e decoder come argomento, rendendo possibile implementazioni libere. Goa ha anche degli encoder e decoder di default, che supportano JSON, XML e gob. Ecco un esempio per la firma del costruttore server generata da Goa (dall’esempio basic):
// New istanzia gli handler HTTP per tutti i service endpoints di calc.
func New(
e *calc.Endpoints,
mux goahttp.Muxer,
dec func(*http.Request) goahttp.Decoder,
enc func(context.Context, http.ResponseWriter) goahttp.Encoder,
eh func(context.Context, http.ResponseWriter, error),
) *Server
L’argomento dec
è una funzione che accetta una richiesta e ritorna un decoder. Goa la invoca
per ogni richiesta, rendendo possibile di sfruttare diversi decoder per diverse richieste HTTP.
L’argomento enc
è una funzione che accetta un context e una writer di risposte HTTP e
ritorna un encoder.
Costruttori di Default per Encoder e Decoder
Il package fornito da Goa fornisce dei costruttori di default per HTTP encoder e decoder che possono codificare e decodificare JSON, XML e gob. Ecco un esempio di come il generatore usa questi costruttori:
var (
dec = goahttp.RequestDecoder
enc = goahttp.ResponseEncoder
)
//...
var (
calcServer *calcsvr.Server
)
{
// ...
calcServer = calcsvr.New(calcEndpoints, mux, dec, enc, eh)
}
Decoding
Il decoder di default controlla il Content-Type
delle richieste ed effettua il match con un decoder
Il valore application/json
è mappato al decoder JSON, application/xml
al decoder XML e
application/gob
al decoder gob. Il decoder JSON è anche usato quando l’header Content-Type
è assente o non ha match con un decoder conosciuto. Se il decoding fallisce Goa
scrive una risposta di errore usando il codice di stato 400 Bad Request e scrive un oggetto
ServiceError nel
corpo della risposta (vedi
Gestione degli errori per avere più
informazioni su come gli errori vendono tradotti in risposte HTTP).
Implementare un Decoder personalizzato
Come indicato nella Panoramica, scrivere un decoder consiste nell’implementare l’interfaccia
Decoder
e il costruttore usando la seguente firma:
func(r *http.Request) (goahttp.Decoder, error)
dove goahttp
è un alias per il package goa.design/goa/http
. Il costruttore
ha accesso al request object e può quindi ispezionare il suo stato per inferire il miglior decoder
possibile. La funzione è quindi passata al costruttore per il server (generato) come mostrato nella panoramica.
Encoding
L’encoder di default implementa un semplice algoritmo di negoziazione che sfrutta il valore dell’header
Accept
della richiesta oppure Content-Type
laddove il primo fosse assente.
L’algoritmo pulisce il valore dell’header e lo confronta con i MIME types JSON, XML
e gob per inferire l’encoder corretto. L’algoritmo ha il suo default su JSON qualora non ci fossero header Accept
o
Content-Type
validi o se il valore non ha un match con un encoder conosciuto.
Implementare un Encoder personalizzato
Ci sono vari motivi per cui il tuo servizio potrebbe usare encoder differenti. Per esempio potresti
voler cambiare e non usare la libreria JSON standard, ma un package diverso con prestazioni migliori
per il tuo caso d’uso. Puoi anche supportare diversi formati di serializzazione, come msgpack
.
Come indicato nella panoramica un encoder deve implementare l’interfaccia Encoder
e può
essere passato al costruttore del server (generato) con la seguente firma:
func(ctx context.Context, w http.ResponseWriter) (goahttp.Encoder, error)
Il context dato quando Goa richiama il costruttore contiene sia l’header Content-Type
che
Accept
rispettivamente sotto le chiaviContentTypeKey
e AcceptTypeKey
.
Questo rende possibile al costruttore dell’encoder di implementare forme differenti di negoziazione che
controllano questi 2 valori e restituiscono il miglior encoder possibile.
Impostare un Content Type di default
Il DSL Response permette di specificare un
content type usando ContentType. Quando
viene impostato, il valore sovrascrive ogni content type specificato nei request headers.
Nota che questo NON sovrascrive alcun valore specificato nell’header Accept
.
Questo permette di controllare il content type anche quando tale header è assente.