Middleware Avançado no Django 2026: Middleware Personalizado, Logging e Perguntas de Entrevista

Guia completo sobre middleware avançado no Django 2026. Criação de middleware personalizado, logging estruturado, tratamento de erros e perguntas de entrevista técnica para desenvolvedores Python.

Middleware Avançado no Django 2026: Middleware Personalizado, Logging e Perguntas de Entrevista

O sistema de middleware representa uma das arquiteturas mais poderosas do Django, permitindo interceptar e processar cada requisição e resposta HTTP que atravessa a aplicação. Em 2026, com o Django 6.0 e as melhorias no sistema assíncrono, dominar os middlewares torna-se essencial para qualquer desenvolvedor backend sério. Este artigo explora a criação de middleware personalizado, estratégias avançadas de logging e as perguntas mais frequentes em entrevistas técnicas.

O middleware funciona como um pipeline de processamento

Cada middleware do Django funciona como uma camada em uma cebola: as requisições atravessam os middlewares de fora para dentro (de cima para baixo em MIDDLEWARE), enquanto as respostas seguem o caminho inverso. Essa arquitetura permite adicionar funcionalidades transversais como autenticação, logging ou gerenciamento de cache sem modificar o código das views.

Arquitetura de Middleware no Django: Funcionamento Interno

O sistema de middleware do Django baseia-se no padrão de cadeia de responsabilidade. Cada middleware recebe uma requisição de entrada ou uma resposta de saída, e pode escolher processá-la, modificá-la ou passá-la para o próximo middleware. A configuração é feita no arquivo settings.py através da lista MIDDLEWARE.

python
# settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    # Middlewares personalizados
    'myapp.middleware.RequestLoggingMiddleware',
    'myapp.middleware.RateLimitMiddleware',
]

A ordem dos middlewares é crucial. SecurityMiddleware deve estar primeiro para adicionar os cabeçalhos de segurança, enquanto SessionMiddleware deve preceder AuthenticationMiddleware porque a autenticação depende das sessões. Um posicionamento incorreto pode causar comportamentos imprevisíveis ou vulnerabilidades de segurança.

Criação de Middleware Personalizado: Sintaxe Moderna

O Django suporta dois estilos de middleware: a sintaxe baseada em classes (recomendada desde o Django 2.0) e a antiga sintaxe baseada em hooks. A sintaxe moderna utiliza uma classe callable com um método call que encapsula o processamento.

python
# myapp/middleware.py
import time
import logging

logger = logging.getLogger(__name__)

class RequestTimingMiddleware:
    """Middleware que mede o tempo de processamento de cada requisição."""
    
    def __init__(self, get_response):
        self.get_response = get_response
        # Configuração única na inicialização do servidor
        
    def __call__(self, request):
        # Código executado ANTES da view (requisição de entrada)
        start_time = time.perf_counter()
        
        # Chamada ao próximo middleware ou à view
        response = self.get_response(request)
        
        # Código executado APÓS a view (resposta de saída)
        duration = time.perf_counter() - start_time
        response['X-Request-Duration'] = f'{duration:.4f}s'
        
        logger.info(
            'Request processed',
            extra={
                'path': request.path,
                'method': request.method,
                'duration_ms': duration * 1000,
                'status_code': response.status_code,
            }
        )
        
        return response

Essa estrutura garante que o código antes de get_response() seja executado durante a fase de requisição, enquanto o código depois é executado durante a fase de resposta. O parâmetro get_response representa o próximo elemento na cadeia, seja outro middleware ou a view final.

Middleware Assíncrono com Django 6.0

O Django 6.0 reforça o suporte nativo a middlewares assíncronos, permitindo operações não bloqueantes como chamadas a serviços externos ou consultas de banco de dados assíncronas.

python
# myapp/middleware.py
import asyncio
from asgiref.sync import iscoroutinefunction

