Axios Interceptor als HTTP-Middleware: Ein mentales Modell für React-Apps
Verstehen, wie Axios-Request- und Response-Interceptors als Middleware funktionieren, was in jedem von ihnen enthalten sein sollte, sowie wie sie die Auth-Header und die Handhabung von 401-Fehlern in React zentralisieren.
Quellen: Axios Interceptors Explained: The Missing Piece Every Frontend Developer Should Learn.
In einer jungen React-Codebasis neigen API-Aufrufe dazu, sich Schritt für Schritt durch Kopieren und Einfügen zu vermehren: Jeder Anfrage wird ein eigener Authorization-Header hinzugefügt, jede Antwort verfügt über ihre eigene Fehlerbehandlung, und jeder Fehlerfall wiederholt dieselbe Wiederherstellungslogik. Das funktioniert bei drei Endpunkten, wird aber zu einem Wartungsproblem bei dreißig. Axios bietet eine integrierte Lösung in Form von Interceptoren – sobald man sie als Middleware für den HTTP-Verkehr betrachtet, wird klar, was in sie verschoben und was in den Komponenten belassen werden sollte.
Die Duplikation, die Interceptoren beseitigen
Ein typischer Ausgangspunkt ist eine Anfrage an Benutzer, bei der ein Token direkt mitgeschickt wird:
axios.get("/users", {
headers: {
Authorization: `Bearer ${token}`,
},
});
Ein zweiter Endpunkt wiederholt den Header-Block wortwörtlich:
axios.get("/products", {
headers: {
Authorization: `Bearer ${token}`,
},
});
Auch eine Schreiboperation macht dasselbe, wobei der Payload in der Mitte steht:
axios.post("/orders", orderData, {
headers: {
Authorization: `Bearer ${token}`,
},
});
Zu Beginn sieht nichts davon schädlich aus. Mit wachsendem Projekt benötigt jede Anfrage ein Token, jede Antwort eine Fehlerbehandlung und jedes Versagen dieselbe Reaktion. Genau für diese durchgängigen Aufgaben dienen Interceptor-Objekte.
Was ein Interceptor tatsächlich ist
Ein Interceptor ist eine Funktion, die Axios bei jeder ausgehenden Anfrage vor dem Senden oder bei jeder eingehenden Antwort, bevor Ihr Code sie erhält, ausführt – ein Kontrollpunkt, an dem Sie den Datenverkehr überprüfen oder ändern können. Die Antwort erhält ebenfalls einen eigenen Kontrollpunkt, bevor sie die Promise erfüllt, auf die Ihr React-Code wartet.
Der gesamte Ablauf beginnt in Ihrer Anwendung:
React Application
und setzt sich über beide Kontrollpunkte fort, bevor er wieder dorthin zurückkehrt:
↓Request Interceptor↓Axios↓Server↓Response Interceptor↓React Application
Ihre Komponenten kommunizieren niemals direkt mit dem Server; alles fließt durch die Kette, was sie zum natürlichen Ort für ein einheitliches Verhalten macht.
Middleware – erklärt am Empfang
Falls Sie bereits Express- oder Koa-Code geschrieben haben, ist das Konzept Ihnen vertraut: es handelt sich um Middleware. Andernfalls denken Sie an einen Bürokomplex. Man geht nicht direkt ins Büro – der Weg beginnt bei Ihnen:
You
und man passiert auf dem Weg dorthin zwei Stationen:
↓Security Check↓Reception↓Office
Die Sicherheit überprüft Ihren Ausweis, die Rezeption bestätigt Ihren Termin – erst danach erreichen Sie das Büro. Keine dieser Stationen ist das Endziel; jede unterbricht den Weg, um zusätzliche Arbeiten durchzuführen, bevor man weitergehen darf oder abgewiesen wird.
Middleware ist jede Schicht zwischen zwei Systemen, die Arbeiten vorantreibt, bevor der Anfrage weitergeleitet wird. Ersetzen Sie das Büro durch einen Server, ausgehend von der React-App:
React App
und die Interceptor-Schicht ist die Schicht dazwischen:
↓Middleware (Interceptor)↓Server
Anders ausgedrückt sind Axios-Interceptors Middleware für HTTP-Anfragen und -Antworten auf der Client-Seite.
Anfrage- und Antwort-Interceptors
Axios bietet zwei Arten davon, die mit axios.interceptors.request.use(...) und axios.interceptors.response.use(...) registriert werden. Jeder Aufruf nimmt einen Erfolgshandler sowie optionalen Fehlerhandler entgegen und gibt eine ID zurück, die später an eject übergeben werden kann, um den Interceptor zu entfernen.
Anfrage-Interceptors
Sie laufen vor dem Senden einer Anfrage ab. Die Komponente führt den Aufruf aus:
Component
und der Interceptor erkennt ihn auf dem Weg nach draußen:
↓Request Interceptor↓Server
Typische Aufgaben für einen Anfrage-Interceptor:
- Hinzufügen von Authentifizierungs-Token
- Einstellen benutzerdefinierter Header wie Lokalisation oder Anfrage-ID
- Anpassen des Anfragedatensatzes
- Protokollieren des ausgehenden Verkehrs
Der Handler erhält die Anfragekonfiguration und muss sie (oder eine Promise dafür) zurückgeben, damit die Anfrage fortgesetzt werden kann; das Vergessen dessen ist der klassische Anfängerfehler.
Antwort-Interceptoren
Diese laufen nachdem der Server geantwortet hat, aber bevor Ihre Anwendung das Ergebnis erhält. Die Antwort kommt vom Server:
Server
und gelangt auf dem Weg zur Komponente durch den Interceptor:
↓Response Interceptor↓Component
Häufige Aufgaben hier:
- Fehler an einem Ort handhaben
- Antwortdaten transformieren oder auflösen
- Antworten protokollieren
- Fehlgeschlagene Anfragen erneut versuchen
- Den Benutzer umleiten, z. B. auf eine Anmeldeseite
Der Erfolgs-Handler kümmert sich um 2xx-Antworten und der Fehler-Handler um den Rest. Wenn keine Wiederherstellung möglich ist, sollte er eine abgelehnte Promise zurückgeben, damit die Aufrufer den Fehler weiterhin sehen können.
Was geschieht hinter einer einzelnen Aufruf
Eine Einzeilenanfrage scheint darauf hinzudeuten, dass Axios sie sofort absetzt:
axios.get("/users");
Intern erfolgt derselbe Aufruf:
axios.get("/users")
er durchläuft jedoch einen längeren Prozess, bevor Ihre .then()-Funktion ausgeführt wird:
↓Request Interceptor↓HTTP Request↓Server↓HTTP Response↓Response Interceptor↓.then()↓Your Component
Die gemeinsame Logik wird bei jedem Aufruf ausgeführt, ohne dass der Aufrufsort davon weiß.
Warum die Zentralisierung wichtig wird, je größer die Anwendung wird
Stellen Sie sich ein Projekt vor, in dem hundert API-Aufrufe auf manuelle Weise implementiert sind. Die Benutzeranfrage enthält den Header:
axios.get("/users", {
headers: {
Authorization: `Bearer ${token}`,
},
});
Auch die Anfrage für Produkte enthält ihn:
axios.get("/products", {
headers: {
Authorization: `Bearer ${token}`,
},
});
Ebenso jeder Schreibvorgang, wie zum Beispiel die Erstellung einer Bestellung:
axios.post("/orders", data, {
headers: {
Authorization: `Bearer ${token}`,
},
});
Falls sich das Backend sein Authentifizierungsverfahren ändert, müssen Sie alle Aufrufe aktualisieren – jeder übersehene Aufruf wird zu einem Fehler. Ein Interceptor speichert diese Logik einmal, und Axios wendet sie überall an. Registrieren Sie ihn bei einer mit axios.create() erstellten Instanz, damit er auf Ihre API und nicht auf URLs Dritter angewendet wird.
Anwendungsfall: Automatische Hinzufügung des Zugriffstokens
Dies ist der häufigste Grund, Interceptors zu verwenden. Nach dem Anmelden speichert die App ein Zugriffstoken, das von jedem geschützten Endpunkt erwartet wird. Der Aufruf beginnt als einfacher Request:
API Call
und der Interceptor fügt den Header vor dem Erreichen des Servers hinzu:
↓Request Interceptor↓Authorization Header Added↓Server
Jeder Request enthält nun das Token. Lesen Sie es innerhalb des Interceptors bei jedem Request statt nur einmal beim Start – andernfalls wird ein aktualisiertes Token niemals erfasst.
Anwendungsfall: Globale Handhabung von 401-Antworten
Access-Tokens verfallen. Wenn das geschieht, antwortet der Server mit:
401 Unauthorized
Ohne Interceptor benötigt jeder Aufruf dazu einen eigenen Codeabschnitt. Der erste API-Aufruf:
API 1
wird von derselben Verarbeitung gefolgt, die für jeden anderen Endpunkt dupliziert wird:
↓401 HandlingAPI 2↓401 HandlingAPI 3↓401 Handling
Ein Response-Interceptor kann hingegen alle 401 Unauthorized-Fehler an einem Ort abfangen. Wenn er einen solchen Fehler erkennt:
401 Received
kann er das Access-Token erneuern, die ursprüngliche Anfrage erneut senden und die erfolgreiche Antwort an den Aufrufer zurückgeben:
↓Refresh Access Token↓Retry Original Request↓Return Successful Response
Der Benutzer bemerkt das abgelaufene Token niemals. Zwei Schutzmaßnahmen sind wichtig: Markieren Sie eine wiederholte Anfrage in deren Konfiguration, damit ein zweiter 401-Fehler nicht endlos weiterläuft, und sorgen Sie dafür, dass gleichzeitige Fehler eine einzige Aktualisierungsanfrage teilen – dazu wird in „Warum gleichzeitige 401-Fehler Benutzer ausloggen und die Lösung mit der einzigen Aktualisierungsanfrage“ erklärt.
Wann Interceptor nicht sinnvoll sind
Eine kleine Anwendung mit zwei oder drei API-Aufrufen profitiert nur wenig von der Umleitung, und verstecktes Verhalten kann Leser überraschen, die nicht erkennen können, woher ein Header stammt. Bei Dutzenden oder Hunderten von Anfragen sind Interceptor eine der besten Möglichkeiten, die Handhabung der Anfragen sauber und konsistent zu halten. Verwenden Sie sie für querverteilte Anforderungen; endpoint-spezifische Logik gehört zum jeweiligen Endpoint.
Zusammenfassung
Die Übermittlung von Anfragen ist nur ein kleiner Teil der Aufgaben einer HTTP-Schicht. Authentifizierung, Fehlerbehandlung, Protokollierung, Formgebung der Antworten sowie das Erneuern von Tokens sind durchgängige Aspekte, und Interceptor-Objekte bieten für jeden davon einen zentralen Ort.
- Betrachten Sie Interceptor-Objekte als Client-seitiges Middleware: Anfragen-Interceptor-Objekte gestalten das, was gesendet wird, Antwort-Interceptor-Objekte gestalten das, was empfangen wird.
- Geben Sie stets die Konfiguration der Anfragen-Interceptor-Objekte zurück und werfen Sie unbehebbare Fehler aus den Antwort-Interceptor-Objekten erneut auf.
- Fügen Sie sie einer
axios.create()-Instanz hinzu, die auf Ihre API beschränkt ist. - Schützen Sie die Wiederholungslogik bei 401-Fehlern vor Schleifen und gleichzeitigen Erneuerungen.