Se fez algum desenvolvimento de Nodo significativo nos últimos sete ou oito anos, provavelmente usou o Express para construir um servidor Web em algum momento. Embora possa criar um servidor em Node sem utilizar uma biblioteca, não lhe dá muito fora da caixa e pode ser bastante incómodo para adicionar funcionalidade. O Express é uma biblioteca de servidores minimalista e “nãoopinionada” e tornou-se o padrão de facto para construir aplicações web no Node. Para compreender Express, é necessário compreender Express Middleware.
O que é Express Middleware?
Middleware significa literalmente qualquer coisa que se coloque no meio de uma camada do software e outra. Middleware Expresso são funções que executam durante o ciclo de vida de um pedido para o servidor Express. Cada middleware tem acesso ao HTTP request
e response
para cada rota (ou caminho) a que está ligado. De facto, o Express Express está totalmente comprometido com funções de middleware. Além disso, o middleware pode terminar o pedido HTTP ou passá-lo para outra função middleware usando next
(mais sobre isso em breve). Este “encadeamento” de middleware permite-lhe compartimentar o seu código e criar middleware reutilizável.
Neste artigo vou explicar o que é middleware, porque o utilizaria, como utilizar o middleware Express existente, e como escrever o seu próprio middleware para Express.
Requisitos para Escrever Middleware Express
Há algumas coisas que precisará de instalar para criar, utilizar, e testar o middleware Express. Em primeiro lugar, precisará de Node e NPM. Para garantir que os tem instalados, pode correr:
npm -v && node -v
Deverá ver as versões Node e NPM que tem instaladas. Se obtiver um erro, terá de instalar o Nó. Estou a utilizar a última versão de ambas à data deste artigo, que é Node 10.9.0 e NPM 6.4.1, mas todos os exemplos devem funcionar com Node versões 8+ e NPM versões 5+.
I também estará a utilizar a versão Express 4.x. Isto é importante porque foram feitas grandes alterações da versão 3.x para a 4.x.
Será também útil ter o Postman instalado para testar rotas usando quaisquer outros verbos HTTP além de GET
.
Express Middleware: O Básico
Para começar, utilizará o mais básico middleware embutido do Express’. Isto dar-lhe-á a oportunidade de ver como o middleware é utilizado, e como o Express middleware é estruturado.
Criar um novo projecto e npm init
it…
npm initnpm install express --save
Criar server.js
e colar o seguinte código:
const express = require('express');const app = express();app.get('/', (req, res, next) => { res.send('Welcome Home');});app.listen(3000);
Executar o servidor via node server.js
, aceder http://localhost:3000
, e deverá ver impresso no seu browser “Welcome Home”.
O app.get()
função é Middleware a nível de Aplicação. Notará que os parâmetros passados para o método são req
res
, e next
. Estes são o pedido recebido, a resposta a ser escrita, e um método de chamada para passar a chamada para a próxima função de middleware uma vez que o middleware actual esteja terminado. Neste caso, assim que a resposta é enviada, a função sai. Pode também encadear outros middleware aqui chamando o next()
método.
Vamos dar mais alguns exemplos dos diferentes tipos de middleware.
Express Request Logging Middleware Example
In Express, pode configurar o middleware para ser middleware “global”; o que significa que será chamado para cada pedido de entrada.
Alterar o conteúdo de server.js
para:
const express = require('express');const app = express();app.use((req, res, next) => { console.log(req); next();});app.get('/', (req, res, next) => { res.send('Welcome Home');});app.listen(3000);
Desta vez, quando for a http://localhost:3000
deverá ver o mesmo na janela do seu browser, mas de volta na janela da consola verá a saída do objecto de pedido de entrada.
O middleware faz o logout do objecto de pedido e depois chama next()
. O próximo middleware no pipeline trata do pedido de obter o URL raiz e envia de volta o texto de resposta. Usando app.use()
significa que este middleware será chamado para cada chamada para a aplicação.
Restrict Express Request Content Type Example
Além de executar middleware para todas as chamadas, também pode especificar para executar apenas middleware para chamadas específicas.
Alterar o server.js
ficheiro novamente para:
const express = require('express');const app = express();const requireJsonContent = () => { return (req, res, next) => { if (req.headers !== 'application/json') { res.status(400).send('Server requires application/json') } else { next() } }}app.get('/', (req, res, next) => { res.send('Welcome Home');});app.post('/', requireJsonContent(), (req, res, next) => { res.send('You sent JSON');})app.listen(3000);
Desta vez, iniciar o servidor executando:
node server.js
Para testar isto, abra o carteiro e crie um pedido de correio para http://localhost:3000
. Não definir quaisquer cabeçalhos e executar o pedido. Receberá de volta a mensagem “Server requires application/json”.
Now go back and add the Content-Type
header with a value of application/json
and run the request again. Receberá a mensagem “You send JSON” de volta do servidor.
This app.post()
method call adds the requireJsonContent()
middleware function to ensure the incoming request payload has a Content-Type
header value set to application/json
. Se não passar a verificação, é enviada uma resposta de erro. Se o fizer, o pedido é então entregue à próxima peça de middleware na cadeia através do next()
método.
Third-Party Express Middleware
Você construiu um par de middlewares personalizados até longe, mas há muitos pacotes já construídos para fazer as coisas que você normalmente poderia querer fazer. De facto, utilizou a biblioteca de middleware de encaminhamento simples, usando as funções app.get()
ou app.post()
middleware. Existem milhares de bibliotecas de middleware para fazer coisas como analisar os dados de entrada, encaminhamento e autorização.
Okta tem um middleware Express para segurança OIDC que lhe mostrarei para demonstrar usando bibliotecas de middleware de terceiros.
Porquê Okta para Aplicações Expresso
No Okta, o nosso objectivo é tornar a gestão de identidade muito mais fácil, mais segura, e mais escalável do que aquilo a que está habituado. Okta é um serviço em nuvem que permite aos programadores criar, editar, e armazenar com segurança contas de utilizadores e dados de contas de utilizadores, e ligá-los a uma ou várias aplicações. A nossa API permite-lhe:
- Autenticar e autorizar os seus utilizadores
- Armazenar dados sobre os seus utilizadores
- Executar login baseado em palavra-passe e social
- Segurar a sua aplicação com autenticação multi-factor
- E muito mais! Verifique a nossa documentação de produto
Okta’s OIDC Express Middleware
Para instalar o middleware OIDC do Okta para Express, execute:
npm install @okta/[email protected] --save
Então no ficheiro server.js
, cria-se uma instância se o middleware com algumas opções de configuração para que o Okta saiba como se ligar à sua aplicação Okta.
const oidc = new ExpressOIDC({ issuer: 'https://{yourOktaDomain}/oauth2/default', client_id: '{yourClientId}', client_secret: '{yourClientSecret}', redirect_uri: 'http://localhost:3000/authorization-code/callback', scope: 'openid profile'});
Terá também de dizer ao Express para usar o router de middleware OIDC em vez do router predefinido.
app.use(oidc.router);
Então utiliza-o como qualquer outro middleware:
app.get('/protected', oidc.ensureAuthenticated(), (req, res) => { res.send('Top Secret');});
A função oidc.ensureAuthenticated()
é um middleware na biblioteca do Okta. Executa uma função para ver se o utilizador actual está ligado. Se estiver, chama next()
para deixar a função app.get()
continuar a tratar do pedido. Se não forem, enviará de volta um HTTP 401 (Unauthorized)
resposta.
Ordem de middleware é importante
Quando um pedido é recebido pelo Express, cada middleware que corresponda ao pedido é executado na ordem em que é inicializado até que haja uma acção de terminação (como uma resposta a ser enviada).
Assim, se ocorrer um erro, todo o middleware que se destina a tratar de erros será chamado em ordem até que um deles não chame a next()
chamada de função.
Manuseamento de erros no Express Middleware
Express tem um manipulador de erros padrão integrado que é inserido no final da tubagem de middleware que trata de quaisquer erros que possam ter ocorrido na tubagem. A sua assinatura acrescenta um parâmetro de erro aos parâmetros padrão de pedido, resposta, e seguinte. A assinatura básica parece assim:
app.use((err, req, res, next) => { // middleware functionality here})
Para chamar um middleware de tratamento de erros, basta passar o erro para next()
, desta forma:
app.get('/my-other-thing', (req, res, next) => { next(new Error('I am passing you an error!'));});app.use((err, req, res, next) => { console.log(err); if(!res.headersSent){ res.status(500).send(err.message); }});
Neste caso, o middleware de tratamento de erros no final do gasoduto tratará do erro. Poderá também reparar que verifiquei a propriedade res.headersSent
. Isto apenas verifica se a resposta já enviou os cabeçalhos para o cliente. Se não enviou o estado 500 HTTP e a mensagem de erro para o cliente. Também pode encadear middleware de tratamento de erros. Isto é comum para lidar com diferentes tipos de erros de diferentes maneiras. Por exemplo:
app.get('/nonexistant', (req, res, next) => { let err = new Error('I couldn\'t find it.'); err.httpStatusCode = 404; next(err);});app.get('/problematic', (req, res, next) => { let err = new Error('I\'m sorry, you can\'t do that, Dave.'); err.httpStatusCode = 304; next(err);});// handles not found errorsapp.use((err, req, res, next) => { if (err.httpStatusCode === 404) { res.status(400).render('NotFound'); } next(err);});// handles unauthorized errorsapp.use((err, req, res, next) => { if(err.httpStatusCode === 304){ res.status(304).render('Unauthorized'); } next(err);})// catch allapp.use((err, req, res, next) => { console.log(err); if (!res.headersSent) { res.status(err.httpStatusCode || 500).render('UnknownError'); }});
Neste caso, o middleware verifica se foi atirado um erro 404 (não encontrado). Se for esse o caso, torna a página de modelo ‘NotFound’ e depois passa o erro para o item seguinte no middleware. O middleware seguinte verifica se foi atirado um erro 304 (não autorizado). Se foi, rasga a página ‘NãoAutorizado’, e passa o erro para o próximo middleware no pipeline. Finalmente, o manipulador de erros “catch all” apenas regista o erro e, se não tiver sido enviada resposta, envia o erro httpStatusCode
(ou um estado HTTP 500 se não for fornecido nenhum) e torna o modelo ‘UnknownError’.
Learn More About Express Middleware
Para instruções detalhadas sobre a configuração do middleware Okta OIDC, pode seguir o ExpressJS Quickstart.
Há também uma lista de middleware Express suportado oficialmente nesta repo GitHub que pode experimentar e aprofundar para aprender mais
Finalmente, se estiver interessado em aprender mais sobre como utilizar o Okta, há um SDK do nó Okta para implementar mais funcionalidades de gestão de utilizadores na sua aplicação.