class AsyncAuthorizationMiddleware:
    """Middleware assíncrono que verifica permissões externas."""
    
    async_capable = True
    sync_capable = False
    
    def __init__(self, get_response):
        self.get_response = get_response
        if iscoroutinefunction(self.get_response):
            self._is_coroutine = asyncio.coroutines._is_coroutine
    
    async def __call__(self, request):
        # Verificação assíncrona de permissões
        if hasattr(request, 'user') and request.user.is_authenticated:
            permissions = await self.fetch_external_permissions(
                request.user.id
            )
            request.user_permissions = permissions
        
        response = await self.get_response(request)
        return response
    
    async def fetch_external_permissions(self, user_id: int) -> list:
        """Chamada assíncrona a um serviço de permissões externo."""
        # Simulação de uma chamada API externa
        await asyncio.sleep(0.01)  # Substituir por httpx ou aiohttp
        return ['read', 'write', 'delete']

Os atributos async_capable e sync_capable indicam ao Django os modos suportados pelo middleware. Um middleware marcado com sync_capable=False só será utilizado em um contexto ASGI com um servidor como Uvicorn ou Daphne.

Logging Estruturado com structlog e JSON

O logging tradicional do Django produz mensagens de texto difíceis de parsear pelos sistemas de monitoramento modernos. A adoção do logging estruturado em JSON facilita a integração com ferramentas como Elasticsearch, Datadog ou Grafana Loki.

python
# settings.py
import structlog

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'json': {
            '()': structlog.stdlib.ProcessorFormatter,
            'processor': structlog.processors.JSONRenderer(),
        },
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'json',
        },
    },
    'loggers': {
        '': {
            'handlers': ['console'],
            'level': 'INFO',
        },
        'django.request': {
            'handlers': ['console'],
            'level': 'INFO',
            'propagate': False,
        },
    },
}

structlog.configure(
    processors=[
        structlog.stdlib.filter_by_level,
        structlog.stdlib.add_logger_name,
        structlog.stdlib.add_log_level,
        structlog.processors.TimeStamper(fmt='iso'),
        structlog.processors.StackInfoRenderer(),
        structlog.processors.format_exc_info,
        structlog.processors.UnicodeDecoder(),
        structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
    ],
    context_class=dict,
    logger_factory=structlog.stdlib.LoggerFactory(),
    cache_logger_on_first_use=True,
)

Essa configuração produz logs JSON que contêm automaticamente o timestamp, o nível de log e os metadados estruturados, facilitando as consultas e alertas nas plataformas de monitoramento.

Middleware de Logging de Requisições com Correlation ID

Para rastrear uma requisição através de múltiplos serviços em uma arquitetura de microsserviços, o uso de um correlation ID (ou request ID) é indispensável. Este middleware gera ou propaga um identificador único para cada requisição.

python
# myapp/middleware.py
import uuid
import structlog
from django.http import HttpRequest, HttpResponse

logger = structlog.get_logger(__name__)

class CorrelationIdMiddleware:
    """Adiciona um correlation ID a cada requisição para rastreabilidade distribuída."""
    
    HEADER_NAME = 'X-Correlation-ID'
    
    def __init__(self, get_response):
        self.get_response = get_response
    
    def __call__(self, request: HttpRequest) -> HttpResponse:
        # Recuperar ou gerar o correlation ID
        correlation_id = request.headers.get(
            self.HEADER_NAME,
            str(uuid.uuid4())
        )
        
        # Anexar à requisição para uso nas views
        request.correlation_id = correlation_id
        
        # Configurar structlog com o contexto
        structlog.contextvars.clear_contextvars()
        structlog.contextvars.bind_contextvars(
            correlation_id=correlation_id,
            path=request.path,
            method=request.method,
        )
        
        logger.info('request_started')
        
        response = self.get_response(request)
        
        # Propagar o correlation ID na resposta
        response[self.HEADER_NAME] = correlation_id
        
        logger.info(
            'request_completed',
            status_code=response.status_code,
        )
        
        return response

