404 Not Found

404 Not Found


nginx

Middleware — Interceptando e Aprimorando Requisições e Respostas

Middleware é como o posto de controle de segurança do aeroporto—cada passageiro (requisição) deve passar pela alfândega, segurança e portão de embarque em sequência, e em qualquer uma dessas paradas, o passageiro pode ser autorizado a prosseguir ou parado, enquanto a viagem de volta (resposta) segue a ordem inversa.

1. O Que Você Vai Aprender


2. A História Real da Alice

(1) Problema: A API sofre picos de tráfego malicioso e não pode ser rastreada

Depois que Alice lançou sua API PriceTracker, ela descobriu que um determinado endereço IP estava enviando 5.000 requisições por minuto, fazendo com que a carga do banco de dados disparasse. Para piorar, quando o front end do Bob chamou a API de localhost:3000, as requisições foram bloqueadas pela política CORS do navegador, fazendo com que todas falhassem. Alice precisava resolver simultaneamente os problemas de acesso cross-origin e limitação de taxa de requisição, mas o Flask não possui um mecanismo de middleware unificado.

(2) Soluções com Middleware do FastAPI

O FastAPI/Starlette fornece middleware estilo cebola, com cada camada resolvendo um problema específico: o middleware CORS lida com requisições cross-origin, o middleware de limitação de taxa controla a frequência de requisições, e o middleware de log registra requisições—eles não interferem uns nos outros e entram em vigor imediatamente após o registro.

PYTHON
from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware

app = FastAPI()
app.add_middleware(CORSMiddleware, allow_origins=["http://localhost:3000"])

(3) Resultado

Bob resolveu o problema cross-domain do front end com apenas 5 linhas de código. O middleware de limitação de taxa reduziu as requisições de IPs maliciosos de 5.000 para 1.000 por minuto, e o middleware de log atribuiu um ID único a cada requisição para fácil rastreamento.


3. O Modelo Cebola do Middleware

(1) Princípio do Modelo Cebola

100%
flowchart TD
    Request[Client Request] --> CORS[CORS Middleware]
    CORS --> Logging[Logging Middleware]
    Logging --> RateLimit[Rate Limit Middleware]
    RateLimit --> App[FastAPI Application]
    App --> RateLimit2[Rate Limit Response]
    RateLimit2 --> Logging2[Logging Response]
    Logging2 --> CORS2[CORS Response]
    CORS2 --> Response[Client Response]
    
    RateLimit -.->|429 Too Many Requests| Reject[Short-circuit Rejection]
    Reject --> Logging2
Característica Descrição
Direção da Requisição De fora para dentro (a camada mais externa registrada primeiro)
Direção da Resposta De dentro para fora (oposto da requisição)
Capacidade de Short-circuit Qualquer middleware pode retornar uma resposta antecipadamente, sem prosseguir para a próxima camada
Ordem de Registro O último middleware registrado é o primeiro a processar a requisição

(1) ▶ Exemplo: Ordem de Registro e Ordem de Execução do Middleware

PYTHON
from fastapi import FastAPI, Request
import time

app = FastAPI()

# Middleware registrado PRIMEIRO = camada mais externa (executa primeiro na requisição)
@app.middleware("http")
async def outer_middleware(request: Request, call_next):
    print("Outer: before request")
    response = await call_next(request)
    print("Outer: after response")
    return response

# Middleware registrado POR ÚLTIMO = camada mais interna (executa por último na requisição)
@app.middleware("http")
async def inner_middleware(request: Request, call_next):
    print("Inner: before request")
    response = await call_next(request)
    print("Inner: after response")
    return response

@app.get("/test")
async def test():
    print("Handler: processing")
    return {"ok": True}

Saída:

TEXT
Outer: before request
Inner: before request
Handler: processing
Inner: after response
Outer: after response

4. CORS Middleware

(1) Configuração de Compartilhamento de Recursos Cross-Domain

