Capítulo 04: Middlewares Customizados no Gin (Auth, Logger, Recovery, CORS)
Especialização em Backend com Go & Gin • Gin Framework & Go 1.22+ • GORM, Clean Architecture, Concorrência e Alta Performance
🗺️ Mapa Conceitual do Tópico
flowchart TD
A["Requisição"] --> B["Middleware 1: CORS"]
B --> C["Middleware 2: Logger (antes de c.Next)"]
C --> D["Middleware 3: Recovery (defer + recover)"]
D --> E["Middleware 4: Auth (c.Abort se inválido)"]
E --> F["Handler Final"]
F -->|"retorna pela pilha"| C
C -->|"loga latência após c.Next()"| G["Resposta"]
style A fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
style B fill:#fff3e0,stroke:#ff9800,stroke-width:2px
style C fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
style D fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
style E fill:#fce4ec,stroke:#e91e63,stroke-width:2px
style F fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px
style G fill:#fffde7,stroke:#fbc02d,stroke-width:2px
🏛️ 1. Fundamentos Técnicos de Middlewares Customizados no Gin (Auth, Logger, Recovery, CORS)
Um middleware no Gin é simplesmente uma função com assinatura func(c *gin.Context) (o tipo gin.HandlerFunc) registrada com engine.Use(...) ou anexada a um grupo de rotas. O que torna essa peça poderosa é a função c.Next(): ela implementa o padrão interceptor — quando um middleware chama c.Next(), a execução é pausada naquele ponto e o controle passa para o próximo middleware (ou para o handler final) na cadeia; quando esse handler final termina, a execução retorna para a linha logo após c.Next() no middleware anterior. É esse “antes/depois” que permite, por exemplo, calcular a latência de uma requisição: registra-se time.Now() antes de c.Next() e calcula-se time.Since(t) depois.
O padrão de recuperação de panics funciona de forma parecida com qualquer defer/recover em Go, mas aplicado por requisição: um middleware de recovery instala um defer func() { if r := recover(); r != nil { ... } }() logo no início, chama c.Next(), e se qualquer handler adiante entrar em pânico, o recover() intercepta antes que a panic suba até a goroutine do servidor HTTP (que, se não tratada, derrubaria a conexão e, em casos extremos, o processo inteiro). É por isso que gin.Recovery() é praticamente obrigatório em produção — sem ele, um único bug de nil-pointer em um handler poderia matar o servidor.
Para interromper a cadeia antecipadamente (por exemplo, quando um token de autenticação é inválido), o método correto é c.AbortWithStatusJSON(status, corpo) — ele escreve a resposta e marca o contexto como abortado, o que impede que o Gin continue chamando os middlewares/handlers seguintes mesmo que eles não checkem explicitamente essa condição. Usar apenas c.JSON(...) sem c.Abort() não impede a execução dos próximos handlers, um erro comum que leva a respostas duplicadas ou a lógica de negócio rodando sobre uma requisição que já deveria ter sido rejeitada.
Middlewares também são o canal correto para compartilhar dados calculados entre etapas da pipeline: c.Set("usuario_id", id) grava no mapa de chave-valor por requisição do *gin.Context, e c.Get("usuario_id") (ou a variante tipada c.MustGet) recupera esse valor em qualquer handler posterior. Isso é preferível a variáveis globais, que em um servidor concorrente como o Gin (cada requisição roda em sua própria goroutine) causariam data races graves.
Por fim, o middleware de CORS (Cross-Origin Resource Sharing) resolve o problema de navegadores bloquearem requisições de um domínio para outro: ele intercepta a requisição, inspeciona o header Origin e responde com os headers Access-Control-Allow-Origin, Access-Control-Allow-Methods e Access-Control-Allow-Headers apropriados — tratando especialmente o método OPTIONS (o preflight request que os navegadores disparam antes de requisições “não simples”).
func CustomLogger() gin.HandlerFunc {
return func(c *gin.Context) {
inicio := time.Now()
c.Next() // executa o restante da cadeia e o handler final
latencia := time.Since(inicio)
log.Printf("[GIN] %s %s %d (%s)", c.Request.Method, c.Request.URL.Path, c.Writer.Status(), latencia)
}
}
💻 2. Código de Demonstração Corporativo
package main
import (
"log"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
func CORSMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization, X-API-Key")
if c.Request.Method == http.MethodOptions {
c.AbortWithStatus(http.StatusNoContent)
return
}
c.Next()
}
}
func RequestLogger() gin.HandlerFunc {
return func(c *gin.Context) {
inicio := time.Now()
c.Next()
log.Printf("[GIN] %s %s %d (%s)",
c.Request.Method, c.Request.URL.Path, c.Writer.Status(), time.Since(inicio))
}
}
func PanicRecovery() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
log.Printf("PANIC recuperado: %v", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"erro": "erro interno do servidor"})
}
}()
c.Next()
}
}
func ExigirChaveAPI() gin.HandlerFunc {
return func(c *gin.Context) {
if c.GetHeader("X-API-Key") != "chave-secreta-empresa" {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"erro": "chave de API inválida"})
return
}
c.Set("cliente_autenticado", true)
c.Next()
}
}
func main() {
engine := gin.New()
// Ordem importa: CORS e Recovery primeiro, autenticação depois.
engine.Use(CORSMiddleware(), RequestLogger(), PanicRecovery())
protegido := engine.Group("/api", ExigirChaveAPI())
protegido.GET("/dados", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"autenticado": true})
})
engine.Run(":8080")
}
🔗 Recursos Pedagógicos do Capítulo 04
| Recurso Didático | Finalidade | Link de Acesso |
|---|---|---|
| 📊 Slides de Aula | Apresentação visual interativa com Dark Mode e suporte a teclado | Ver Slides |
| 🧠 Quiz Formativo | Teste interativo de fixação com feedback imediato por alternativa | Fazer Quiz |
| 💻 Exemplos de Código | Demonstrações funcionais com código executável | Ver Exemplos |
| 🧩 Exercícios em 4 Níveis | Lista progressiva de fixação com gabarito em bloco colapsável | Resolver Exercícios |
| ⬅️ Capítulo Anterior | 📚 Sumário de Tópicos | Próximo Capítulo ➡️ |