Esse padrão permite correlacionar os logs entre um frontend, uma API Django, workers do Celery e serviços externos, facilitando consideravelmente o debugging em produção.

Middleware de Rate Limiting com Redis

A limitação de taxa (rate limiting) protege a API contra abusos e ataques de negação de serviço. Esta implementação utiliza o Redis para uma contagem distribuída compatível com deploys multi-instância.

python
# myapp/middleware.py
import redis
from django.http import JsonResponse
from django.conf import settings

class RateLimitMiddleware:
    """Limita o número de requisições por IP ou usuário."""
    
    def __init__(self, get_response):
        self.get_response = get_response
        self.redis = redis.Redis.from_url(settings.REDIS_URL)
        self.rate_limit = 100  # Requisições por janela
        self.window_seconds = 60  # Janela deslizante
    
    def __call__(self, request):
        client_id = self.get_client_identifier(request)
        key = f'ratelimit:{client_id}'
        
        # Incrementar o contador com expiração
        pipe = self.redis.pipeline()
        pipe.incr(key)
        pipe.expire(key, self.window_seconds)
        count, _ = pipe.execute()
        
        if count > self.rate_limit:
            return JsonResponse(
                {
                    'error': 'Rate limit exceeded',
                    'retry_after': self.window_seconds,
                },
                status=429,
            )
        
        response = self.get_response(request)
        
        # Adicionar os cabeçalhos de rate limiting
        response['X-RateLimit-Limit'] = str(self.rate_limit)
        response['X-RateLimit-Remaining'] = str(
            max(0, self.rate_limit - count)
        )
        
        return response
    
    def get_client_identifier(self, request) -> str:
        """Identifica o cliente por usuário autenticado ou IP."""
        if hasattr(request, 'user') and request.user.is_authenticated:
            return f'user:{request.user.id}'
        
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            return f'ip:{x_forwarded_for.split(",")[0].strip()}'
        
        return f'ip:{request.META.get("REMOTE_ADDR")}'

Os cabeçalhos X-RateLimit-* informam aos clientes sobre sua cota restante, permitindo que as aplicações consumidoras adaptem seu comportamento antes de atingir o limite.

Tratamento de Exceções com Middleware

Um middleware de tratamento centralizado de exceções captura os erros não tratados, os registra com contexto e retorna respostas consistentes aos clientes.

python
# myapp/middleware.py
import traceback
import structlog
from django.http import JsonResponse

logger = structlog.get_logger(__name__)

class ExceptionHandlingMiddleware:
    """Captura e registra todas as exceções não tratadas."""
    
    def __init__(self, get_response):
        self.get_response = get_response
    
    def __call__(self, request):
        return self.get_response(request)
    
    def process_exception(self, request, exception):
        """Hook chamado quando uma view lança uma exceção."""
        logger.error(
            'unhandled_exception',
            exception_type=type(exception).__name__,
            exception_message=str(exception),
            path=request.path,
            method=request.method,
            user_id=getattr(request.user, 'id', None),
            traceback=traceback.format_exc(),
        )
        
        # Resposta genérica em produção
        return JsonResponse(
            {
                'error': 'Internal server error',
                'correlation_id': getattr(
                    request, 'correlation_id', 'unknown'
                ),
            },
            status=500,
        )

O método process_exception é um hook especial chamado apenas quando uma view lança uma exceção. Ele permite capturar o erro antes que suba pela pilha de middlewares.

Pronto para mandar bem nas entrevistas de Django?

Pratique com nossos simuladores interativos, flashcards e testes tecnicos.

Perguntas de Entrevista: Middleware Django

As entrevistas técnicas para posições Django frequentemente abordam os middlewares. Estas são as perguntas mais comuns com suas respostas detalhadas.

Pergunta: Qual é a diferença entre call e process_view em um middleware?

