Startseite / Artikel

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.

Verlag: REACTAPP.TOP6 Min. Lesezeit1357 Wörter

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.