Gérer un cycle de vie des requêtes avec un middleware

Effectué

Tailwind Traders souhaite sécuriser son API web. Dans certains cas, lorsqu’une requête atteint une application web, vous devez peut-être vérifier :

  • Authentification : qui est l’utilisateur
  • Autorisation : ce que l’utilisateur est autorisé à voir ou à faire

Étapes de requête

Gérer une requête est un processus composé de plusieurs étapes. Si l’utilisateur doit être connecté pour gérer une ressource, les étapes seront les suivantes :

  1. Prétraitement : étape facultative d’exécution du code avant le traitement de la demande.

    Un exemple consiste à déterminer que l’utilisateur a envoyé les informations d’identification appropriées via un en-tête de requête. Si les informations d’identification sont valides, envoyez la requête à l’étape suivante. Si la journalisation échoue, le serveur retourne une réponse HTTP 401.

  2. Traitement : traitez la demande, par exemple, parler à un type de source de données, comme une base de données ou un point de terminaison d’API.

    Cette étape renvoie la ressource, à condition que la requête demande la ressource correctement.

  3. Post-traitement : étape facultative d’exécution du code une fois la demande terminée.

    Par exemple, la journalisation des résultats à des fins de supervision.

Le framework Express a une prise en charge intégrée de la gestion d’une requête de cette façon. Pour exécuter un prétraitement ou un post-traitement pour une requête, mettez en œuvre la méthode use() sur votre objet app Express avec la syntaxe suivante :

app.use((req, res, next) => {})

La méthode transmise à la méthode use() a les paramètres suivants :

  • req : requête entrante qui contient des en-têtes de requête et l’URL appelante. Il peut également contenir un corps de données si le client a envoyé des données avec leur requête.
  • res : flux de réponse à utiliser pour écrire des informations telles que des en-têtes et des données que vous souhaitez renvoyer au client appelant.
  • next : la fonction d’intergiciel next dans la pile. Si la fonction next() n’est pas appelée, le traitement de la requête s’arrête. Si la requête a réussi, vous pouvez appeler next() pour apporter des modifications à la réponse ou consigner les résultats.

Un pipeline de requête

Si vous avez des itinéraires qui tirent parti du prétraitement ou post-traitement d’un intergiciel, configurez les fonctions dans le fichier de code source de sorte que :

  • L’intergiciel qui doit s’exécuter avant la requête (pré-requête) est défini avant la demande réelle.
  • L’intergiciel qui doit s’exécuter après la requête (post requête) est défini après la demande réelle.

Prenons cet exemple :

app.use((req, res, next) => {
  // Pre request
})
app.get('/protected-resource', () => {
  // Handle the actual request
})
app.use((req, res, next) => {
  // Post request
})

app.get('/login', () => {})

Vous pouvez aussi exécuter le code de l’intergiciel pré-requête comme argument de la gestion de la requête, comme suit :

app.get(
  '/<some route>',
 () => {
   // Pre request middleware
 }, () => {
   // Handle the actual request
 })

L’ordre des fonctions d’intergiciel dans Express.js est crucial, car ces fonctions sont exécutées de manière séquentielle, dans l’ordre dans lequel elles sont définies dans le code. Cela signifie que si une fonction d’intergiciel est placée après un gestionnaire de routage, elle ne sera pas exécutée pour cet itinéraire.

Meilleures pratiques de gestion des itinéraires

Voici quelques meilleures pratiques pour gérer l’ordre des fonctions d’intergiciel :

  • Placez l’intergiciel global en premier : les fonctions d’intergiciel qui s’appliquent à tous les itinéraires doivent être placées au début de votre code, avant tous les gestionnaires de routage. Vous garantissez ainsi leur exécution pour chaque requête.

  • Ordonnez les intergiciels par spécificité : les fonctions d’intergiciel plus spécifiques doivent être placées après celles plus générales. Ainsi, les fonctions d’intergiciel générales peuvent gérer les tâches communes pour tous les itinéraires, et celles spécifiques gérer les tâches pour des itinéraires spécifiques.

  • Placez l’intergiciel de gestion des erreurs en dernier : les fonctions d’intergiciel composées de quatre arguments sont traitées comme un intergiciel de gestion des erreurs. Elles doivent être placées à la fin de votre pile d’intergiciels, après tous les autres intergiciels et gestionnaires de routage.

Voici un exemple :

const express = require('express');
const app = express();

// Global middleware
app.use((req, res, next) => {
  console.log('This is a global middleware');
  next();
});

// Route handler
app.get('/', (req, res, next) => {
  console.log('This is a route handler');
  next();
});

// Specific middleware
app.use((req, res, next) => {
  console.log('This is a specific middleware');
  next();
});

// Error-handling middleware
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});

app.listen(3000);