Les intercepteurs Axios en tant que middleware HTTP : un modèle mental pour les applications React
Comprendre le fonctionnement des intercepteurs de requête et de réponse d’Axios en tant que middleware, ce qui doit figurer dans chacun d’eux, ainsi que la manière dont ils centralisent les en-têtes d’authentification et le traitement des erreurs 401 dans React.
Sources: Axios Interceptors Explained: The Missing Piece Every Frontend Developer Should Learn.
Dans une base de code React jeune, les appels API ont tendance à s’accumuler petit à petit par copier-coller : chaque requête crée son propre en-tête Authorization, chaque réponse dispose de sa propre gestion des erreurs, et chaque scénario d’échec répète la même logique de récupération. Cela fonctionne pour trois points d’entrée, mais devient un problème de maintenance dès qu’il y en a trente. Axios propose une solution intégrée via les intercepteurs ; une fois que l’on les considère comme des middleware pour le trafic HTTP, il devient clair ce qu’il convient de déplacer vers eux et ce qu’il faut laisser dans les composants.
La duplication éliminée par les intercepteurs
Un point de départ typique est une requête concernant des utilisateurs qui inclut un token porteur directement dans le message :
axios.get("/users", {
headers: {
Authorization: `Bearer ${token}`,
},
});
Un deuxième point d’entrée répète mot pour mot ce bloc d’en-têtes :
axios.get("/products", {
headers: {
Authorization: `Bearer ${token}`,
},
});
Et une opération d’écriture fait de même, avec le chargement dans l’intervalle :
axios.post("/orders", orderData, {
headers: {
Authorization: `Bearer ${token}`,
},
});
D’abord, rien de tout cela ne semble nocif. À mesure que le projet se développe, chaque requête nécessite un jeton, chaque réponse exige une gestion des erreurs, et chaque échec doit faire l’objet de la même réaction. C’est précisément pour ce type de tâches transversales que servent les intercepteurs.
Qu’est-ce qu’un intercepteur en réalité ?
Un intercepteur est une fonction que Axios exécute sur chaque requête sortante avant qu’elle ne soit envoyée, ou sur chaque réponse entrante avant que votre code ne la reçoive : c’est un point de contrôle où vous pouvez inspecter ou modifier le trafic. La réponse passe également par son propre point de contrôle avant que la promesse attendue par votre code React ne soit résolue.
Le parcours global commence dans votre application :
React Application
et se poursuit à travers ces deux points de contrôle avant de revenir à elle :
↓Request Interceptor↓Axios↓Server↓Response Interceptor↓React Application
Vos composants ne communiquent jamais directement avec le serveur ; tout passe par la chaîne, ce qui en fait l’endroit idéal pour assurer un comportement uniforme.
Le middleware, expliqué à l’aide d’un bureau d’accueil
Si vous avez écrit du code Express ou Koa, ce concept vous est familier : il s’agit de middleware. Sinon, imaginez que vous visitez un immeuble de bureaux. Vous n’entrez pas directement dans les bureaux. Le parcours commence par vous :
You
et passe par deux étapes en chemin :
↓Security Check↓Reception↓Office
La sécurité vérifie votre identité, la réception confirme votre rendez-vous, et ce n’est qu’alors que vous atteignez les bureaux. Aucune de ces étapes n’est la destination finale ; chacune vous intercepte pour effectuer des vérifications supplémentaires, puis vous laisse continuer ou vous refuse l’accès.
Le middleware est toute couche située entre deux systèmes qui effectue des tâches avant que la requête ne progresse. Remplacez les bureaux par un serveur, en partant de l’application React :
React App
et l’intercepteur est la couche intermédiaire :
↓Middleware (Interceptor)↓Server
En d’autres termes, les intercepteurs d’Axios sont des middleware pour les requêtes et réponses HTTP côté client.
Intercepteurs de requête et de réponse
Axios propose deux types d’intercepteurs, enregistrés via axios.interceptors.request.use(...) et axios.interceptors.response.use(...). Chaque appel prend un gestionnaire de succès et un gestionnaire d’erreur optionnel, puis retourne une identifiant que l’on peut par la suite transmettre à eject pour le supprimer.
Intercepteurs de requête
Ces derniers s’exécutent avant l’envoi d’une requête. Le composant effectue l’appel :
Component
et l’intercepteur le détecte au moment de son envoi :
↓Request Interceptor↓Server
Tâches typiques d’un intercepteur de requête :
- ajout de tokens d’authentification
- mise en place de en-têtes personnalisés tels qu’une région linguistique ou un identifiant de requête
- modification du payload de la requête
- journalisation du trafic sortant
Le gestionnaire reçoit la configuration de la requête et doit la retourner (ou une promesse la concernant) pour que la requête puisse continuer ; oublier cela est l’erreur classique des débutants.
Intercepteurs de réponse
Ces derniers s’exécutent après que le serveur a répondu, mais avant que votre application ne reçoive le résultat. La réponse provient du serveur :
Server
et passe par l’intercepteur en se dirigeant vers le composant :
↓Response Interceptor↓Component
Responsabilités courantes ici :
- Gérer les erreurs en un seul endroit
- Transformer ou déballer les données de réponse
- Journaliser les réponses
- Réessayer les requêtes échouées
- Rediriger l’utilisateur, par exemple vers une page de connexion
Le gestionnaire de succès gère les réponses 2xx et le gestionnaire d’erreur le reste. S’il ne peut pas récupérer la situation, il doit retourner une promesse rejetée afin que les appels suivants puissent encore détecter l’échec.
Que se passe-t-il derrière une seule appel
Une requête en une seule ligne donne l’impression qu’Axios la lance immédiatement :
axios.get("/users");
En interne, c’est la même appel :
axios.get("/users")
qui traverse un pipeline plus long avant que votre .then() ne s’exécute :
↓Request Interceptor↓HTTP Request↓Server↓HTTP Response↓Response Interceptor↓.then()↓Your Component
La logique partagée s’exécute à chaque appel sans que le point d’appel ne soit au courant.
Pourquoi la centralisation est importante à mesure que l’application grandit
Imaginez un projet avec cent appels API écrits manuellement. La requête de l’utilisateur contient l’en-tête suivant :
axios.get("/users", {
headers: {
Authorization: `Bearer ${token}`,
},
});
De même pour la requête relative aux produits :
axios.get("/products", {
headers: {
Authorization: `Bearer ${token}`,
},
});
Et de même pour toute opération d’écriture, comme la création d’une commande :
axios.post("/orders", data, {
headers: {
Authorization: `Bearer ${token}`,
},
});
Si le backend modifie son schéma d’authentification, vous devez mettre à jour chaque appel, et chacun qui est omis devient une erreur. Un intercepteur stocke cette logique une seule fois et Axios l’applique partout. Inscrivez-le sur une instance créée avec axios.create(), afin qu’il s’applique à votre API et non aux URLs de tiers.
Cas d’usage : ajout automatique du jeton d’accès
C’est la raison la plus courante d’adopter des intercepteurs. Après connexion, l’application stocke un jeton d’accès que chaque point de terminaison protégé attend. L’appel commence comme une requête ordinaire :
API Call
et l’intercepteur ajoute l’en-tête avant que la requête n’atteigne le serveur :
↓Request Interceptor↓Authorization Header Added↓Server
Chaque requête contient désormais le jeton. Lisez-le à l’intérieur de l’intercepteur pour chaque requête plutôt qu’une seule fois au démarrage, sinon un jeton mis à jour ne sera jamais détecté.
Cas d’usage : gestion globale des réponses 401
Les tokens d’accès expirent. Lorsque cela se produit, le serveur répond comme suit :
401 Unauthorized
En l’absence d’intercepteurs, chaque appel nécessite un traitement distinct pour ce cas. Le premier appel API :
API 1
est suivi du même traitement, dupliqué pour chaque autre point de terminaison :
↓401 HandlingAPI 2↓401 HandlingAPI 3↓401 Handling
Un intercepteur de réponse peut quant à lui capturer tous les 401 Unauthorized en un seul endroit. Lorsqu’il en détecte un :
401 Received
il peut renouveler le token d’accès, réexécuter la demande initiale et transmettre la réponse réussie au demandeur :
↓Refresh Access Token↓Retry Original Request↓Return Successful Response
L’utilisateur ne remarque jamais le jeton expiré. Deux mesures de précaution sont essentielles : signaler une requête réutilisée dans sa configuration afin qu’un second 401 ne crée pas une boucle infinie, et faire en sorte que les échecs simultanés partagent une seule demande de mise à jour, comme expliqué dans pourquoi les 401 simultanés déconnectent les utilisateurs et la solution de mise à jour unique.
Lorsque les intercepteurs ne sont pas utiles
En résumé
L’envoi de requêtes n’est qu’une petite partie des tâches d’une couche HTTP. L’authentification, la gestion des erreurs, le journalisation, la mise en forme des réponses et le renouvellement des tokens sont des fonctions transversales, et les intercepteurs leur fournissent un point d’organisation unique.
- Considérez les intercepteurs comme du middleware côté client : les intercepteurs de requête modifient ce qui est envoyé, tandis que les intercepteurs de réponse modifient ce qui arrive.
- Retournez toujours la configuration provenant des intercepteurs de requête et relancez les erreurs irréparables depuis les intercepteurs de réponse.
- Attachez-les à une instance
axios.create()spécifique à votre API. - Protégez la logique de tentative en cas d’erreur 401 contre les boucles et les renouvellements simultanés.