Pipeline de Processamento de Requisições e Middlewares no Express com Node.js
Quando o framework Express recebe uma requisição, ela é passada para um pipeline de processamento. O pipeline é composto por um conjunto de componentes ou middleware, que recebem os dados da requisição e decidem como processá-la.
Assim, no exemplo anterior, o arquivo de aplicação tinha o seguinte código:
const express = require("express");
const app = express();
app.get("/", function (request, response) {
response.send("<h1>Página Principal</h1>");
});
app.get("/about", function (request, response) {
response.send("<h1>Sobre o Site</h1>");
});
app.get("/contact", function (request, response) {
response.send("<h1>Contatos</h1>");
});
app.listen(3000, () => console.log("Servidor iniciado na porta 3000"));
Aqui, o pipeline de processamento consistia em chamadas a app.get()
, que comparavam o endereço solicitado com a rota, e, se houvesse correspondência entre o endereço e a rota, a requisição era processada pelo método app.get()
.
Se necessário, podemos integrar qualquer função middleware no pipeline de processamento da requisição em qualquer etapa. Para isso, usamos o método app.use()
. Assim, alteramos o arquivo app.js
da seguinte forma:
const express = require("express");
const app = express();
app.use(function (_, _, next) {
console.log("Middleware 1");
next();
});
app.use(function (_, _, next) {
console.log("Middleware 2");
next();
});
app.get("/", function (_, response) {
console.log("Rota /");
response.send("Olá");
});
app.listen(3000, () => console.log("Servidor iniciado em http://localhost:3000"));
A função passada para app.use()
recebe três parâmetros:
request
: dados da requisiçãoresponse
: objeto para gerenciar a respostanext
: próxima função no pipeline de processamento
Cada uma das funções middleware apenas exibe uma mensagem no console e, no final, chama a próxima função usando next()
.
Ao executar a aplicação e acessar o endereço http://localhost:3000/
, todas as funções middleware serão chamadas antes da rota ser processada. O console exibirá:
c:\app> node app.js Middleware 1 Middleware 2 Rota /
No entanto, não é obrigatório chamar todas as funções middleware subsequentes. Podemos interromper o processamento em qualquer etapa:
const express = require("express");
const app = express();
app.use(function (_, _, next) {
console.log("Middleware 1");
next();
});
app.use(function (_, response) {
console.log("Middleware 2");
response.send("Middleware 2");
});
app.get("/", function (_, response) {
console.log("Rota /");
response.send("Olá");
});
app.listen(3000, () => console.log("Servidor iniciado em http://localhost:3000"));
Agora, o processamento termina no Middleware 2, pois neste método ocorre o envio da resposta usando response.send()
, sem chamada à próxima função via next()
:
c:\app> node app.js Middleware 1 Middleware 2
As funções middleware também podem ser associadas a rotas específicas. Por exemplo:
const express = require("express");
const app = express();
app.use(function (_, _, next) {
console.log("Middleware 1");
next();
});
app.use("/about", function (_, response) {
console.log("About Middleware");
response.send("About Middleware");
});
app.get("/", function (_, response) {
console.log("Rota /");
response.send("Olá");
});
app.listen(3000, () => console.log("Servidor iniciado em http://localhost:3000"));
Neste caso, a segunda função middleware é associada explicitamente à rota /about
, então ela processará apenas a requisição http://localhost:3000/about
:
Por exemplo, ao acessar a rota /about
:
c:\app> node app.js Middleware 1 About Middleware
Ao acessar a rota /
:
c:\app> node app.js Middleware 1 Rota /
Exemplo de Middleware
Os middlewares ajudam a executar algumas tarefas que precisam ser feitas antes de enviar a resposta. Uma tarefa comum é o log das requisições.
Por exemplo, alteramos o arquivo app.js
da seguinte forma:
const express = require("express");
const fs = require("fs");
const app = express();
app.use(function (request, response, next) {
const now = new Date();
const hour = now.getHours();
const minutes = now.getMinutes();
const seconds = now.getSeconds();
const data = `${hour}:${minutes}:${seconds} ${request.method} ${request.url} ${request.get("user-agent")}`;
console.log(data);
fs.appendFile("server.log", data + "\n", function (error) {
if (error) return console.log(error);
console.log("Gravação no arquivo concluída");
});
next();
});
app.get("/", function (_, response) {
response.send("Olá");
});
app.listen(3000, () => console.log("Servidor iniciado em http://localhost:3000"));
Aqui, usando o objeto request, obtemos várias informações sobre a requisição e as adicionamos ao arquivo server.log utilizando o módulo fs
.