Na Unidade 11 do Node.js Learning Path, vamos mostrar o Express.js. Este curso não estaria completo sem, pelo menos, apresentarmos um web framework, e escolhemos Express.js por ser um dos mais populares no ecossistema Node. Essa também é a base para outros ecossistemas, como Loopback e krakenjs.
Express é minimalista, rápido e possui inúmeras contribuições implementadas. Caso você seja um desenvolvedor profissional e atue com o ecossistema Node, é bem possível que você encontre esse framework. Então, você deve estar familiarizado com ele.
Dentro do Express
Em sua estrutura básica, aplicações Express possuem os seguintes elementos:
Middleware: código utilizado para objetos de solicitação/resposta HTTP.
Roteamento: combina um caminho URL com uma função a ser executada.
Processamento de visualização: renderiza uma página HTML baseada em um template e dados de request.
Validação e limpeza de dados: compara dados de solicitações com regras de validação, assegurando que não haja dados exploratórios.
Este tutorial apresenta todos os conceitos acima. Caso você necessite de mais funcionalidades, as contribuições estendem esse núcleo mínimo.
Node.js Learning Path
O guia a seguir é parte do Node.js Learning Path, sendo resultado de um processo contínuo de ensinamentos já publicados. Por isso, para aprender tudo desde o início, confira o conteúdo do Learn Node.js, Unit 1: Overview of Node.js Learning Path.
Sobre o tutorial
Converti uma aplicação da lista de compras da Unidade 6 para implementar o Express, além de adicionar uma Interface Gráfica do Usuário (Graphical User Interface, ou GUI) para que fosse possível utilizar o navegador na hora de interagir com a construção. Você encontra o código necessário para este passo a passo no GitHub.
Recomendo a leitura completa do material primeiramente, adicionando os códigos dos exemplos ao seu editor. Após ter uma visão geral do tema, inicie a aplicação a partir de uma janela do terminal ou um comando prompt.
1 – Navegue pelo diretório Unit-11 em sua cópia local do repositório GitHub.
2 – Execute npm install para instalar os pacotes necessários do registro npm.
3 – Leia os dados na data base SQLite3: npm run load-db.
4 – Inicie a aplicação: npm start.
5 – Direcione seu navegador para http://localhost:3000.
Agora você pode começar a trabalhar no passo a passo utilizando o código-fonte e a aplicação em execução como referência. Conforme realiza as alterações, tente comparar as telas da UI com os módulos, as páginas e outros recursos encontrados na fonte. Assim, você aprende como eles funcionam.
Aplicação de lista de compras
Aqui, você desenvolverá uma versão aprimorada da aplicação de lista de compras desenvolvida na Unidade 6. Para converter a versão anterior, primeiro executei o express-generator para criar uma estrutura básica; depois, adicionei novos diretórios. O código abaixo mostra a estrutura final do diretório.
Listagem 1: Estrutura do diretório do projeto de lista de compras
Ix:~/src/projects/IBM-Developer/Node.js/Course/Unit-11 sperry$ tree -d .
.
+-- bin
+-- config
+-- controllers
+-- models
+-- node_modules
+-- public
¦ +-- images
¦ +-- javascripts
¦ +-- stylesheets
+-- routes
+-- scripts
+-- test
+-- utils
+-- views
14 directories
Sugiro separar um momento para conferir os diretórios e se familiarizar com o código. Isso vai ajudá-lo a avançar mais facilmente pelo restante do curso.
Nos próximas seções, vamos explorar módulos essenciais de uma aplicação Express.
Módulo 1: Middleware Express
Middleware é uma função Express que fica entre o servidor HTTP e a lógica de negócio (business logic), tendo acesso a objetos de solicitação e resposta HTTP para processamento. Tais funções são instaladas por meio da função use().
Aplicações Express utilizam o middleware para executar muitas funções relacionadas ao gerenciamento de web requests. Normalmente, cada middleware é responsável por apenas uma tarefa, enviando solicitações para o próximo middleware executar outra ação.
Algumas dessas funções são fornecidas pelo próprio Express, como análise do corpo da solicitação ou manipulação de recursos estáticos, e outras pelo contribs, como análise de cookies e gerenciamento de erros HTTP. Há também pacotes Node API para ações como processamento de paths.
Alguns middlewares, como os de roteamento, podem ser encontrados no Express Router, que apresentarei na próxima seção.
Nota: o diretório raiz de todo o código mostrado nesta seção é o Node.js/Course/Unit-11/. Todos os locais de arquivos e caminhos estão relacionados a ele.
Configuração de uma aplicação Express
O ponto inicial de uma aplicação Express é ./bin/www.js, cuja função principal é criar o servidor HTTP. Para isso, o ./bin/www.js utiliza a configuração encontrada no módulo principal, chamado app.js ou server.js.
A configuração em ./app.js é bem simples. Separei em duas seções para facilitar ainda mais a descrição.
Baseado no config, o servidor Express primeiro chama os módulos require(), necessários ao módulo da aplicação. Os módulos require() incluem módulos locais para gerenciar o roteamento.
// require
const path = require('path');
const express = require('express');
const cookieParser = require('cookie-parser');
const createError = require('http-errors');
// local modules
const indexRouter = require('./routes/index');
const listsRouter = require('./routes/lists');
const restRouter = require('./routes/rest');
A seguir, a aplicação Express é criada:
// Create Express application
const app = express();
Depois, ela chama o app.use() para instalar funções middleware insert-request:
// middleware
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));
app.use(cookieParser());
O bloco de código utiliza o seguinte middleware:
1 – Análise de body request
– Objetos JSON no body request: (express.json())
– Parâmetros de URL codificada no body request: (express.urlencoded())
2 – Acesso a recursos estáticos, como imagens e Cascading Style Sheets (CSS): (express.static())
3 – Análise de cookies: (cookie-parser)
O servidor Express ajusta os caminhos das rotas por meio do app.use(), cujos parâmetros são (1) o caminho e (2) o módulo que o gerencia:
// Path routing
app.use(indexRouter);
app.use('/lists', listsRouter);
app.use('/rest', restRouter);
Nota: nenhum argumento no path implica que o middleware seja aplicado a todos os caminhos (/).
Então, ele configura o mecanismo de exibição por meio de chamadas app.set:
// View engine
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
Finalmente, é instalado o middleware responsável pelo gerenciamento de erros:
// Error handling
app.use(function(req, res, next) {
// catch 404 and forward to error handler
next(createError(404));
});
app.use(function(err, req, res, next) {
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
Nota: middleware de gerenciamento de erros utiliza módulos http-errors.
A listagem a seguir mostra o módulo na sua totalidade:
Listagem 2: Configuração da aplicação Express no app.js
01 // require
02 const path = require('path');
03 const express = require('express');
04 const cookieParser = require('cookie-parser');
05 const createError = require('http-errors');
06
07 // local modules
08 const indexRouter = require('./routes/index');
09 const listsRouter = require('./routes/lists');
10 const restRouter = require('./routes/rest');
11
12 // Create Express application
13 const app = express();
14
15 // Install Middleware
16 app.use(express.json());
17 app.use(express.urlencoded({ extended: false }));
18 app.use(express.static(path.join(__dirname, 'public')));
19 app.use(cookieParser());
20
21 // Path routing
22 app.use(indexRouter);
23 app.use('/lists', listsRouter);
24 app.use('/rest', restRouter);
25
26 // View engine
27 app.set('views', path.join(__dirname, 'views'));
28 app.set('view engine', 'pug');
29
30 // Error handling
31 app.use(function(req, res, next) {
32 // catch 404 and forward to error handler
33 next(createError(404));
34 });
35 app.use(function(err, req, res, next) {
36 res.locals.message = err.message;
37 res.locals.error = req.app.get('env') === 'development' ? err : {};
38 res.status(err.status || 500);
39 res.render('error');
40 });
41
42 module.exports = app;
Executando a cadeia middleware
A ordem de chamadas do app.use() é importante porque o middleware constrói uma “cadeia” executada na ordem em que o app.use() é requisitado. O middleware vai gerenciar a solicitação e redirecionar para outro URL/path/etc. ou repassá-la para o próximo elo da cadeia por meio da função next().
Há uma boa razão para que o código gerenciador de erros fique no fim da cadeia: se uma solicitação chegou ao middleware responsável por essa tarefa, significa que nenhum dos outros conseguiu atender a solicitação ou que uma exceção passou pelo caminho todo. Só então é que o gerenciador de erros recebeu a solicitação.
Módulo 2: Roteamento Express
Mais sobre middleware: A documentação Express traz um bom material sobre a utilização de middleware. Caso você deseje mais informações sobre execução de cadeia middleware e sobre como escrever um middleware funcional, dê uma conferida.
Rota é um mapeamento entre o caminho de uma URL e o destino de sua aplicação, como uma página ou uma lógica de negócio em uma função REST.
Roteamento é o processo de mapear uma solicitação HTTP especificada por um método, como GET ou POST, junto a um caminho URL para refazer uma chamada de função e gerenciar a solicitação.
A Unidade 6 apresentou a funcionalidade de roteamento para a aplicação de lista de compras, sendo um dos exercícios propostos. O roteamento utilizado para mapear uma combinação de método/caminho HTTP para um serviço REST foi básico, se não tedioso. Nesta unidade, você vai aprender quão mais fácil é o roteamento Express.
Rotas
Em uma típica aplicação Express, as rotas estão localizadas em uma pasta ./routes, imediatamente subordinada à raiz do projeto. A aplicação de lista de compras contém três módulos de definição de rotas no diretório dedicado a elas.
– index.js define a rota padrão.
– rest.js mantém as definições de rotas para serviços REST fornecidos pela aplicação de lista de compras.
– lists.js mantém as definições das rotas relacionadas à visualização de rotas atendidas pela aplicação.
Vamos começar com as rotas do serviço REST, resumidas na Tabela 1.
Tabela 1: Rotas para serviços REST fornecidos pela aplicação de lista de compras.
É fácil escrever o código de roteamento no Express. Depois de require(express), a função express.Router() deve ser chamada para criar o roteador. No roteador, você chama funções que correspondem ao método HTTP que você precisa gerenciar, como router.get() para uma solicitação GET, router.post() para POST e por aí vai.
Rotas delegadas para funções controladoras
A funções de roteamento seguem os seguintes argumentos:
1 – O route path é a parte do caminho requisitado da URL, podendo ser um padrão string ou uma expressão regular.
2 – O gerenciador de rotas é a função controladora que gerencia as rotas.
O roteamento de cada caminho da aplicação de lista de compras é realizado em duas fases. A primeira envolve a parte superior do caminho.
Vamos relembrar o código do exemplo. A parte superior da rota (/rest) era especificada em app.js:
const express = require('express');
.
.
// local modules
.
const restRouter = require('./routes/rest');
.
// Path routing
.
.
app.use('/rest', restRouter);
.
Normalmente, o nome do módulo da rota tem a ver com a parte do caminho utilizado por ela. Nesse caso, /rest é o caminho, então rest.js é o nome do módulo.
Todas as rotas relacionadas a /rest são encaminhadas ao roteador rest para posterior roteamento. O roteador, então, examina a parte restante do caminho para determinar seu destino final.
Esse exemplo mostra como roteadores podem ser combinados para modularizar o roteamento. A aplicação de lista de compras é simples, e seus caminhos não são tão complicados, então poucos roteadores bastam. Aplicações complexas, entretanto, normalmente possuem URLs mais longas e um roteamento mais complexo. Logo, desenvolver a habilidade de empilhar roteadores é muito útil.
Toda vez que o caminho /rest corresponder ao módulo rest, o restRouter é chamado para manipular o mapeamento do restante do caminho.
Listas de roteamento
Agora, na aplicação de lista de compras, vamos dar uma olhada nas rotas /lists do rest.js. (Perceba que estamos exemplificando o roteamento de listas nesta seção; estudaremos o módulo lists.js em breve.)
Listagem 3: Rotas do REST para /lists no rest.js
01 const express = require('express');
02
03 // We have no control over stuff like this, so tell eslint to chill
04 const router = express.Router();//eslint-disable-line new-cap
05
06 // The rest controller that handles the requests
07 const restController = require('../controllers/rest-controller');
08
09 // REST service - fetch all shopping lists
10 router.get('/lists', restController.fetchAll);
11 // REST service - create new shopping list
12 router.post('/lists', restController.create);
13 // REST service - fetch shopping list by ID
14 router.get('/lists/:listId', restController.read);
15 // REST service - update the specified list
16 router.put('/lists/:listId', restController.update);
17 // REST service - add an item to the specified shopping list
18 router.post('/lists/:listId/items', restController.addItem);
19 // REST service - fetch all items for the specified shopping list
20 router.get('/lists/:listId/items', restController.fetchAllItems);
21 // REST service - update the specified item for the specified list
22 router.put('/lists/:listId/items/:itemId', restController.updateItem);
23 // REST service - remove the specified item from the specified list
24 router.delete('/lists/:listId/items/:itemId', restController.removeItem);
25 // REST service - search for items
26 router.get('/items', restController.itemSearch);
27
28 module.exports = router;
Veja que no roteador rest você não precisa mapear a parte /rest do caminho novamente; é preciso apenas especificar a parte depois de /rest.
Solicitação de correspondência
O roteador tenta encontrar uma alta correspondência entre a parte do caminho que solicita a URL e o caminho em suas rotas. Ele vai executar uma função callback para cada rota correspondente.
Nota sobre roteamento: Uma rota é uma combinação única do caminho e do método HTTP. Se duas ou mais rotas correspondem a uma única solicitação, todas essas rotas serão executadas. As rotas da aplicação de lista de compras são todas únicas, mas outra aplicação pode ter de gerenciar a mesma rota em diferentes controladores (para especificar uma solicitação, por exemplo). O Express concede essa capacidade.
Considere a seguinte URL:
http://localhost:3000/rest/lists
A parte do caminho dessa URL sozinha é ambígua, correspondendo às rotas /rest/lists acima (linhas 10 e 12, respectivamente). O Express analisa o método HTTP especificado na solicitação para determinar qual rota é a correspondente.
Se o método GET é especificado, o restController.fetchAll() será chamado (linha 10). Se POST é especificado, a função restController.create será chamada (linha 12).
No caso em que a solicitação seja como a exemplificada abaixo:
HTTP Method: POST
URL: http://localhost:3000/rest/lists
O servidor Express vai corresponder à seguinte rota:
router.post('/lists', restController.create);
Funções controladoras aprimoram serviços Rest
Em uma aplicação Express típica, as rotas estão localizadas em uma pasta ./controllers, imediatamente subordinada à raiz do projeto.
O módulo controlador para o roteador rest, chamado de rest-controller, tem uma função para cada rota.
Todas as funções de rotas middleware trazem uma assinatura semelhante à função create() exemplificada abaixo:
function create(req, res, next) {
let requestBody = req.body;
listsDao.create(requestBody.description).then((result) => {
utils.writeServerJsonResponse(res, result.data, result.statusCode);
}).catch((err) => {
next(err);
});
}
Os parâmetros são:
req: Um objeto Express Request, que representa a solicitação HTTP.
res: Um objeto Express Response, que representa a resposta HTTP.
next: uma função Express utilizada para transferir a solicitação para o próximo middleware da cadeia.
Vamos conferir a função create().
Primeiro, recuperamos o corpo da solicitação como um objeto JSON — graças ao middleware express.json(). Sendo parte da solicitação POST, o usuário envia o atributo de descrição da lista de compras a ser criado no corpo da solicitação. O express.json() o analisa e o disponibiliza na propriedade req.body.description.
Depois, chamamos listsDao.create e passamos a descrição da nova lista de compras. Caso a chamada seja bem-sucedida, chamamos utils.writeServerJsonResponse() — a mesma função vista na Unidade 6 — para enviar uma resposta e um status code 200 do JSON para o usuário.
Se um erro ocorrer, simplesmente repassamos a solicitação ao próximo middleware da cadeia. Caso a atribuição não seja repassada, a tarefa não será concluída e será suspensa em algum momento.
Módulo 3: Visualizações Express
A visualização é a interface da aplicação — ou aquilo com o que o usuário se depara ao abrir a aplicação. Até agora, a aplicação da lista de compras é uma coleção de serviços REST; útil, porém não tão atraente. Então, criei uma Interface Gráfica do Usuário (Graphical User Interface, ou GUI), que iremos explorar nesta seção.
O Express possui uma arquitetura de processamento de modelo de visualização conectável, o que significa que utiliza um processador de modelo, junto com templates, para renderizar visualizações. Os usuários interagem com essas visualizações.
Ele suporta vários processadores de modelos. Alguns dos mais populares são:
Pug (antigo Jade)
O processador de modelo padrão é o Pug, que será explorado nesta unidade.
Criando um template
Uma aplicação Pug normalmente define um ou mais layouts de páginas padrão, replicado a outras.
O layout da aplicação da lista de compras é chamado de layout.pug e está localizado no diretório de views (visualizações).
Listagem 4: Template base do layout da aplicação de lista de compras (layout.pug)
doctype html
html(lang='en')
head
title= title
meta(charset='utf-8')
meta(name='viewport', content='width=device-width, initial-scale=1')
link(rel="stylesheet", href='https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css')
script(src='https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js')
link(rel='stylesheet', href='/stylesheets/style.css')
body
div(class='container-fluid')
div(class='row')
div(class='col-sm-2')
block sidebar
ul(class='sidebar-nav')
li
//- Stateless navigation goes here
a(href='/lists') Home
div(class='col-sm-10')
//- Replace individual page content in the block below
block content
//- The page footer
block foot
//- Render errors in the footer
if errors
p
h3 You have errors:
ul
for error in errors
li.list-group-item-danger= error.msg
Esse template fornece um GUI responsivo e também utiliza Bootstrap.
Rotas de visualização
Cada visualização da aplicação da lista de compras possui uma rota associada a ela, como exemplificado abaixo.
Tabela 2: Rotas de visualização da aplicação de lista de compras.
Rotas e funções controladoras
Mais uma vez, rotas são delegadas a funções controladoras. Lembre que a parte superior da rota (/lists) foi especificada em app.js:
const express = require('express');
.
.
// local modules
.
const listsRouter = require('./routes/lists');
.
// Path routing
.
.
app.use('/lists', listsRouter);
.
Para melhor legibilidade do código, sugerimos dar ao módulo roteador um nome que combine com a parte do caminho utilizado na rota. Nesse caso, /lists é o caminho, então lists.js é o nome do módulo.
Todas as rotas do caminho da URL relacionadas a /lists serão encaminhadas ao roteador lists. Então, o roteador vai examinar a parte restante do caminho para determinar o destino final de cada uma das que sobraram.
Toda vez que o caminho /lists é identificado, o listsRouter (no módulo lists) é chamado para mapear o restante do caminho. A Listagem 5 mostra as rotas /lists no lists.js.
Listagem 5: Middleware roteador para as visualizações da aplicação da lista de compras (lists.js).
Perceba que você não precisa mapear a parte /lists do caminho novamente; basta especificar a parte do caminho após /lists.
Solicitar correspondência
Lembre-se que o roteador vai tentar encontrar uma alta correspondência entre a parte do caminho da solicitação URL e os caminhos em suas rotas, sendo que vai executar a função callback para cada rota correspondente. Como exemplo, considere a seguinte URL:
http://localhost:3000/lists/create
A parte do caminho dessa URL corresponde às duas rotas /lists acima (linhas 11 e 13, respectivamente), mas a função controladora depende do método HTTP especificado na solicitação. Se o método GET é especificado, listsController.fetchAll() será chamado. Se POST, a função listsController.create é que será solicitada.
Em um caso no qual a solicitação se pareça com:
HTTP Method: GET
URL: http://localhost:3000/create
O servidor Express vai corresponder à seguinte rota:
router.get('/create', listsController.create);
Renderizando a Interface Gráfica do Usuário (GUI)
Quando criamos uma lista de compras com um serviço rest, o usuário prepara os dados e os envia. Assim, a lista é criada. Ao adicionarmos uma Interface Gráfica do Usuário, precisamos de mais duas ações:
1. Renderizar a GUI, utilizada pelo usuário para inserir dados.
2. Validar os dados e repassá-los ao serviço rest.
Você aprenderá sobre validação na próxima seção. Por enquanto, vamos ver como as páginas são renderizadas.
Passo 1: Renderizar a página com funções controladoras
O módulo controlador para o roteador de listas, chamado de lists-controller, tem uma função para cada rota. Abaixo, você encontra a função listsController.create().
function renderCreatePage(req, res, next) {
// Render the page to create a new shopping list
res.render('lists-create', { title: 'Create Shopping List' });
}
Ela chama a função render() do objeto de resposta res Express, fornecendo o nome do template, junto com o objeto JSON que contém os dados da página.
Observação: em uma aplicação Express típica, visualizações estão localizadas em uma pasta ./views imediatamente subordinada à raiz do projeto.
O template Pug contendo a fonte da página se chama lists-create.pug, como mostrado na Listagem 6.
Listagem 6: Template para criar a página de lista de compras (lists-create.pug)
extends layout
block content
h1= title
p Enter required fields and click the Save link to create your new shopping list.
form(method='POST' action='/lists')
div.form-group
label(for='description') Description*:
input(type='text', placeholder='Description' name='description')
div.form-group
button.btn.btn-primary(type='submit') Create
Considerando o que a página faz, você pode se perguntar como esse template pode ser tão simples. Perceba a primeira linha:
extends layout
Esse template estende o que você viu na Listagem 4, chamado de layout.pug. O template lists-create.pug proporciona capacidades que estendem o layout base.
Agora, dê uma olhada na sintaxe do Pug. Cada tag HTML tem um atributo Pug que a copia. A página é bem simples: contém um H1 para o título da página e algumas instruções em uma tag de parágrafo, seguida de um formulário para que o usuário insira a descrição da nova lista de compras a ser criada.
A página é exibida dessa maneira no Chrome:
Figura 1: Criação da página de listas de compras.
Quando o usuário insere a descrição da lista de compras e clica em "Create", o formulário com os dados contendo as informações será enviado para /lists (que se expande para http://localhost:3000/lists). Relembre a rota criada para o gerenciamento dessa URL (da Listagem 4, linha 24):
// Call REST service to create new shopping list
router.post('/', listsController.createList);
Passo 2: funções de chamadas na cadeia de funções controladoras
Uma vez que o usuário inseriu uma descrição de lista, a lista de compras pode ser criada. A criação de listas exige uma série de funções de chamadas, que realizam as seguintes ações:
Validar e limpar os dados.
Gerenciar erros de validação.
Invocar o serviço rest para criar a lista de compras.
Precisaremos de mais que uma função para executar todas essas ações. A função createList é, na verdade, uma cadeia de funções; ou seja, uma matriz de funções de chamadas.
Listagem 7: Cadeia de funções utilizadas para criar uma nova lista de funções (createList)
01 const createList = [
02 // Validate input(s)
03 body('description', 'Description cannot be empty').isLength({ min: 1 }),
04 // Sanitize fields.
05 sanitizeBody('description').trim().escape(),
06 // Check validation results
07 (req, res, next) => {
08 const errors = validationResult(req);
09 if (!errors.isEmpty()) {
10 let errorsArray = errors.array();
11 logger.debug(`Found ${errorsArray.length} errors with the request`);
12 res.render('lists-create', { title: 'Create Shopping List', data: '', errors: errorsArray });
13 } else {
14 logger.debug('Request is error free. Moving on...', 'createList()');
15 next();
16 }
17 },
18 // All is well (if we got this far). Send the request!
19 (req, res, next) => {
20 let requestBody = JSON.stringify(req.body);
21 request('POST', '/rest/lists', requestBody, (err, data) => {
22 if (err) {
23 next(err);
24 } else {
25 // Redirect to main /lists page
26 res.redirect('/lists');
27 }
28 });
29 }
30 ];
Os dados são validados e limpos nas linhas 3 e 5. Então, os resultados da validação são computados em seus próprios callbacks req/res/next (linhas 7 a 17).
Se a solicitação é considerada válida, as funções /rest/lists do serviço rest são invocadas para a criação da lista (linhas 20 e 21). Caso um erro ocorra (linha 22), a solicitação é enviada para o próximo middleware (linha 23). Aí, se a solicitação é bem-sucedida, o navegador redireciona para a rota /lists (linha 26), que leva o usuário à página principal.
Módulo 4: Validação e limpeza de dados (express-validator)
Validação de dados é um problema complicado de resolver, especialmente no contexto de solicitações web. Felizmente, express-validator é compatível com Express e soluciona a questão.
Para validar e limpar o corpo de solicitação, você chama o express-validator e define quais campos do corpo você quer validar e como fazer isso. Abaixo está a fonte relevante de lists-controllers.js:
const { body, validationResult } = require('express-validator/check');
('express-validator/filter');
const { sanitizeBody } = require('express-validator/filter');
.
.
body('description', 'Description cannot be empty').isLength({ min: 1 }),
.
.
A primeira tarefa do validador é, como o nome sugere, realizar a validação de dados. Ele utiliza a regra acima para validar o atributo de descrição na solicitação JSON usando o validador isLength() e assegurar que a descrição possua ao menos um caractere. Essa regra garante que, caso o usuário não insira descrição alguma, ele veja uma mensagem de erro: “A descrição não pode estar vazia”.
Depois é feita a limpeza dos dados, o que importante porque o usuário pode inserir o que quiser no campo de descrição. Um usuário mal-intencionado poderia tentar utilizar esse campo para explorar sua aplicação, inserindo um código JavaScript ou SQL. Você pode evitar isso limpando o input, removendo quaisquer caracteres potencialmente maliciosos:
sanitizeBody('description').trim().escape(),
Nesse caso, você pode eliminar qualquer espaço em branco e escapar de dados maliciosos.
Retornando erros ao usuário
Mais informações sobre validação e limpeza de dados
Confira os documentos do express-validator para aprender mais sobre validação e limpeza de dados. Sugerimos começar com a checagem de API, utilizada aqui para validar o corpo de solicitação.
Há mais um passo para a validação de dados, que é a informação de erros ao usuário. Mais uma vez, o express-validator pode ajudar.
Reportar erros com express-validator requer duas ações:
1. Uma chamada para validationResult().
2. Renderizar a página contendo erros para o usuário, junto com uma mensagem.
Olhando novamente a Listagem 7, linha 8, perceba o chamado para validationResult(req), passando o objeto de solicitação. Dessa chamada, o express-validator retorna o resultado da validação. O objeto é convertido para uma matriz (linha 10), e a página original é renderizada novamente, passando o objeto de erros nos dados JSON (linha 12).
Se esse erro de validação ocorrer, o usuário recebe a seguinte mensagem:
Figura 2: Validação de erro na criação de página de lista de compras.
Executando a aplicação de lista de compras
Se você seguiu o guia até aqui, entendeu como o Express gerencia rotas e renderiza páginas para a aplicação de lista de compras. Agora é a sua chance de executar a aplicação e vê-la como um usuário a veria.
Para executar a aplicação, certifique-se de que configurou os códigos corretamente. Então, abra a janela do terminal ou o comando prompt. Navegue até o diretório no qual está o código (caso precise de orientação adicional, revisite a Unidade 2).
– Execute o npm install para instalar todos os pacotes necessários.
– Carregue os dados no banco executando o npm run load-db (esse comando pode levar alguns minutos para ser concluído; portanto, seja paciente).
– Depois, execute o npm start para inicializar a aplicação.
Agora, direcione seu navegador para http://localhost:3000, endereço no qual você verá a tela principal:
Figura 3: Página principal da aplicação de lista de compras.
Crie uma nova lista de compras chamada “My Shopping List”. Insira a descrição e clique no botão “Create”:
Figura 4: Crie uma nova lista de compras.
Fazendo isso, você verá o seguinte resultado na tela principal:
Figura 5: Tela principal com um link para a nova lista de compras.
Clique em “My Shopping List” para visualizar a nova lista:
Figura 6: Visualizando a nova lista de compras.
Para adicionar itens, clique no link “Add Item to Shopping List”. Nesse exemplo, realizamos a pesquisa por “Red Delicious”:
Figura 7: Procurando e adicionando itens à lista de compras.
Selecione alguns itens, clique no botão “Add Selected Item(s)”, e eles estarão na sua lista.
Figura 8: Lista de compras com os itens selecionados.
Conclusão da Unidade 11
Nesta unidade, você aprendeu sobre middleware Express, roteamento e processamento de visualização de templates com Pug. Além disso, iniciou a validação e a limpeza de dados utilizando express-validator.
Certifique-se de separar o código-fonte da aplicação de lista de compras, executá-lo e fazer as alterações que desejar. Não existe um código que substitua você mesmo quando se trata de aprender a executar códigos (citação nossa, pode passar adiante). Você pode também assistir ao vídeo abaixo para ver a UI da lista de compras em ação.
...
Quer ler mais conteúdo especializado de programação? Conheça a IBM Blue Profile e tenha acesso a matérias exclusivas, novas jornadas de conhecimento e testes personalizados. Confira agora mesmo, consiga as badges e dê um upgrade na sua carreira!
.....
Participe da Maratona Behind the Code 2020, um desafio para desenvolvedores e entusiastas da tecnologia! Além de concorrer a prêmios, você ainda tem acesso a conteúdos e serviços gratuitos. Não perca essa chance, as inscrições vão até 7 de agosto!