Padrões de Middleware & Decorator
Middleware em Go envolve um http.Handler com outro http.Handler para adicionar comportamento transversal: logging, autenticação, recuperação e métricas.
Busque em todas as páginas da documentação
Middleware em Go envolve um http.Handler com outro http.Handler para adicionar comportamento transversal: logging, autenticação, recuperação e métricas.
É a forma prática do padrão decorator em uma linguagem sem herança.
Cada middleware recebe o próximo handler, retorna um novo handler e decide se executa a lógica antes, depois ou em torno da chamada interna.
O servidor net/http invoca o handler mais externo; o middleware aninha para dentro até que o handler da rota seja executado.
O mesmo modelo de composição aparece em chi, gin, echo e interceptores gRPC com helpers de registro específicos do framework.
Cartão de referência rápida - pronto para copiar e colar.
func logging(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
slog.Info("handled", "path", r.URL.Path, "ms", time.Since(start).Milliseconds())
})
}Quando usar isso:
package main
import (
"context"
"log"
"net/http"
"time"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
type ctxKey int
const userKey ctxKey = 1
func withUser(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
ctx := context.WithValue(r.Context(), userKey, "demo-user")
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func greet(w http.ResponseWriter, r *http.Request) {
user, _ := r.Context().Value(userKey).(string)
w.Write([]byte("hello " + user))
}
func main() {
r := chi.NewRouter()
r.Use(middleware.RequestID)
r.Use(middleware.Recoverer)
r.Use(withUser)
r.Get("/greet", greet)
srv := &http.Server{
Addr: ":8080",
Handler: r,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
log.Fatal(srv.ListenAndServe())
}O que isso demonstra:
r.Use registra middleware para rotas registradas nesse roteadorhttp.Error antes de chamar nextcontext.ContextRequest -->
Middleware RequestID
--> Middleware Recoverer
--> Middleware withUser
--> Handler greet
<-- withUser (após next retornar)
<-- Recoverer
<-- RequestID
Response <--next.ServeHTTP a menos que ele lide completamente com a requisiçãoResponseWriter captura o status e o tamanho do corpo para métricastype statusWriter struct {
http.ResponseWriter
code int
}
func (w *statusWriter) WriteHeader(code int) {
w.code = code
w.ResponseWriter.WriteHeader(code)
}http.ResponseWriter para delegar a maioria dos métodosWriteHeader e Write para observar o comportamentohttp.ResponseController (Go 1.20+) para flush/hijack quando necessário| Framework | Registro | Tipo de Handler |
|---|---|---|
| net/http | Aninhamento manual | http.Handler |
| chi | r.Use(mw) | http.Handler |
| gin | engine.Use(mw) | gin.HandlerFunc |
| echo | e.Use(mw) | echo.MiddlewareFunc |
| gRPC | grpc.ChainUnaryInterceptor | função interceptora |
next - Requisição fica pendente ou retorna resposta vazia. Correção: sempre chame next.ServeHTTP a menos que você tenha escrito a resposta completa."user". Correção: constantes de chave tipada privada por pacote.middleware.NewWrapResponseWriter do chi ou implemente interfaces opcionais.Recoverer como o mais externo; autenticação antes de handlers que precisam de identidade.main.| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| Verificações de handler inline | Uma rota precisa de autenticação | Muitas rotas compartilham a preocupação |
| Cadeia de middleware HTTP | Preocupações de transporte transversais | Ramificações de negócios principais |
| Structs decorator incorporando Handler | Servidores customizados com padrões | Servidor de arquivos estáticos simples |
| Service mesh / autenticação de gateway | Política de borda centralizada | Simplicidade de desenvolvimento local importa |
Sim em função - interceptores gRPC unários/de stream envolvem handlers de RPC da mesma forma que middleware HTTP envolve ServeHTTP.
Use httptest.NewRecorder e httptest.NewRequest; passe um handler next stub que define uma flag quando chamado.
Sim, mas leia e restaure com cuidado; prefira limitar o tamanho do corpo no nível do servidor (MaxBytesReader).
Middleware valida credenciais e anexa identidade ao contexto; serviços aplicam regras de autorização sobre recursos.
Não há um máximo fixo - mas se a ordem for difícil de raciocinar, agrupe preocupações relacionadas ou documente uma pilha padrão em main.
No chi, sim para rotas registradas no roteador; caminhos não correspondentes atingem o handler NotFound após a mesma pilha Use.
Registre a rota em um sub-roteador sem esse middleware, ou use no-op dentro do middleware com base no prefixo do caminho.
Prefira slog com atributos de escopo de requisição (ID da requisição do contexto) para logs estruturados.
gin envolve http.Request em Context; middleware ainda compõe, mas usa a API do gin em vez de handlers brutos.
Sim - envolva o mux: logging(mux) ou registre handlers por método individualmente envolvidos.
Versões da Stack: Esta página foi escrita para Go 1.26.x (padrão GC Green Tea, go fix modernizers - verifique o patch na compilação), chi (última versão - verifique na compilação), gin (última versão - verifique na compilação), echo (última versão - verifique na compilação), google.golang.org/grpc (última versão - verifique na compilação), sigs.k8s.io/controller-runtime (última versão - verifique na compilação), kubebuilder (última versão - verifique na compilação), tinygo (última versão - verifique os alvos de placa na compilação), wazero (última versão - verifique na compilação) e golangci-lint (última versão - verifique na compilação).
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026