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 ➡️