Alguma vez você já precisou validar se seu usuário tem a permissão “x” para acessar sua rota? Se sim, provavelmente você adicionou algumas linhas de código que fazem a verificação na função responsável pela rota, tudo isso antes de adicionar o código que realmente deve ser executado. E claro, copiou e colou esse código em outras rotas, ou talvez criou uma função que continha esse código, mas ainda assim, copiou e colou em outras rotas.
Esse tipo de verificação, ou outros scripts que precisam ser executados antes do código principal das rotas são comuns no dia a dia de um desenvolvedor, e com o passar do tempo, melhores maneiras de solucionar esse problema surgiram.
O que são Middlewares?
https://cdn-images-1.medium.com/max/768/1*PW4Sl8ZJQbj-SU7QWYQ3HQ.png
Middlewares são classes (ou função) que são executadas dentro da request/response no ciclo de vida de uma aplicação Django. Middlewaes são executados mais de duas vezes dentro do clico de vida de uma aplicação. Aqui está um resumo das principais fases em que os middlewares são executados:
Antes da Solicitação Chegar à View (Request Middleware):
- Antes de a solicitação chegar à view, os middlewares de solicitação (request middleware) são executados. Isso ocorre após o Django receber a solicitação HTTP do cliente, mas antes de chegar à view que irá processá-la.
- Exemplos de uso: autenticação, manipulação de cabeçalhos HTTP, controle de acesso, pré-processamento de dados da solicitação, entre outros.
Antes e Depois da View Ser Chamada:
- Após a execução dos middlewares de solicitação, a view correspondente é chamada para processar a solicitação. No entanto, você pode ter middlewares que são executados antes e depois da chamada da view.
- Exemplos de uso: pré-processamento e pós-processamento específicos antes e depois do processamento da view.
Antes da Resposta Ser Enviada ao Cliente (Response Middleware):
- Após a view processar a solicitação e gerar uma resposta, os middlewares de resposta (response middleware) são executados. Isso ocorre antes de a resposta ser enviada de volta ao cliente.
- Exemplos de uso: manipulação da resposta, adição de cabeçalhos HTTP à resposta, compressão de dados, pós-processamento de dados da resposta, entre outros.
Erros e Exceções:
- Além dessas fases, os middlewares também podem ser envolvidos no tratamento de erros e exceções. Existem middlewares específicos para lidar com erros HTTP, exceções não tratadas e outras situações de erro.
Cada request/response vai ser processada pelos middlewares na ordem configurada pelo projeto.
Com os Middlewares, tarefas como verificar a permissão de um usuário se torna simples, já que temos uma forma melhor de lidar com ações antes e/ou depois da chamada de uma rota, por exemplo.
Por padrão, o Django nos oferece uma boa variedade de Middlewares já instalados na nossa aplicação, como mecanismos de segurança contra ataques CSRF, XSS, entre outros. Você pode ver isso no arquivo settings.py do seu projeto, buscando por MIDDLEWARE.
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'
]
Por que devemos utilizar middlewares customizados?
Afinal, se já temos Middlewares prontos para configurar e usar, por que devemos nos dar o trabalho de criar um do zero? Como no caso da verificação de permissão que mencionei anteriormente, muitas aplicações podem requisitar implementações diferentes de coisas simples para que funcionem de acordo com a regra de negócio de uma empresa, e é aí que entram os Middlewares customizados.
Imagine que, por algum motivo, sempre que um usuário requisitar a rota “/usuarios-cadastrados-no-sistema”, eu precise verificar se ele tem permissão para acessar essa rota e, em seguida, precise enviar um e-mail para os administradores do sistema informando que alguém tentou acessar esse endpoint, e quem foi. Você deve ter pensado, “simples, basta ativar o Middleware de verificação de permissões do Django e, na minha rota, chamar a função para enviar o e-mail”. Vai funcionar, sim, mas imagine que você precise implementar isso para 10 outras rotas, ou melhor, que a regra de negócio mude e agora você também precise enviar um alerta no e-mail de usuários que não têm permissão para essa rota. Você vai editar arquivo por arquivo? Copiando e colando o novo código?
Acho que ficou claro que essa não é uma boa abordagem. Então, aí entram os middlewares customizados, quando precisamos de algo específico, ou que ainda não exista. Aqui estão algumas razões pelas quais você pode querer utilizá-los:
- Funcionalidade Personalizada: Com middlewares customizados, você pode adicionar funcionalidades específicas ao fluxo de processamento das requisições/respostas. Isso pode incluir autenticação personalizada, manipulação de cabeçalhos HTTP, controle de acesso, entre outras.
- Reutilização de Código: Ao encapsular lógicas comuns em middlewares, você pode reutilizar esse código em várias partes da sua aplicação. Isso promove a modularidade e evita a repetição de código.
- Separação de Responsabilidades: Utilizar middlewares ajuda a separar as responsabilidades dentro da sua aplicação. Por exemplo, você pode ter um middleware específico para lidar com autenticação e outro para manipular cache, mantendo cada componente focado em sua tarefa específica.
- Flexibilidade e Configuração: Os middlewares permitem configurar a ordem de execução das camadas de processamento de requisições/respostas. Isso oferece flexibilidade para ajustar o comportamento da aplicação de acordo com suas necessidades.
- Monitoramento e Logging: Com middlewares, é possível adicionar logging detalhado para monitorar o fluxo de requisições/respostas, o que pode ser valioso para debugar problemas e analisar o desempenho da aplicação.
- Tratamento de Exceções: Você pode criar middlewares para lidar com exceções de forma centralizada, proporcionando uma maneira consistente de tratar erros em toda a aplicação.
Mão no código – Criando middlewares customizadas:
Supondo que você já tenha seu projeto Django criado, na pasta do seu projeto crie um novo diretório “middlewares” para manter a organização. Dentro desse diretório, crie um arquivo que representará o seu middleware, por exemplo, printar_no_terminal_middleware.py.
Agora, abra esse arquivo e adicione o seguinte código:
class PrintarNoTerminalMiddleware():
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
print("Estou criando meu primeiro middleware")
response = self.get_response(request)
return response
Neste exemplo, temos uma classe simples que implementa os métodos __init__ e __call__, ambos extremamente importantes ao criar nossos próprios middlewares.
- __init__(self, get_response): É chamado uma única vez, quando a aplicação é iniciada. Esse método é utilizado para configurar e inicializar o middleware, bem parecido com um constructor. Nesse caso, apenas estamos pegando a response que será retornada para o usuário em caso de sucesso
- __call__(self, request): É o método que será chamado na request/response quando o middleware é ativado. Esse método retorna a response que recuperamos no __init__ em caso de sucesso, ou em caso de erros, podemos retornar um objeto da classe Response, por exemplo.
Para garantir que o Django reconheça o seu middleware personalizado, você deve registrar o caminho para ele no arquivo settings.py do seu projeto. Certifique-se de que o caminho para o seu middleware está correto e de que a ordem na qual você adiciona o middleware no array MIDDLEWARE é apropriada para o funcionamento correto da sua aplicação. Aqui está um exemplo revisado do código que você deve adicionar ao 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',
'meu_projeto.middlewares.printar_no_terminal_middleware.PrintarNoTerminalMiddleware'
]
Lembre-se de que a ordem dos middlewares é crucial, pois determina a sequência na qual eles serão executados pelo Django. Certifique-se de posicionar o seu middleware personalizado de acordo com a lógica desejada para o processamento das requisições/respostas da sua aplicação.