Articles

Costruire e capire il Middleware Express attraverso gli esempi

Posted on

Se avete fatto qualsiasi sviluppo significativo di Node negli ultimi sette o otto anni, probabilmente avete usato Express per costruire un server web ad un certo punto. Mentre puoi creare un server in Node senza usare una libreria, non ti dà molto fuori dalla scatola e può essere abbastanza complicato aggiungere funzionalità. Express è una libreria server minimalista e “inopinata” ed è diventata lo standard de facto per costruire applicazioni web in Node. Per capire Express, devi capire Express Middleware.

Che cos’è Express Middleware?

Middleware significa letteralmente qualsiasi cosa che si mette in mezzo tra un livello del software e un altro. I middleware Express sono funzioni che vengono eseguite durante il ciclo di vita di una richiesta al server Express. Ogni middleware ha accesso all’HTTP request e response per ogni rotta (o percorso) a cui è collegato. In effetti, Express stesso è compromesso interamente da funzioni middleware. Inoltre, il middleware può terminare la richiesta HTTP o passarla ad un’altra funzione middleware usando next (ne parleremo più avanti). Questo “concatenamento” di middleware permette di compartimentare il codice e creare middleware riutilizzabili.

In questo articolo spiegherò cos’è il middleware, perché si dovrebbe usare, come usare il middleware Express esistente e come scrivere il proprio middleware per Express.

Requisiti per scrivere middleware Express

Ci sono alcune cose di cui avrete bisogno per creare, usare e testare middleware Express. Per prima cosa, avrete bisogno di Node e NPM. Per assicurarti di averli installati, puoi eseguire:

npm -v && node -v

Dovresti vedere le versioni di Node e NPM che hai installato. Se ottieni un errore, devi installare Node. Sto usando l’ultima versione di entrambi al momento di questo articolo, che è Node 10.9.0 e NPM 6.4.1, ma tutti gli esempi dovrebbero funzionare con Node versione 8+ e NPM versione 5+.

Io userò anche Express versione 4.x. Questo è importante perché sono state apportate importanti modifiche dalla versione 3.x alla 4.x.

Sarà anche utile avere installato Postman per testare i percorsi utilizzando qualsiasi verbo HTTP diverso da GET.

Express Middleware: Le basi

Per iniziare, useremo il più semplice dei middleware integrati in Express. Questo ti darà la possibilità di vedere come viene usato il middleware e come è strutturato il middleware Express.

Crea un nuovo progetto e npm init esso…

npm initnpm install express --save

Crea server.js e incolla il seguente codice:

const express = require('express');const app = express();app.get('/', (req, res, next) => { res.send('Welcome Home');});app.listen(3000);

Esegui il server tramite node server.js, accedi http://localhost:3000, e dovresti vedere “Welcome Home” stampato nel tuo browser.

La funzione app.get() è un Middleware a livello di applicazione. Noterete che i parametri passati al metodo sono reqres, e next. Questi sono la richiesta in arrivo, la risposta che viene scritta, e un metodo da chiamare per passare la chiamata alla prossima funzione middleware una volta che il middleware corrente è finito. In questo caso, una volta inviata la risposta, la funzione esce. Si potrebbero anche concatenare altri middleware chiamando il metodo next().

Diamo un’occhiata a qualche altro esempio dei diversi tipi di middleware.

Esempio di middleware di registrazione delle richieste in Express

In Express, si può impostare un middleware come middleware “globale”, cioè sarà chiamato per ogni richiesta in arrivo.

Cambia il contenuto di server.js in:

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);

Questa volta, quando andate su http://localhost:3000 dovreste vedere la stessa cosa nella finestra del browser, ma nella finestra della console vedrete l’output dell’oggetto richiesta in arrivo.

Il middleware fa il logout dell’oggetto richiesta e poi chiama next(). Il prossimo middleware nella pipeline gestisce la richiesta get all’URL root e rimanda la risposta testuale. Usare app.use() significa che questo middleware sarà chiamato per ogni chiamata all’applicazione.

Esempio di tipo di contenuto della richiesta Express restrittiva

Oltre a eseguire il middleware per tutte le chiamate, si può anche specificare di eseguire il middleware solo per chiamate specifiche.

Cambiare il file server.js di nuovo in:

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);

Questa volta, avviare il server eseguendo:

node server.js

Per testarlo, apri Postman e crea una richiesta di post a http://localhost:3000. Non impostare alcuna intestazione ed eseguire la richiesta. Otterrete il messaggio “Server requires application/json”.

Ora tornate indietro e aggiungete l’intestazione Content-Type con un valore di application/json ed eseguite nuovamente la richiesta. Otterrete il messaggio “Hai inviato JSON” dal server.

