Backend - Middlewares¶
Este documento descreve todos os middlewares do backend, suas responsabilidades, uso e como criar novos middlewares.
Índice¶
- Visão Geral
- AuthMiddleware
- RoleMiddleware
- ProfileApprovalMiddleware
- RateLimitMiddleware
- Criando Novos Middlewares
Visão Geral¶
Middlewares são funções que interceptam requisições HTTP antes de chegarem aos handlers. Eles são executados em sequência e podem:
- Validar autenticação
- Verificar permissões
- Adicionar dados ao contexto
- Modificar requisições/respostas
- Interromper o fluxo (retornar erro)
Localização: internal/middleware/
Framework: Echo v4
Ordem de Execução:
Request → Middleware 1 → Middleware 2 → ... → Handler → Response
AuthMiddleware¶
Responsabilidade¶
Valida tokens JWT e adiciona informações do usuário ao contexto do Echo.
Localização: internal/middleware/auth/auth.go
Funcionalidade¶
- Extrai token do header
Authorization: Bearer <token> - Valida formato do token
- Valida assinatura do token
- Verifica expiração
- Adiciona claims ao contexto:
user_id(uint)user_type(string)email(string)
Uso¶
api := e.Group("/api")
api.Use(authMiddleware.AuthMiddleware())
Dados no Contexto¶
Após passar pelo middleware, o handler pode acessar:
userID := c.Get("user_id").(uint)
userType := c.Get("user_type").(string)
email := c.Get("email").(string)
Verificação Segura¶
userID, ok := c.Get("user_id").(uint)
if !ok {
return echo.NewHTTPError(http.StatusUnauthorized, "User ID not found")
}
Rotas Públicas¶
Rotas que não devem passar pelo middleware devem ser registradas antes:
// Rotas públicas (antes do middleware)
e.POST("/api/auth/login", loginHandler)
e.GET("/api/plans", plansHandler)
// Rotas protegidas (depois do middleware)
api := e.Group("/api")
api.Use(authMiddleware.AuthMiddleware())
api.GET("/cycles", cyclesHandler)
Erros¶
401 Unauthorized- Token ausente, inválido ou expirado- Mensagem: "Invalid or expired token"
RoleMiddleware¶
Responsabilidade¶
Verifica se o user_type do token está na lista de roles permitidas.
Localização: internal/middleware/role/role.go
Funcionalidade¶
- Extrai
user_typedo contexto (adicionado por AuthMiddleware) - Verifica se está na lista de roles permitidas
- Se não estiver, retorna
403 Forbidden
Uso¶
Apenas Admin:
adminRoutes := api.Group("")
adminRoutes.Use(role.RoleMiddleware([]string{"admin"}))
adminRoutes.GET("/users", getUsersHandler)
Admin ou Professional:
usersRoutes := api.Group("")
usersRoutes.Use(role.RoleMiddleware([]string{"admin", "professional"}))
usersRoutes.GET("/users", getUsersHandler)
Apenas Client:
clientRoutes := api.Group("")
clientRoutes.Use(role.RoleMiddleware([]string{"client"}))
clientRoutes.GET("/cycles", getCyclesHandler)
Erros¶
403 Forbidden- Usuário não tem role permitida- Mensagem: "Insufficient permissions"
Dependência¶
Requer: AuthMiddleware (deve ser aplicado antes)
api := e.Group("/api")
api.Use(authMiddleware.AuthMiddleware()) // Primeiro
api.Use(role.RoleMiddleware([]string{"admin"})) // Depois
ProfileApprovalMiddleware¶
Responsabilidade¶
Verifica se profissional tem perfil aprovado. Aplica apenas a rotas de profissionais que requerem aprovação.
Localização: internal/middleware/profile/profile.go
Funcionalidade¶
- Verifica se
user_type == "professional" - Se for admin, permite acesso (admin não precisa de perfil)
- Busca perfil profissional no banco
- Verifica se
status == "approved" - Se não estiver aprovado, retorna
403 Forbidden
Uso¶
approvedRoutes := api.Group("")
approvedRoutes.Use(role.RoleMiddleware([]string{"professional"}))
approvedRoutes.Use(profile.ProfileApprovalMiddleware())
approvedRoutes.GET("/patients", getPatientsHandler)
Erros¶
403 Forbidden- Profissional não tem perfil aprovado- Mensagem: "Professional profile not approved"
Dependências¶
Requer:
1. AuthMiddleware (deve ser aplicado antes)
2. RoleMiddleware com "professional" (deve ser aplicado antes)
Rotas que NÃO Requerem Aprovação¶
Algumas rotas de profissional não requerem aprovação (ex: criar/atualizar perfil):
// Sem ProfileApprovalMiddleware
api.GET("/profile", getProfileHandler)
api.POST("/profile", createProfileHandler)
RateLimitMiddleware¶
Responsabilidade¶
Limita taxa de requisições por IP ou usuário para prevenir abuso.
Localização: internal/middleware/ratelimit/ratelimit.go
Funcionalidade¶
- Identifica origem da requisição (IP ou user_id)
- Verifica limite de requisições no período
- Se exceder limite, retorna
429 Too Many Requests - Adiciona headers de rate limit:
X-RateLimit-Limit- Limite de requisiçõesX-RateLimit-Remaining- Requisições restantesX-RateLimit-Reset- Timestamp de reset
Uso¶
api := e.Group("/api")
api.Use(ratelimit.RateLimitMiddleware(100, time.Minute)) // 100 req/min
Configuração¶
Parâmetros:
- limit - Número máximo de requisições
- window - Janela de tempo (ex: time.Minute)
Exemplos:
// 100 requisições por minuto
api.Use(ratelimit.RateLimitMiddleware(100, time.Minute))
// 10 requisições por segundo
api.Use(ratelimit.RateLimitMiddleware(10, time.Second))
// 1000 requisições por hora
api.Use(ratelimit.RateLimitMiddleware(1000, time.Hour))
Erros¶
429 Too Many Requests- Limite excedido- Mensagem: "Rate limit exceeded"
Headers de Resposta¶
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1640995200
Criando Novos Middlewares¶
Estrutura Básica¶
package middleware
import (
"github.com/labstack/echo/v4"
)
func MyMiddleware() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Lógica do middleware
// Continuar para próximo middleware/handler
return next(c)
// OU interromper e retornar erro
// return echo.NewHTTPError(http.StatusForbidden, "Access denied")
}
}
}
Exemplo: Logging Middleware¶
func LoggingMiddleware() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
start := time.Now()
// Executar próximo handler
err := next(c)
// Log após execução
duration := time.Since(start)
log.Printf(
"%s %s %d %v",
c.Request().Method,
c.Request().URL.Path,
c.Response().Status,
duration,
)
return err
}
}
}
Exemplo: CORS Middleware¶
func CORSMiddleware() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
origin := c.Request().Header.Get("Origin")
// Verificar se origem é permitida
if isAllowedOrigin(origin) {
c.Response().Header().Set("Access-Control-Allow-Origin", origin)
c.Response().Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Response().Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
c.Response().Header().Set("Access-Control-Allow-Credentials", "true")
}
// Tratar OPTIONS (preflight)
if c.Request().Method == "OPTIONS" {
return c.NoContent(http.StatusNoContent)
}
return next(c)
}
}
}
Exemplo: Request ID Middleware¶
func RequestIDMiddleware() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Gerar ou usar ID existente
requestID := c.Request().Header.Get("X-Request-ID")
if requestID == "" {
requestID = uuid.New().String()
}
// Adicionar ao contexto
c.Set("request_id", requestID)
// Adicionar ao header de resposta
c.Response().Header().Set("X-Request-ID", requestID)
return next(c)
}
}
}
Ordem de Aplicação¶
Ordem Recomendada¶
api := e.Group("/api")
// 1. CORS (se necessário)
api.Use(corsMiddleware.CORSMiddleware())
// 2. Logging (opcional)
api.Use(loggingMiddleware.LoggingMiddleware())
// 3. Rate Limiting (opcional)
api.Use(ratelimit.RateLimitMiddleware(100, time.Minute))
// 4. Autenticação (obrigatório para rotas protegidas)
api.Use(authMiddleware.AuthMiddleware())
// 5. Role (se necessário)
api.Use(role.RoleMiddleware([]string{"admin"}))
// 6. Profile Approval (se necessário)
api.Use(profile.ProfileApprovalMiddleware())
// Handlers
api.GET("/users", getUsersHandler)
Exemplo Completo¶
func RegisterRoutes(e *echo.Echo) {
// Rotas públicas
e.GET("/health", HealthCheck)
authRoutes.RegisterRoutes(e)
// Rotas protegidas
api := e.Group("/api")
// Middlewares globais
api.Use(authMiddleware.AuthMiddleware())
// Rotas gerais (qualquer autenticado)
generalRoutes.RegisterRoutes(api)
// Rotas de admin
adminRoutes := api.Group("")
adminRoutes.Use(role.RoleMiddleware([]string{"admin"}))
adminRoutes.GET("/users", adminRoutes.GetUsers)
// Rotas de professional aprovado
approvedRoutes := api.Group("")
approvedRoutes.Use(role.RoleMiddleware([]string{"professional"}))
approvedRoutes.Use(profile.ProfileApprovalMiddleware())
approvedRoutes.GET("/patients", professionalRoutes.GetPatients)
}
Boas Práticas¶
1. Verificar Dependências¶
Sempre verificar se dados necessários existem no contexto:
func MyMiddleware() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
userID, ok := c.Get("user_id").(uint)
if !ok {
return echo.NewHTTPError(http.StatusUnauthorized, "User ID not found")
}
// Usar userID
return next(c)
}
}
}
2. Retornar Erros Apropriados¶
Use códigos HTTP corretos:
401 Unauthorized- Não autenticado403 Forbidden- Autenticado mas sem permissão429 Too Many Requests- Rate limit excedido
3. Adicionar Dados ao Contexto¶
Use c.Set() para adicionar dados que handlers podem usar:
c.Set("request_id", requestID)
c.Set("ip_address", ip)
4. Não Modificar Request Body¶
Evite ler o body da requisição no middleware, pois ele só pode ser lido uma vez:
// ❌ ERRADO
body, _ := io.ReadAll(c.Request().Body)
c.Request().Body = io.NopCloser(bytes.NewBuffer(body))
// ✅ CORRETO - Deixar handler ler o body
5. Performance¶
Middlewares são executados para cada requisição. Mantenha-os leves:
- Evite queries pesadas no middleware
- Use cache quando apropriado
- Evite operações síncronas lentas
Testes¶
Testando Middleware¶
func TestAuthMiddleware(t *testing.T) {
e := echo.New()
req := httptest.NewRequest(http.MethodGet, "/", nil)
req.Header.Set("Authorization", "Bearer valid-token")
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
// Executar middleware
handler := authMiddleware.AuthMiddleware()(func(c echo.Context) error {
userID := c.Get("user_id").(uint)
assert.Equal(t, uint(1), userID)
return c.String(http.StatusOK, "OK")
})
handler(c)
assert.Equal(t, http.StatusOK, rec.Code)
}