Articles

Construire et comprendre le middleware Express à travers des exemples

Posted on

Si vous avez fait un développement Node significatif au cours des sept ou huit dernières années, vous avez probablement utilisé Express pour construire un serveur web à un moment donné. Bien que vous puissiez créer un serveur dans Node sans utiliser de bibliothèque, cela ne vous donne pas beaucoup de choses hors de la boîte et peut être assez lourd pour ajouter des fonctionnalités. Express est une bibliothèque de serveur minimaliste, « sans opinion », et est devenue la norme de facto pour la création d’applications Web dans Node. Pour comprendre Express, vous devez comprendre Express Middleware.

Qu’est-ce que Express Middleware?

Middleware signifie littéralement tout ce que vous mettez au milieu d’une couche du logiciel et d’une autre. Les intergiciels Express sont des fonctions qui s’exécutent pendant le cycle de vie d’une requête au serveur Express. Chaque intergiciel a accès au HTTP request et response pour chaque route (ou chemin) à laquelle il est attaché. En fait, Express lui-même est entièrement composé de fonctions d’intergiciel. En outre, l’intergiciel peut soit mettre fin à la requête HTTP, soit la transmettre à une autre fonction intergicielle à l’aide de next (nous y reviendrons bientôt). Ce  » chaînage  » d’intergiciels vous permet de compartimenter votre code et de créer des intergiciels réutilisables.

Dans cet article, j’expliquerai ce qu’est un intergiciel, pourquoi vous l’utiliseriez, comment utiliser les intergiciels Express existants et comment écrire votre propre intergiciel pour Express.

Requêtes pour écrire un intergiciel Express

Il y a quelques choses que vous devrez installer pour créer, utiliser et tester les intergiciels Express. Tout d’abord, vous aurez besoin de Node et de NPM. Pour vous assurer que vous les avez installés, vous pouvez exécuter:

npm -v && node -v

Vous devriez voir les versions de Node et NPM que vous avez installées. Si vous obtenez une erreur, vous devez installer Node. J’utilise la dernière version des deux au moment de cet article, à savoir Node 10.9.0 et NPM 6.4.1, mais tous les exemples devraient fonctionner avec les versions 8+ de Node et les versions 5+ de NPM.

J’utiliserai également la version 4.x d’Express. C’est important car des changements majeurs ont été apportés de la version 3.x à la 4.x.

Il sera également utile d’avoir Postman installé pour tester les routes utilisant tout verbe HTTP autre que GET.

Moyen logiciel Express : Les bases

Pour commencer, vous allez utiliser le plus basique des intergiciels intégrés d’Express. Cela vous donnera l’occasion de voir comment les intergiciels sont utilisés et comment les intergiciels Express sont structurés.

Créer un nouveau projet et npm init le…

npm initnpm install express --save

Créer server.js et coller le code suivant :

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

Lancer le serveur via node server.js, accéder à http://localhost:3000, et vous devriez voir s’imprimer « Welcome Home » dans votre navigateur.

La fonction app.get() est un intergiciel de niveau application. Vous remarquerez que les paramètres passés à la méthode sont reqres, et next. Il s’agit de la requête entrante, de la réponse en cours d’écriture, et d’une méthode à appeler pour passer l’appel à la fonction middleware suivante une fois que le middleware actuel est terminé. Dans ce cas, une fois la réponse envoyée, la fonction se termine. Vous pourriez également enchaîner d’autres intergiciels ici en appelant la méthode next().

Regardons quelques autres exemples des différents types d’intergiciels.

Express Exemple d’intergiciel de journalisation des requêtes

Dans Express, vous pouvez configurer un intergiciel pour qu’il soit un intergiciel  » global  » ; ce qui signifie qu’il sera appelé pour chaque requête entrante.

Changez le contenu de server.js en :

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

Cette fois, lorsque vous allez sur http://localhost:3000, vous devriez voir la même chose dans la fenêtre de votre navigateur, mais de retour dans la fenêtre de la console, vous verrez la sortie de l’objet de requête entrant.

Le middleware déconnecte l’objet de requête et appelle ensuite next(). L’intergiciel suivant dans le pipeline traite la requête get vers l’URL racine et renvoie la réponse textuelle. L’utilisation de app.use() signifie que cet intergiciel sera appelé pour chaque appel à l’application.

Exemple de type de contenu de requête express restreint

En plus d’exécuter l’intergiciel pour tous les appels, vous pourriez également spécifier de n’exécuter l’intergiciel que pour des appels spécifiques.

Changez à nouveau le fichier server.js pour :

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

Cette fois, démarrez le serveur en exécutant :

node server.js

Pour tester cela, ouvrez Postman et créez une requête post vers http://localhost:3000. Ne définissez aucun en-tête et exécutez la requête. Vous obtiendrez le message « Server requires application/json ».

Maintenant, revenez en arrière et ajoutez l’en-tête Content-Type avec une valeur de application/json et exécutez à nouveau la requête. Vous obtiendrez le message « You sent JSON » en retour du serveur.

Cet appel de méthode app.post() ajoute la fonction middleware requireJsonContent() pour s’assurer que la charge utile de la requête entrante a une valeur d’en-tête Content-Type fixée à application/json. S’il ne passe pas le contrôle, une réponse d’erreur est envoyée. Si c’est le cas, la requête est alors transmise à l’intergiciel suivant dans la chaîne via la méthode next().

L’intergiciel express tiers

Vous avez construit quelques intergiciels personnalisés jusqu’à présent, mais il existe de nombreux paquets déjà construits pour faire les choses que vous pourriez normalement vouloir faire. En fait, vous avez utilisé la bibliothèque middleware de routage simple en utilisant les fonctions middleware app.get() ou app.post(). Il existe des milliers de bibliothèques de middleware pour faire des choses comme l’analyse des données entrantes, le routage et l’autorisation.

Okta a un middleware Express pour la sécurité OIDC que je vais vous montrer pour démontrer l’utilisation de bibliothèques de middleware tierces.

Pourquoi Okta pour les applications Express

Au sein d’Okta, notre objectif est de rendre la gestion des identités beaucoup plus facile, plus sûre et plus évolutive que ce à quoi vous êtes habitué. Okta est un service cloud qui permet aux développeurs de créer, modifier et stocker en toute sécurité les comptes et les données des comptes utilisateurs, et de les connecter à une ou plusieurs applications. Notre API vous permet de :

  • Authentifier et autoriser vos utilisateurs
  • Stocker des données sur vos utilisateurs
  • Préparer une connexion basée sur un mot de passe et une connexion sociale
  • Sécuriser votre application avec une authentification multi-facteurs
  • Et bien plus encore ! Consultez notre documentation produit

Milieu OIDC Express d’Okta

Pour installer le milieu OIDC d’Okta pour Express, exécutez :

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

Puis dans le fichier server.js, vous créez une instance si le middleware avec quelques options de configuration afin qu’Okta sache comment se connecter à votre application 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'});

Vous devrez également indiquer à Express d’utiliser le routeur du middleware OIDC au lieu du routeur par défaut.

app.use(oidc.router);

Puis vous l’utilisez comme tout autre middleware :

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

La oidc.ensureAuthenticated() fonction est un middleware de la bibliothèque Okta. Elle exécute une fonction pour voir si l’utilisateur actuel est connecté. S’il l’est, elle appelle next() pour laisser la fonction app.get() continuer à traiter la requête. Si ce n’est pas le cas, elle renvoie une HTTP 401 (Unauthorized) réponse.

L’ordre des intergiciels est important

Lorsqu’une requête est reçue par Express, chaque intergiciel qui correspond à la requête est exécuté dans l’ordre où il est initialisé jusqu’à ce qu’il y ait une action de terminaison (comme l’envoi d’une réponse).

Flux d'intergiciels

Si une erreur se produit, tous les intergiciels censés gérer les erreurs seront appelés dans l’ordre jusqu’à ce que l’un d’eux n’appelle pas l’appel de fonction next().

Gestion des erreurs dans les intergiciels Express

Express dispose d’un gestionnaire d’erreurs intégré par défaut qui est inséré à la fin du pipeline d’intergiciels et qui gère toutes les erreurs non gérées qui ont pu se produire dans le pipeline. Sa signature ajoute un paramètre d’erreur aux paramètres standard de la demande, de la réponse et de next. La signature de base ressemble à ceci:

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

Pour appeler un intergiciel de gestion des erreurs, il suffit de passer l’erreur à next(), comme ceci :

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

Dans ce cas, le middleware de gestion des erreurs à la fin du pipeline traitera l’erreur. Vous pouvez également remarquer que j’ai vérifié la propriété res.headersSent. Cette propriété vérifie simplement si la réponse a déjà envoyé les en-têtes au client. Si ce n’est pas le cas, elle envoie un état HTTP 500 et le message d’erreur au client. Vous pouvez également enchaîner des intergiciels de gestion des erreurs. Ceci est courant pour gérer différents types d’erreurs de différentes manières. Par exemple :

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

Dans ce cas, le middleware vérifie si une erreur 404 (not found) a été lancée. Si c’est le cas, il rend la page de modèle ‘NotFound’ et transmet ensuite l’erreur à l’élément suivant de l’intergiciel. L’intergiciel suivant vérifie si une erreur 304 (non autorisé) a été générée. Si c’est le cas, il affiche la page ‘Unauthorized’ et transmet l’erreur à l’intergiciel suivant dans le pipeline. Enfin, le gestionnaire d’erreur  » catch all  » se contente d’enregistrer l’erreur et si aucune réponse n’a été envoyée, il envoie le httpStatusCode de l’erreur (ou un statut HTTP 500 si aucun n’est fourni) et rend le modèle ‘UnknownError’.

En savoir plus sur le middleware Express

Pour des instructions détaillées sur la configuration du middleware Okta OIDC, vous pouvez suivre le ExpressJS Quickstart.

Il existe également une liste des intergiciels Express officiellement pris en charge dans ce repo GitHub que vous pouvez essayer et creuser pour en savoir plus

Enfin, si vous souhaitez en savoir plus sur la façon d’utiliser Okta, il existe un SDK Okta Node pour mettre en œuvre davantage de fonctionnalités de gestion des utilisateurs dans votre application.

La mise en place d’un système de gestion des utilisateurs est un élément essentiel de la gestion des utilisateurs.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *