Strona główna / Artykuły

Interceptory Axios jako middleware HTTP: model mentalny dla aplikacji React

Zrozum, jak intercepcje żądań i odpowiedzi Axios działają jako middleware, co powinno znajdować się w każdej z nich oraz jak centralizują one nagłówki autoryzacji i obsługę błędu 401 w React.

Wydawca: REACTAPP.TOP6 min czytania1357 słów

Źródła: Axios Interceptors Explained: The Missing Piece Every Frontend Developer Should Learn.

W młodej bazie kodu React wywołania API zazwyczaj rosną krok po kroku poprzez kopiowanie i wklejanie: każda prośba tworzy własny nagłówek Authorization, każda odpowiedź ma własne zarządzanie błędami, a każda ścieżka awarii powtarza tę samą logikę naprawczą. To działa przy trzech punktach końcowych, ale staje się problemem konserwacyjnym przy trzydziestu. Axios oferuje wbudowane rozwiązanie w postaci interceptorów – gdy spojrzymy na nie jako middleware dla ruchu HTTP, staje się jasne, co należy przenieść do nich, a co pozostawić w komponentach.

Duplikacja usuwana przez interceptorów

Typowym punktem wyjścia jest żądanie dotyczące użytkowników, które zawiera token nośny bezpośrednio w treści żądania:

