Você sabe o que é 'middleware'?
Tags: laravel, middleware, php, beginners
Published: 2022-07-05 16:10:00

## Apresentação

Antes de falar de middleware é importante compreender o conceito de "pipeline", que em tradução livre significa "encanamento".

Em desenvolvimento de software, pipeline representa o caminho percorrido pela informação.

Para ajudar na compreensão, vamos imaginar uma avenida com tráfego intenso. A avenida possui cruzamentos, rotatórias, retornos e desvios que levam a diversos destinos.

Assim que o veículo entra nessa avenida ele pode percorrê-la até o final baseado na sinalização.

Nosso veículo então continua seu caminho até encontrar uma sinalização indicando que apenas veículos com 2 ou mais ocupantes podem seguir. Caso contrário será necessário voltar ao início da avenida.

O cenário que foi mostrado traz os componentes necessários para compreensão do artigo.

Por analogia, temos:
- avenida -> pipeline
- veículos -> dado (ou informação)
- sinalização -> middleware

---


> Mas afinal o que é um middleware?

A palavra middleware foi utilizada pela primeira vez em uma convenção de engenharia de software organizada pela OTAN em Outubro de 1968, na Alemanha. 

Conforme observado por Alexander d’Agapeyeff "_...não importa o quão bom seja um software de controle de arquivos (por exemplo) ... ainda assim será inapropriado ou ineficiente..._". 

Outro ponto que merece destaque é o fato de que "_...versões novas do mesmo software geralmente não tem como base a versão anterior..._".

Essa premissas trouxeram à tona a necessidade de desenvolver uma rotina que servisse de ponte entre o software dos clientes e o software principal.

Podemos ver na pirâmide invertida de d’Agapeyeff que o middleware foi posicionado justamente entre esses extremos, fazendo o papel de "cola".


![Pirâmide invertida](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3pq6rbc8908z81aexzz5.png)


Outro cenário comum no uso de middleware está no acesso a instituições financeiras. 

A tarefa de autorizar o acesso para quem está se conectando pode ser delegada a um middleware. 

Se as credenciais forem válidas o middleware encaminha o usuário para o software principal onde ele poderá realizar quaisquer operações disponíveis para ele.

E se as credenciais não forem válidas, o usuário é redirecionado para a página de login.

Este cenário é particularmente interessante pois mostra a versatilidade do middleware. 

Caso a instituição financeira decida reforçar a segurança, ela pode simplesmente modificar as regras contidas no middleware sem afetar a aplicação principal.

---


## Ferramentas necessárias

[PHP 8](https://www.php.net/downloads.php) ou superior
[MySQL Server](https://www.mysql.com/downloads/) versão 5.7 ou superior
[Composer](https://getcomposer.org/) versão 2.0 ou superior
Editor de textos

Existem instaladores que trazem embutidos o PHP, MySQL e Apache (servidor web), como [WAMPSERVER](https://www.wampserver.com/), [XAMPP](https://sourceforge.net/projects/xampp/) e [Laragon](https://laragon.org/). Deixo a seu critério a escolha de alguns desses pacotes, ou outro que seja familiar a você.

Para o editor de textos estou utilizando o [Visual Studio Code](https://code.visualstudio.com/download). Além de ser gratuito possibilita a instalação de diversas extensões.

---

## Banco de dados

Se tudo estiver configurado corretamente conseguiremos acessar o banco de dados.

Utilizando o terminal, digite a seguinte instrução:

```
mysql -u root -p
```

Basta informar a senha configurada na instalação do MySQL para obtermos acesso ao servidor.

Saberemos que estamos conectados porque a linha de comando do terminal agora virá precedida de `mysql>`.

A partir deste ponto, todas as instruções digitadas no terminal do servidor MySQL devem terminar com ponto-e-vírgula (;).

Em primeiro lugar precisamos criar um database exclusivo para nosso projeto.

No terminal MySQL vamos digitar a instrução a seguir:

```sql
create database middleware;
```

Em seguida nos conectamos ao database criado, digitando o seguinte:

```sql
use middleware;
```

Em segundo lugar criaremos uma tabela para armazenar alguns registros.

Dentro do terminal do MySQL digitaremos a seguinte instrução:

```sql
create table invoices (id smallint not null auto_increment, issuer_date date not null, customer varchar(100) not null, value double default 0, primary key(id));

```

Se não houver nenhum erro, teremos uma tabela com esta estrutura:

![Describe invoices table](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ar41gfrdrqupnqnio68o.jpg)

Agora que nossa tabela foi criada vamos inserir alguns registros.

```sql
insert into invoices (issuer_date, customer, value) values ('2021-11-3', 'George', 1651.68);
insert into invoices (issuer_date, customer, value) values ('2021-7-29', 'Alfred', 834.01);
insert into invoices (issuer_date, customer, value) values ('2022-4-13', 'Lewis', 146.17);
```

Consultando os dados da tabela invoices obteremos este resultado:


![Select invoices table](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hza306y482xeldlo2lis.jpg)

---

## Projeto

Nosso projeto será uma aplicação [Laravel](https://laravel.com/), conhecido framework PHP para desenvolvimento de aplicações web e que no momento em que escrevo este artigo se encontra na versão 9.

A documentação oficial oferece um exemplo de middleware que testa um token passado no corpo da requisição e embora seja um exemplo bem didático, vamos fazer diferente: testaremos se o banco de dados está disponível antes de fazer consultas.

Para isso vamos acessar o terminal e digitar a seguinte instrução:

```
composer create-project --prefer-dist laravel/laravel middleware
```

O tempo para conclusão desta etapa pode variar conforme a velocidade de conexão à internet e a configuração do computador

---

> Middleware

Ainda dentro do terminal, vamos acessar a pasta do projeto e criar a classe middleware com a seguinte instrução:

```
php artisan make:middleware EnsureDatabaseIsAvailable
```

Por padrão os middlwares criados através da instrução acima ficarão na pasta **App\Http\Middleware**.

Utilizando o editor de textos vamos abrir a classe criada e editar o método `handle`.

```php
<?php

namespace App\Http\Middleware;

use Closure;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB;

class EnsureDatabaseIsAvailable
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse)  $next
     * @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
     */
    public function handle(Request $request, Closure $next)
    {
        try
        {
            DB::connection()->getPdo();
            return $next($request);
        }
        catch(Exception $e)
        {
            return response()->json([
                'errorCode' => $e->getCode(),
                'message' => 'Database is unavailable. Try again later',
                'timestamp' => now()
            ], Response::HTTP_SERVICE_UNAVAILABLE);
        }
    }
}

```

Explicando o código, verificamos se há conexão com o banco de dados através do método `getPdo()` da classe `DB`.

Se não houver conexão disponível o método `getPdo()` lançará uma exceção que será capturada no bloco `catch` logo abaixo, possibilitando a criação de uma resposta customizada.

---


> Controller

Voltando ao terminal criaremos agora a controller com a seguinte instrução:

```
php artisan make:controller InvoiceController
```

Por padrão as controllers criadas através da instrução acima ficarão na pasta **App\Http\Controllers**.

Para a classe controller implementaremos apenas um método que será responsável por fazer uma consulta simples ao banco de dados.

```php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class InvoiceController extends Controller
{
    public function getInvoices()
    {
        $result = DB::select('select * from invoices');
        return response()->json($result);
    }
}

```


> Rota

Ainda no editor vamos abrir a classe **\route\api.php** adicionando uma nova rota que acionará o método `getInvoices()` criado na controller **\app\Http\Controllers\InvoiceController**.

```php
<?php

use App\Http\Controllers\InvoiceController;
use App\Http\Middleware\EnsureDatabaseIsAvailable;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::middleware(
    [
        EnsureDatabaseIsAvailable::class
    ])
    ->get('/invoice', [InvoiceController::class, 'getInvoices']);
```

## Testando

Para obtermos o resultado esperado (retornar uma mensagem caso o banco de dados esteja indisponível) precisamos parar o servidor MySQL.

Agora, acessando novamente o terminal na pasta do projeto vamos iniciar o servidor web embutido no PHP:

```
php artisan serve --port=8106
```

Voltando ao Visual Studio Code vamos abrir a extensão Thunder, que nos possibilitará realizar requisições REST para nossa api.


![Database unavailable](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j39i9uqmu4ep5ywp0fiu.jpg)

Podemos ver a mensagem customizada que implementamos na classe 
**\app\Http\middleware\EnsureDatabaseIsAvailable.php**.


Para concluir iniciamos o servidor MySQL realizando uma requisição para nossa api.


![Database available](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8yzjxwzgpzqxwdeuumal.jpg)


## Ordem de execução

Caso a rota ou aplicação precise de mais de um middleware (sim, pode acontecer) podemos determinar a ordem de execução.

O método `middleware()` na classe **\route\api.php** recebe como parâmetro um array das classes que serão utilizadas na rota.

Por exemplo, se fosse necessário verificar a conexão com banco de dados antes de validar um token, o código que implementaria esta situação seria este:

```php
<?php

use App\Http\Controllers\InvoiceController;
use App\Http\Middleware\EnsureDatabaseIsAvailable;
use App\Http\Middleware\EnsureTokenIsValid;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::middleware(
    [
        EnsureDatabaseIsAvailable::class,
        EnsureTokenIsValid::class
    ])
    ->get('/invoice', [InvoiceController::class, 'getInvoices']);

```

## Conclusão

Quase todas as linguagens de programação trazem alguma implementação de middleware.

Middleware é uma ferramenta poderosa e às vezes, pouco explorada.

Entretanto, dada sua característica de processar todas as requisições da pipeline, a sua criação e utilização devem ser criteriosas para evitar adversidades com performance, por exemplo.


## Extensões utilizadas

Em desenvolvimento web, uma boa ferramenta para testar requisições api faz toda diferença.

Por isso disponibilizo aqui o link para a extensão para VSCode Thunder [RESTClient](https://marketplace.visualstudio.com/items?itemName=rangav.vscode-thunder-client), não deixando nada a desejar para outras ferramentas.

---

Já para aplicações Laravel, a extensão [Laravel Extra Intellisense](https://marketplace.visualstudio.com/items?itemName=amiralizadeh9480.laravel-extra-intellisense) ajuda muito na inclusão automática de referências.

---


## Repositório público

Este projeto está publicado no Github [neste repositório](https://github.com/MarcioPolicarpo/laravel9-middleware.git).