O método call é o ponto de entrada principal que encapsula todo o ciclo requisição-resposta. Ele é executado para cada requisição. O método process_view é um hook opcional chamado logo antes da execução da view, após a resolução da URL mas antes de chamar a função da view. Ele recebe a view, seus argumentos posicionais e seus argumentos nomeados, permitindo uma inspeção ou modificação detalhada.

Pergunta: Como um middleware pode curto-circuitar a cadeia de processamento?

Retornando diretamente um objeto HttpResponse a partir de call em vez de chamar self.get_response(request), o middleware impede que os próximos middlewares e a view sejam executados. Essa técnica é usada para autenticação, rate limiting ou validação.

python
def __call__(self, request):
    if not self.is_authorized(request):
        return HttpResponse('Unauthorized', status=401)
    return self.get_response(request)

Pergunta: Como lidar com middlewares com dependências circulares?

As dependências circulares entre middlewares devem ser evitadas por design. Se um middleware A depende de dados adicionados por B, e B depende de dados de A, deve-se fundir os middlewares ou criar um middleware intermediário que consolide as dependências. A ordem em MIDDLEWARE sempre deve formar um grafo acíclico.

Pergunta: Quando usar um middleware vs um decorator de view?

Os middlewares se aplicam globalmente a todas as requisições e são adequados para funcionalidades transversais (logging, segurança, sessões). Os decorators se aplicam seletivamente a views específicas e são adequados para validações locais (permissões específicas, cache condicional). Um middleware que verifica uma condição e ignora a maioria das requisições provavelmente deveria ser um decorator.

Pergunta: Como testar um middleware de forma isolada?

Os middlewares são testados criando uma instância com um get_response mockado, depois chamando o middleware com objetos RequestFactory.

python
from django.test import TestCase, RequestFactory
from myapp.middleware import RequestTimingMiddleware

class TestTimingMiddleware(TestCase):
    def setUp(self):
        self.factory = RequestFactory()
        self.get_response = lambda r: HttpResponse('OK')
        self.middleware = RequestTimingMiddleware(self.get_response)
    
    def test_adds_duration_header(self):
        request = self.factory.get('/test/')
        response = self.middleware(request)
        self.assertIn('X-Request-Duration', response)

Boas Práticas e Padrões Avançados

O design de middlewares performantes e manuteníveis segue vários princípios estabelecidos. Cada middleware deve ter uma responsabilidade única e bem definida. Um middleware que lida tanto com autenticação quanto com logging deveria ser dividido em dois componentes distintos.

As operações custosas (consultas de banco de dados, chamadas de rede) devem ser minimizadas ou tornadas assíncronas. Um middleware síncrono que faz chamadas HTTP bloqueantes degradará significativamente a performance sob carga.

A configuração deve ser externalizada nos settings do Django em vez de ser hardcoded. Isso facilita o deploy em diferentes ambientes sem modificação do código.

python
class ConfigurableMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        self.enabled = getattr(settings, 'MY_MIDDLEWARE_ENABLED', True)
        self.threshold = getattr(settings, 'MY_MIDDLEWARE_THRESHOLD', 100)

Conclusão

Dominar os middlewares do Django abre caminho para arquiteturas de aplicação robustas e observáveis. A criação de middlewares personalizados para logging estruturado, rate limiting e tratamento de erros constitui uma habilidade fundamental para desenvolvedores Python que trabalham em aplicações de produção. Os conceitos abordados, desde o correlation ID até o logging JSON passando pelo rate limiting distribuído, representam o estado da arte das práticas Django em 2026. Esses conhecimentos, combinados com a compreensão profunda do ciclo requisição-resposta, preparam efetivamente para as entrevistas técnicas de posições backend exigentes.

Desafio do dia

Você saberia encontrar o bug em Django?

Um trecho real, um bug escondido, uma tentativa por dia. Sem conta para testar.

Anthony Fillion-Maillet

Escrito por

Anthony Fillion-Maillet

Fundador da SharpSkill

Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.

Atualizado em 18 de setembro de 2026

Compartilhar

Artigos relacionados