Como fazer: configurar a pilha de middleware corretamente
Recomendado: use setup_middlewares()
Em vez de alinhar chamadas add_middleware manualmente, use o helper fornecido pelo framework para evitar erros de ordenação LIFO (como um 500 não receber um X-Request-Id).
from nene2.middleware import setup_middlewares
setup_middlewares(
app,
debug=cfg.app_debug,
domain_handlers=[NoteNotFoundExceptionHandler()],
throttle_limit=cfg.throttle_limit if cfg.throttle_enabled else None,
max_request_bytes=cfg.max_body_size,
cors_allowed_origins=cfg.cors_origins if cfg.cors_enabled else None,
)Pilha resultante (externo → interno):
CORS → RequestId → SecurityHeaders → SizeLimit → Throttle → RequestLogging → ErrorHandlerUse o padrão de registro manual abaixo apenas quando precisar de middleware customizado.
Registro manual (TL;DR)
# chame add_middleware nesta ordem
app.add_middleware(ErrorHandlerMiddleware) # mais interno
app.add_middleware(RequestLoggingMiddleware)
app.add_middleware(ThrottleMiddleware, limit=100, window=60)
app.add_middleware(RequestSizeLimitMiddleware, max_bytes=1_048_576)
app.add_middleware(SecurityHeadersMiddleware)
app.add_middleware(RequestIdMiddleware) # mais externoPor que essa ordem
Regra LIFO do Starlette
app.add_middleware() faz o último adicionado ser o mais externo (LIFO).
add_middleware(A) → B(A(Router))
add_middleware(B)A requisição flui de externo → interno (B → A → Router), e a resposta flui de interno → externo (Router → A → B).
O que quebra se ErrorHandler for o mais externo
# ❌ errado
app.add_middleware(RequestIdMiddleware)
app.add_middleware(ErrorHandlerMiddleware) # mais externo
# pilha: ErrorHandler(RequestId(Router))Quando um handler lança uma exceção:
ErrorHandlerMiddleware.dispatchcaptura a exceção- retorna uma nova Response diretamente via
problem_details_response(...) - essa Response não passa pelo
RequestIdmiddleware interno - resultado: o erro 500 não tem
X-Request-Id
Pela mesma razão, se SecurityHeadersMiddleware for interno, as respostas de erro não receberão headers de segurança.
O diagrama correto da pilha
RequestIdMiddleware ← adiciona X-Request-Id a toda resposta (200–5xx)
└─ SecurityHeadersMiddleware ← adiciona headers de segurança a toda resposta
└─ RequestSizeLimitMiddleware ← retorna 413 diretamente (sem ErrorHandler necessário)
└─ ThrottleMiddleware ← retorna 429 diretamente (sem ErrorHandler necessário)
└─ RequestLoggingMiddleware
└─ ErrorHandlerMiddleware ← converte exceções do handler em 500
└─ Router (handlers FastAPI)RequestSizeLimitMiddleware e ThrottleMiddleware retornam problem_details_response() eles mesmos, então colocá-los dentro ou fora do ErrorHandler não muda o formato 413/429. Se X-Request-Id é anexado depende da posição do RequestIdMiddleware.
Quando algum middleware não é usado
Mesmo se você omitir algum middleware, o restante segue a mesma regra de ordenação:
# quando ThrottleMiddleware e RequestLoggingMiddleware são omitidos
app.add_middleware(ErrorHandlerMiddleware)
app.add_middleware(RequestSizeLimitMiddleware, max_bytes=1_048_576)
app.add_middleware(SecurityHeadersMiddleware)
app.add_middleware(RequestIdMiddleware)Usando ErrorHandlerMiddleware.install()
install() faz add_middleware e add_exception_handler juntos, mas você ainda precisa definir a ordem em relação a outros middlewares manualmente:
# chame install() primeiro (ele se torna o mais interno)
ErrorHandlerMiddleware.install(app) # interno
# então adicione os outros middlewares
app.add_middleware(RequestSizeLimitMiddleware, max_bytes=1_048_576)
app.add_middleware(SecurityHeadersMiddleware)
app.add_middleware(RequestIdMiddleware) # externoFAQ
Q: RequestSizeLimitMiddleware deve ficar dentro ou fora do ErrorHandler?
A: Funciona de ambas as formas, mas colocá-lo dentro do RequestIdMiddleware significa que a resposta 413 também recebe um X-Request-Id. Basta seguir a ordem recomendada acima.
Q: Onde adiciono middleware customizado?
A: Depende da natureza do middleware:
- quer adicionar algo a toda resposta → logo antes de
RequestIdMiddleware(externo) - quer capturar exceções do handler → logo após
ErrorHandlerMiddleware(interno) - quer rejeitar requisições cedo → perto de
RequestSizeLimitMiddlewareouThrottleMiddleware