Utilizando Express.js para aplicações Node

21 min de leitura
Patrocinado
Imagem de: Utilizando Express.js para aplicações Node
Avatar do autor

Equipe TecMundo

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 1Tabela 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:

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 2Tabela 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 5Listagem 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 1Figura 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 2Figura 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 3Figura 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 4Figura 4: Crie uma nova lista de compras.

Fazendo isso, você verá o seguinte resultado na tela principal:

Figura 5Figura 5: Tela principal com um link para a nova lista de compras.

Clique em “My Shopping List” para visualizar a nova lista:

Figura 6Figura 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 7Figura 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 8Figura 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!

Você sabia que o TecMundo está no Facebook, Instagram, Telegram, TikTok, Twitter e no Whatsapp? Siga-nos por lá.