CORS (Cross-Origin Resource Sharing) é uma política de segurança do navegador que restringe páginas web de origens diferentes de acessar APIs. Quando o front end do Bob (localhost:3000) acessa a API da Alice (localhost:8000), isso constitui uma requisição cross-origin.

(1) ▶ Exemplo: Configuração CORS para o Ambiente de Desenvolvimento

PYTHON
from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],  # Front end do Bob
    allow_credentials=True,
    allow_methods=["*"],  # Todos os métodos HTTP
    allow_headers=["*"],  # Todos os cabeçalhos
)

Saída:

TEXT
# Execução Bem-sucedida

(2) ▶ Exemplo: Configuração CORS para um Ambiente de Produção

PYTHON
from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware

app = FastAPI()

ALLOWED_ORIGINS = [
    "https://pricetracker.example.com",
    "https://admin.pricetracker.example.com",
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=ALLOWED_ORIGINS,
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "DELETE"],
    allow_headers=["Authorization", "Content-Type"],
)

Saída:

TEXT
# Execução Bem-sucedida
Configuração CORS Ambiente de Desenvolvimento Ambiente de Produção
allow_origins ["*"] ou localhost Lista de nomes de domínio específicos
allow_credentials True True (se cookies são necessários)
allow_methods ["*"] Métodos Necessários
allow_headers ["*"] Apenas os cabeçalhos necessários
max_age Padrão 3600 (cache de pré-verificação)

5. Middleware Personalizado

(1) Middleware de Cronometragem de Requisição

(1) ▶ Exemplo: Registrando o tempo de processamento de cada requisição

PYTHON
from fastapi import FastAPI, Request
import time

app = FastAPI()

@app.middleware("http")
async def timing_middleware(request: Request, call_next):
    start_time = time.perf_counter()
    response = await call_next(request)
    process_time = time.perf_counter() - start_time
    response.headers["X-Process-Time"] = f"{process_time:.4f}s"
    return response

@app.get("/products")
async def list_products():
    return [{"id": 1, "name": "Widget"}]

Saída:

TEXT
# Função definida com sucesso

(2) ▶ Exemplo: Middleware de Injeção de ID de Requisição

PYTHON
import uuid
from fastapi import FastAPI, Request

app = FastAPI()

@app.middleware("http")
async def request_id_middleware(request: Request, call_next):
    request_id = str(uuid.uuid4())
    request.state.request_id = request_id  # Anexar ao estado da requisição
    response = await call_next(request)
    response.headers["X-Request-ID"] = request_id
    return response

@app.get("/health")
async def health_check(request: Request):
    return {
        "status": "healthy",
        "request_id": request.state.request_id,
    }

Saída:

TEXT
# Função definida com sucesso

(3) ▶ Exemplo: Middleware de Limitação de Taxa da API (Versão Simplificada)

PYTHON
from fastapi import FastAPI, Request, HTTPException
from collections import defaultdict
import time

app = FastAPI()

# Limitador de taxa simples em memória
rate_limits: dict[str, list[float]] = defaultdict(list)
RATE_LIMIT = 1000  # requisições por minuto
WINDOW = 60  # segundos

@app.middleware("http")
async def rate_limit_middleware(request: Request, call_next):
    client_ip = request.client.host if request.client else "unknown"
    now = time.time()
    
    # Limpar entradas antigas
    rate_limits[client_ip] = [
        t for t in rate_limits[client_ip] if now - t < WINDOW
    ]
    
    # Verificar limite
    if len(rate_limits[client_ip]) >= RATE_LIMIT:
        raise HTTPException(
            status_code=429,
            detail=f"Rate limit exceeded: {RATE_LIMIT} requests per {WINDOW}s",
        )
    
    rate_limits[client_ip].append(now)
    response = await call_next(request)
    response.headers["X-RateLimit-Limit"] = str(RATE_LIMIT)
    response.headers["X-RateLimit-Remaining"] = str(
        RATE_LIMIT - len(rate_limits[client_ip])
    )
    return response

Saída:

TEXT
# Função definida com sucesso

