Trafegando dados entre middleware http usando contexto em Golang
Veja o vídeo desse arquivo aqui.
Trafegando dados entre middleware
Já vimos como funciona um middleware HTTP e agora vamos ver como passar informação entre os middlewares. Isso é usado por exemplo para passar as credenciais de um usuário para o próximo middleware e qualquer outra informação que seja coletada em algum dos middlewares e você queria passar para frente.
Antes de mais nada vamos fazer um exemplo para mostrar da forma mais clara possível quando os middlewares são executados, isso é muito importante porque erros no entendimento dessa ordem de execução é uma incrível fonte de bugs. No exemplo abaixo colocamos mensagens no terminal toda vez que um middleware é carregado e descarregado. E é sempre bom reforçar isso vai acontecer na mesma ordem em que eles foram registrados.
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/urfave/negroni"
)
func handleMain(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte("{\"value\":42}\n"))
if err != nil {
fmt.Println("error handleMain", err)
}
}
func middleware1() negroni.Handler {
fmt.Println("carregando middleware 1")
return negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
fmt.Println("empilhando middleware 1")
next(w, r)
fmt.Println("desempilhando middleware 1")
})
}
func middleware2() negroni.Handler {
fmt.Println("carregando middleware 2")
return negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
fmt.Println("empilhando middleware 2")
next(w, r)
fmt.Println("desempilhando middleware 2")
})
}
func middleware3() negroni.Handler {
fmt.Println("carregando middleware 3")
return negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
fmt.Println("empilhando middleware 3")
next(w, r)
fmt.Println("desempilhando middleware 3")
})
}
func main() {
n := negroni.Classic()
n.Use(middleware1())
n.Use(middleware2())
n.Use(middleware3())
fmt.Println("-=-=-=-=-=-=-=-=-=-=-=-=-=-")
r := mux.NewRouter().StrictSlash(true)
n.UseHandler(r)
r.HandleFunc("/", handleMain).Methods("GET")
fmt.Println("main listen at :8080")
err := http.ListenAndServe(":8080", n)
if err != nil {
fmt.Println(err)
}
}
A maneira canônica de passar informações a diante durante o processamento de uma requisição HTTP é usando contexto. Veja o exemplo.
package main
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/urfave/negroni"
)
type key int
const (
dataKey key = iota
)
type data struct {
ValueA string `json:"value_a"`
ValueB int `json:"value_b"`
}
func setContextData(r *http.Request, d *data) (ro *http.Request) {
ctx := r.Context()
ctx = context.WithValue(ctx, dataKey, d)
ro = r.WithContext(ctx)
return
}
func getContextData(r *http.Request) (d data) {
d = *r.Context().Value(dataKey).(*data)
return
}
func middleware1() negroni.Handler {
return negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
d := data{
ValueA: "valor A",
ValueB: 42,
}
r = setContextData(r, &d)
next(w, r)
})
}
func middleware2() negroni.Handler {
return negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
d := getContextData(r)
d.ValueA += "A"
r = setContextData(r, &d)
next(w, r)
})
}
func middleware3() negroni.Handler {
return negroni.HandlerFunc(func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
d := getContextData(r)
d.ValueA += "A"
r = setContextData(r, &d)
next(w, r)
})
}
func handleMain(w http.ResponseWriter, r *http.Request) {
d := getContextData(r)
j, err := json.MarshalIndent(d, "", "\t")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
_, err = w.Write(j)
if err != nil {
fmt.Println(err)
}
}
func main() {
n := negroni.Classic()
n.Use(middleware1())
n.Use(middleware2())
n.Use(middleware3())
r := mux.NewRouter().StrictSlash(true)
n.UseHandler(r)
r.HandleFunc("/", handleMain).Methods("GET")
fmt.Println("main listen at :8080")
err := http.ListenAndServe(":8080", n)
if err != nil {
fmt.Println(err)
}
}
Tem muito mais exemplos no repositório do nosso grupo de estudos de Go, vale a pena dar uma conferida e também colaborar, estamos sempre precisando de ajuda para ter uma material completo e atualizado.