axios.get("/users", {
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

Drugi punkt końcowy powtarza ten sam blok nagłówków słowo w słowo:

axios.get("/products", {
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

A operacja zapisu robi to samo, z treścią żądania umieszczoną pomiędzy nagłówkami:

axios.post("/orders", orderData, {
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

Na pierwszy rzut oka nic z tego nie wygląda na szkodliwe. W miarę rozwoju projektu każde żądanie wymaga tokena, każda odpowiedź wymaga obsługi błędów, a każda awaria wymaga tej samej reakcji. Właśnie do takich zadań służą interceptorzy.

Czym właściwie jest interceptor

Interceptor to funkcja, którą Axios wykonywa dla każdego wysyłanego żądania przed jego wysłaniem lub dla każdej przychodzącej odpowiedzi przed tym, zanim twój kod ją otrzyma – to punkt kontrolny, w którym można sprawdzić lub zmienić ruch sieciowy. Odpowiedź przechodzi przez swój własny punkt kontrolny, zanim spełni obietnicę, na którą czeka kod React.

Ogólna ścieżka zaczyna się od twojej aplikacji:

React Application

i kontynuuje się przez oba punkty kontrolne, zanim wróci do niej:

↓Request Interceptor↓Axios↓Server↓Response Interceptor↓React Application

Twoje komponenty nigdy nie komunikują się bezpośrednio z serwerem; wszystko przechodzi przez tę łańcuchową strukturę, co czyni ją naturalnym miejscem dla jednolitego zachowania.

Middleware, wyjaśnione przy pomocy recepcji

Jeśli pisałeś kod w Express lub Koa, to pojęcie jest ci znane: to właśnie middleware. W przeciwnym razie wyobraź sobie wizytę w budynku biurowym. Nie idziesz prosto do biura – droga zaczyna się od ciebie:

You

i przechodzisz przez dwa etapy po drodze:

↓Security Check↓Reception↓Office

Bezpieczeństwo sprawdza twoją tożsamość, recepcja potwierdza umówione spotkanie, a dopiero wtedy docierasz do biura. Żaden z tych etapów nie jest ostatecznym celem; każdy przerywa ci drogę, aby wykonać dodatkowe czynności, a następnie pozwala ci kontynuować lub odmawia wejścia.

Middleware to każda warstwa pomiędzy dwoma systemami, która wykonuje zadania przed dalszym przetwarzaniem żądania. Zastąp biuro serwerem, zaczynając od aplikacji React:

React App

a warstwą pośredniczącą jest właśnie ta środkowa warstwa:

↓Middleware (Interceptor)↓Server

Innymi słowy, interceptorzy Axios to middleware dla żądań i odpowiedzi HTTP na stronie klienta.

Interceptorzy żądań i odpowiedzi

Axios oferuje dwa rodzaje interceptorów, rejestrowane za pomocą axios.interceptors.request.use(...) i axios.interceptors.response.use(...). Każda funkcja przyjmuje obsługę sukcesu oraz opcjonalną obsługę błędów i zwraca identyfikator, który można później przekazać do eject w celu jego usunięcia.

Interceptorzy żądań

One działają przed wysłaniem żądania. Komponent wywołuje tę funkcję:

Component

a interceptor widzi to w trakcie wysyłania żądania:

↓Request Interceptor↓Server

Typowe zadania interceptora żądań:

  • dodawanie tokenów autoryzacji
  • ustawianie niestandardowych nagłówków, takich jak lokalizacja lub identyfikator żądania
  • modyfikowanie treści żądania
  • rejestracja ruchu wychodzącego

Obsługa żądania otrzymuje konfigurację żądania i musi ją zwrócić (lub obietnicę jej zwrotu), aby żądanie mogło kontynuować; zapomnienie o tym to klasyczny błąd początkujących.

Interceptory odpowiedzi

One działają po tym, jak serwer udzieli odpowiedzi, ale zanim twoja aplikacja otrzyma wynik. Odpowiedź pochodzi od serwera:

Server

i przechodzi przez interceptor w drodze do komponentu:

↓Response Interceptor↓Component

Powszechne zadania w tym przypadku to:

  • obsługa błędów w jednym miejscu
  • przekształcanie lub rozpakowywanie danych odpowiedzi
  • rejestracja odpowiedzi
  • ponawianie nieudanych żądań
  • przekierowanie użytkownika, na przykład na stronę logowania

Obsługa sukcesu zajmuje się odpowiedziami 2xx, a obsługa błędów pozostałymi. Jeśli nie jest możliwe odzyskanie sytuacji, należy zwrócić odrzuconą obietnicę, aby wywołujący nadal mogli zobaczyć błąd.

Co dzieje się za pojedynczym wywołaniem

Zapytanie jednoliniowe sprawia wrażenie, jakby Axios wysyłał je natychmiast:

axios.get("/users");

Wewnętrznie ten sam wywołanie:

axios.get("/users")

przechodzi przez dłuższy łańcuch przetwarzania, zanim zostanie wywołany .then():

↓Request Interceptor↓HTTP Request↓Server↓HTTP Response↓Response Interceptor↓.then()↓Your Component

Logika wspólna jest wykonywana przy każdym wywołaniu, a żadne miejsce wywołania o tym nie wie.

Dlaczego centralizacja jest ważna w miarę rozwoju aplikacji

Wyobraź sobie projekt z setką wywołań API napisanych w sposób ręczny. Zapytanie użytkownika zawiera nagłówek:

axios.get("/users", {
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

Tak samo jest z zapytaniem o produkty:

axios.get("/products", {
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

I tak samo z każdym zapisem, np. tworzeniem zamówienia:

axios.post("/orders", data, {
  headers: {
    Authorization: `Bearer ${token}`,
  },
});

Jeśli serwer zmieni swój schemat autoryzacji, musisz zaktualizować każde żądanie, a każde pominięte staje się błędem. Interceptor przechowuje tę logikę raz, a Axios stosuje ją we wszystkich miejscach. Zarejestruj go w instancji utworzonej za pomocą axios.create(), aby stosowała się ona do twojej API, a nie do adresów URL stron trzecich.

Przypadek użycia: automatyczne dołączanie tokena dostępu

To najczęstszy powód stosowania interceptorów. Po zalogowaniu aplikacja przechowuje token dostępu, którego oczekuje każdy zabezpieczony punkt końcowy. Żądanie rozpoczyna się jako zwykłe żądanie:

API Call

a interceptor dodaje nagłówek przed tym, jak dotrze ono do serwera:

↓Request Interceptor↓Authorization Header Added↓Server

Każde żądanie teraz zawiera ten token. Odczytaj go w intersecieorze przy każdym żądaniu, a nie raz podczas uruchamiania, w przeciwnym razie odświeżony token nigdy nie zostanie przyjęty.

Przypadek użycia: globalne obsługiwanie odpowiedzi 401

Tokeny dostępu wygasają. Gdy tak się stanie, serwer odpowiada w następujący sposób:

401 Unauthorized

Bez interceptorów każda wywołanie wymaga osobnego rozwiązania w tym celu. Pierwsze wywołanie API:

API 1

jest następnie obsługiwane w taki sam sposób, co każde inne endpoint:

↓401 HandlingAPI 2↓401 HandlingAPI 3↓401 Handling

Interceptor odpowiedzi może natomiast przechwycić wszystkie błędy 401 Unauthorized w jednym miejscu. Gdy je zauważy:

401 Received

może odnowić token dostępu, ponownie wysłać oryginalną prośbę i przekazać udaną odpowiedź do osoby, która ją wysłała:

↓Refresh Access Token↓Retry Original Request↓Return Successful Response

Użytkownik nigdy nie zauważa wygasłego tokena. Ważne są dwa środki ostrożności: oznaczenie powtórzonej żądania w jego konfiguracji, aby drugie błędy 401 nie mogły trwać w nieskończoność, oraz umożliwienie wspólnego wykorzystania jednego żądania odświeżenia przy jednoczesnych awariach, co omówiono w dlaczego błędy 401 występujące jednocześnie wylogowują użytkowników i jak to naprawić za pomocą pojedynczego żądania odświeżenia.

Kiedy interceptorzy nie są potrzebne

Niewielka aplikacja z dwoma lub trzema żądaniami API niewiele zyskuje na takiej pośrednicy, a ukryte zachowania mogą zaskoczyć czytelnika, który nie widzi źródła danego nagłówka. Przy dziesiątkach lub setkach żądań interceptorzy stanowią jeden z najlepszych sposobów na utrzymanie uporządkowanego i spójnego obsługi żądań. Zachowaj je do spraw wspólnych dla wielu elementów aplikacji; logika specyficzna dla konkretnego punktu końcowego powinna znajdować się w nim samym.

Podsumowanie

Wysyłanie żądań to tylko niewielka część zadań warstwy HTTP. Autoryzacja, obsługa błędów, logowanie, kształtowanie odpowiedzi oraz odświeżanie tokenów to zagadnienia wspólne dla różnych elementów, a intercepcje zapewniają im jedno spójne miejsce realizacji.

  • Traktuj intercepcje jako middleware po stronie klienta: intercepcje żądań kształtują to, co jest wysyłane, a intercepcje odpowiedzi – to, co przychodzi.
  • Zawsze zwracaj konfigurację z intercepcji żądań oraz ponownie rzucaj błędy nie do naprawienia z intercepcji odpowiedzi.
  • Powiąż je z instancją axios.create() skonfigurowaną specyficznie dla twojej API.
  • Ochrona logiki ponawiania prób przy błędzie 401 przed pętlami i jednoczesnymi odświeżeniami.

Literatura pokrewna