6. Middleware Baseado em Classe e ASGI Callables

(1) Como Escrever Middleware Baseado em Classe

Para lógica de middleware mais complexa, recomendamos usar uma abordagem baseada em classe que manipula diretamente o scope, receive e send do ASGI.

(1) ▶ Exemplo: Middleware de Log de Requisição Baseado em Classe

PYTHON
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response
import logging
import time

logger = logging.getLogger("pricetracker")

class LoggingMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: Request, call_next) -> Response:
        start = time.perf_counter()
        logger.info(f"Request: {request.method} {request.url.path}")
        
        response = await call_next(request)
        
        duration = time.perf_counter() - start
        logger.info(
            f"Response: {request.method} {request.url.path} "
            f"status={response.status_code} duration={duration:.4f}s"
        )
        return response

# Registrar middleware baseado em classe
app = FastAPI()
app.add_middleware(LoggingMiddleware)

Saída:

TEXT
# Função definida com sucesso

(2) Comparação de Tipos de Middleware

Abordagem Cenários Aplicáveis Vantagens Desvantagens
@app.middleware("http") Interceptação simples Código mínimo Lida apenas com HTTP
BaseHTTPMiddleware Complexidade moderada Configurável Processa apenas HTTP
Classe ASGI pura Controle total Também lida com WebSockets Código complexo

❓ Perguntas Frequentes

P Por que a ordem de registro do middleware é importante?
R O middleware registrado por último é o primeiro a processar requisições (camada mais interna). CORS deve ser registrado por último (camada mais externa) para garantir que todas as respostas incluam cabeçalhos CORS. Limitação de taxa deve ser registrada antes (camadas internas) para que requisições rejeitadas ainda passem pelo CORS.
P @app.middleware e add_middleware podem ser usados juntos?
R Sim, mas o middleware registrado com @app.middleware vem antes de add_middleware (em um nível superior). É recomendado usar uma abordagem consistente.
P Qual solução deve ser usada para limitação de taxa em produção?
R Limitação de taxa simples em memória é adequada apenas para ambientes de processo único. Em produção, use Redis + SlowAPI ou um middleware de limitação de taxa personalizado com Redis, que suporta ambientes distribuídos e multi-processo.
P Posso usar * para a configuração allow_origins no CORS?
R É aceitável em um ambiente de desenvolvimento, mas em um ambiente de produção, você deve especificar um domínio específico. Você não pode usar allow_credentials=True quando * é especificado; o navegador rejeitará.
P O middleware pode modificar o corpo da requisição?
R Sim, mas deve primeiro ler o corpo e então reconstruí-lo. Há um problema conhecido com BaseHTTPMiddleware (esgotamento de stream após ler o corpo); para cenários complexos, recomendamos usar middleware ASGI puro.
P Como desabilitar um middleware específico?
R Não há um toggle embutido. Recomendamos usar variável de ambiente: if settings.ENABLE_RATE_LIMIT: app.add_middleware(RateLimitMiddleware).

📖 Resumo


📝 Exercícios

  1. Problema Básico (Dificuldade ⭐): Adicione um middleware CORS ao PriceTracker para permitir acesso cross-origin de http://localhost:3000, e verifique em um navegador que o front end do Bob pode chamar a API. Dica: app.add_middleware(CORSMiddleware, ...)
  2. Exercício Avançado (Dificuldade: ⭐⭐): Implemente um middleware de cronometragem de requisição que adicione X-Process-Time aos cabeçalhos de resposta, e use o Swagger UI para visualizar os cabeçalhos de resposta. Dica: @app.middleware("http") + time.perf_counter()
  3. Desafio (Dificuldade: ⭐⭐⭐): Implemente middleware de limitação de taxa baseado em IP (1.000 requisições por minuto). Quando o limite for excedido, retorne um status 429 e o cabeçalho de resposta Retry-After, enquanto exibe a cota restante no cabeçalho de resposta. Dica: defaultdict(list) + limpeza de janela de tempo

---|

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%