Questa chiamata al metodo app.post() aggiunge la funzione middleware requireJsonContent() per assicurarsi che il payload della richiesta in arrivo abbia un valore di intestazione Content-Type impostato su application/json. Se non supera il controllo, viene inviata una risposta di errore. Se lo supera, la richiesta viene passata al prossimo pezzo di middleware della catena attraverso il metodo next().

Third-Party Express Middleware

Finora avete costruito un paio di middleware personalizzati, ma ci sono molti pacchetti già costruiti per fare le cose che normalmente vorreste fare. Infatti, hai usato la semplice libreria middleware di routing utilizzando le funzioni middleware app.get() o app.post(). Ci sono migliaia di librerie middleware per fare cose come il parsing dei dati in entrata, il routing e l’autorizzazione.

Okta ha un middleware Express per la sicurezza OIDC che vi mostrerò per dimostrare l’utilizzo di librerie middleware di terze parti.

Perché Okta per le applicazioni Express

In Okta, il nostro obiettivo è quello di rendere la gestione delle identità molto più facile, più sicura e più scalabile di ciò a cui siete abituati. Okta è un servizio cloud che permette agli sviluppatori di creare, modificare e memorizzare in modo sicuro gli account utente e i dati degli account utente, e collegarli con una o più applicazioni. La nostra API ti permette di:

  • Autenticare e autorizzare i tuoi utenti
  • Memorizzare i dati dei tuoi utenti
  • Effettuare login basati su password e social login
  • Proteggere la tua applicazione con l’autenticazione multi-fattore
  • E molto di più! Controlla la nostra documentazione del prodotto

Okta’s OIDC Express Middleware

Per installare il middleware OIDC di Okta per Express, esegui:

npm install @okta/[email protected] --save

Poi nel file server.js, si crea un’istanza del middleware con alcune opzioni di configurazione in modo che Okta sappia come connettersi alla vostra applicazione 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'});

Avrete anche bisogno di dire a Express di usare il router middleware OIDC invece del router di default.

app.use(oidc.router);

Poi lo si usa come qualsiasi altro middleware:

app.get('/protected', oidc.ensureAuthenticated(), (req, res) => { res.send('Top Secret');});

La funzione oidc.ensureAuthenticated() è un middleware nella libreria Okta. Esegue una funzione per vedere se l’utente corrente è loggato. Se lo è, chiama next() per lasciare che la funzione app.get() continui a gestire la richiesta. Se non lo sono, rimanda indietro una HTTP 401 (Unauthorized) risposta.

L’ordine dei middleware è importante

Quando una richiesta viene ricevuta da Express, ogni middleware che corrisponde alla richiesta viene eseguito nell’ordine in cui è stato inizializzato fino a quando non c’è un’azione terminante (come una risposta che viene inviata).

Flusso del middleware

Quindi se si verifica un errore, tutti i middleware che sono destinati a gestire gli errori saranno chiamati in ordine fino a quando uno di essi non chiamerà la next() chiamata di funzione.

Gestione degli errori in Express Middleware

Express ha un gestore di errori predefinito incorporato che viene inserito alla fine della pipeline del middleware che gestisce qualsiasi errore non gestito che possa essersi verificato nella pipeline. La sua firma aggiunge un parametro di errore ai parametri standard di request, response e next. La firma di base assomiglia a questa:

app.use((err, req, res, next) => { // middleware functionality here})

Per chiamare un middleware di gestione degli errori, è sufficiente passare l’errore a next(), come questo:

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); }});

In questo caso, il middleware di gestione degli errori alla fine della pipeline gestirà l’errore. Potreste anche notare che ho controllato la proprietà res.headersSent. Questa controlla solo per vedere se la risposta ha già inviato le intestazioni al client. Se non l’ha fatto, invia uno stato HTTP 500 e il messaggio di errore al client. Si può anche concatenare il middleware di gestione degli errori. Questo è comune per gestire diversi tipi di errori in modi diversi. Per esempio:

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'); }});

In questo caso, il middleware controlla se è stato lanciato un errore 404 (non trovato). Se è così, rende la pagina del template ‘NotFound’ e poi passa l’errore al prossimo elemento del middleware. Il prossimo middleware controlla se è stato lanciato un errore 304 (non autorizzato). Se lo era, rende la pagina ‘Unauthorized’ e passa l’errore al prossimo middleware nella pipeline. Infine il gestore dell’errore “catch all” registra solo l’errore e se non è stata inviata alcuna risposta, invia il httpStatusCode dell’errore (o uno stato HTTP 500 se non è stato fornito) e rende il template ‘UnknownError’.

Impara di più su Express Middleware

Per istruzioni dettagliate sulla configurazione del middleware Okta OIDC, puoi seguire il ExpressJS Quickstart.

C’è anche una lista di middleware Express ufficialmente supportati in questo repo GitHub che puoi provare e scavare per saperne di più

Infine, se sei interessato ad imparare di più su come usare Okta, c’è un Okta Node SDK per implementare più funzionalità di gestione degli utenti nella tua applicazione.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *