Axios Interceptors як HTTP-мідлвейр: ментальна модель для додатків React
Зрозумійте, як інтерцептори запитів та відповідей Axios функціонують як проміжний шар, що має бути в кожному з них, та як вони централізують заголовки автентифікації та обробку помилок 401 у React.
Джерела: Axios Interceptors Explained: The Missing Piece Every Frontend Developer Should Learn.
У молодій кодовій базі на React виклики API зазвичай розростаються поступово, шляхом копіювання та вставки: кожен запит створює власний заголовок Authorization, кожна відповідь має власну обробку помилок, а кожен сценарій невдачі повторює одну й ту саму логіку відновлення. Це підходить для трьох кінцевих точок, але стає проблемою підтримки при тридцяти. Axios пропонує вбудовані рішення у вигляді інтерцепторів, і як тільки ви починаєте розглядати їх як проміжне програмне забезпечення для обробки HTTP-трафіку, стає зрозуміло, що можна перемістити до них, а що залишити у компонентах.
Дублювання, яке усувають інтерцептори
Типовою відправною точкою є запит до користувачів із додаванням токена-носія безпосередньо в запит:
axios.get("/users", {
headers: {
Authorization: `Bearer ${token}`,
},
});
Друга кінцева точка повторює цей блок заголовків слово в слово:
axios.get("/products", {
headers: {
Authorization: `Bearer ${token}`,
},
});
А операція запису робить те саме, з вмістом у середині:
axios.post("/orders", orderData, {
headers: {
Authorization: `Bearer ${token}`,
},
});
Спочатку ніщо з цього не виглядає шкідливим. У міру зростання проекту кожен запит потребує токена, кожна відповідь — обробки помилок, а кожна невдача — однакової реакції. Саме для виконання такої універсальної роботи існують інтерцептори.
Що насправді є інтерцептором
Інтерцептор — це функція, яку Axios виконує перед кожним вихідним запитом перед його надсиланням або перед кожною вхідною відповіддю, перш ніж ваш код її побачить: це контрольна точка, де можна перевірити або змінити трафік. Відповідь також проходить через власну контрольну точку перед тим, як буде виконано обіцянку, на яку чекає ваш код React.
Загальний шлях починається у вашому додатку:
React Application
і продовжується через обидві контрольні точки, перш ніж повернутися до нього:
↓Request Interceptor↓Axios↓Server↓Response Interceptor↓React Application
Ваші компоненти ніколи не взаємодіють безпосередньо з сервером; все проходить через ланцюг, що робить його ідеальним місцем для забезпечення однакової поведінки.
Мідлвейр, пояснений на прикладі рецепції
Якщо ви писали код на Express чи Koa, цей концепт вам знайомий: це мідлвейр. Якщо ні, уявіть собі відвідування офісної будівлі. Ви не заходите прямо в офіс. Шлях починається з вас:
You
і пролягає через дві зупинки по дорозі:
↓Security Check↓Reception↓Office
Система безпеки перевіряє ваш ідентифікатор, рецепція підтверджує вашу зустріч, і лише тоді ви потрапляєте до офісу. Жодна з цих зупинок не є кінцевою метою; кожна зупинка перериває ваш шлях для виконання додаткових дій, а потім дозволяє продовжити шлях або відмовляє.
Мідлвейр — це будь-який шар між двома системами, який виконує обробку перед тим, як запит буде переданий далі. Замініть офіс на сервер, починаючи з React-додатку:
React App
а інтерцептор — це шар посередині:
↓Middleware (Interceptor)↓Server
Іншими словами, інтерцептори Axios є проміжним програмним забезпеченням для запитів та відповідей HTTP на клієнті.
Інтерцептори запитів та відповідей
Axios пропонує два типи інтерцепторів, які реєструються за допомогою axios.interceptors.request.use(...) та axios.interceptors.response.use(...). Кожен виклик приймає функцію обробки успіху та необов’язкову функцію обробки помилок, а також повертає ідентифікатор, який можна згодом передати до eject для його видалення.
Інтерцептори запитів
Вони виконуються перед надсиланням запиту. Компонент виконує відповідний виклик:
Component
а інтерцептор бачить його під час виходу:
↓Request Interceptor↓Server
Типові завдання інтерцептора запиту:
- додавання токенів автентифікації
- налаштування користувацьких заголовків, таких як локалізація чи ідентифікатор запиту
- корекція вмісту запиту
- логування вихідного трафіку
Обробник отримує конфігурацію запиту та повинен її повернути (або обіцянку її отримання), щоб запит міг продовжуватися; забуття цього — класична помилка початківців.
Інтерцептори відповідей
Вони виконуються після того, як сервер надає відповідь, але до того, як ваше додаток отримає результат. Відповідь надходить від сервера:
Server
і проходить через інтерцептор по дорозі до компонента:
↓Response Interceptor↓Component
Загальні завдання тут:
- обробка помилок в одному місці
- трансформація або розпакування даних відповіді
- логування відповідей
- перепробування невдалих запитів
- перенаправлення користувача, наприклад на сторінку входу
Обробник успіху керує відповідями 2xx, а обробник помилок — рештою. Якщо він не може виправити ситуацію, він повинен повернути відхилений обіцянок, щоб викликаючі все одно бачили про помилку.
Що відбувається за одним викликом
Запит у одному рядку здається таким, ніби Axios відправляє його миттєво:
axios.get("/users");
Внутрішньо цей самий запит:
axios.get("/users")
проходить через довшу ланцюгову обробку, перш ніж виконається ваш .then():
↓Request Interceptor↓HTTP Request↓Server↓HTTP Response↓Response Interceptor↓.then()↓Your Component
Спільна логіка виконується при кожному запиті, без усвідомлення цього місцем виклику.
Чому централізація важлива з ростом додатку
axios.get("/users", {
headers: {
Authorization: `Bearer ${token}`,
},
});
Так само і запит щодо продуктів:
axios.get("/products", {
headers: {
Authorization: `Bearer ${token}`,
},
});
І так само кожен запис, наприклад створення замовлення:
axios.post("/orders", data, {
headers: {
Authorization: `Bearer ${token}`,
},
});
Якщо бекенд змінює свою схему автентифікації, вам потрібно оновлювати кожен запит, і кожен пропущений запит стає помилкою. Інтерцептор зберігає цю логіку один раз, а Axios застосовує її всюди. Зареєструйте його на екземплярі, створеному за допомогою axios.create(), щоб він діяв на ваш API, а не на URL сторонніх сервісів.
Сценарій використання: автоматичне додавання токена доступу
Це найпоширеніша причина використання інтерцепторів. Після входу у систему додаток зберігає токен доступу, який потрібен кожному захищеному ендпоїнту. Запит починається як звичайний запит:
API Call
а інтерцептор додає заголовок перед тим, як запит досягне сервера:
↓Request Interceptor↓Authorization Header Added↓Server
Тепер кожен запит містить токен. Читайте його в інтерцепторі під час кожного запиту, а не один раз під час запуску, інакше оновлений токен ніколи не буде отриманий.
Сценарій використання: глобальна обробка відповідей 401
Токени доступу закінчують свою дію. Коли це відбувається, сервер відповідає наступним чином:
401 Unauthorized
Без інтерцепторів кожен виклик потребує окремого розгляду для цього. Перший виклик API:
API 1
після чого слідує такий самий процес обробки, який дублюється для кожного іншого ендпоінта:
↓401 HandlingAPI 2↓401 HandlingAPI 3↓401 Handling
Інтерцептор відповідей може замість цього захоплювати всі помилки 401 Unauthorized в одному місці. Коли він їх бачить:
401 Received
він може оновити токен доступу, повторно надіслати початковий запит та передати успішну відповідь користувачеві:
↓Refresh Access Token↓Retry Original Request↓Return Successful Response
Користувач ніколи не помічає закінчення терміну дії токена. Важливі два заходи безпеки: позначати повторні запити у їхній конфігурації, щоб другий код 401 не міг постійно циклюватися, та забезпечувати, щоб одночасні помилки використовували один і той самий запит на оновлення, про що йдеться у статті про те, чому одночасні помилки 401 виводять користувачів та як це виправити за допомогою єдиного запиту на оновлення.
Коли інтерцептори не є доцільними
Невеликий додаток із двома-трьома запитами до API мало виграє від непрямого оброблення, а прихована поведінка може здивувати користувача, який не бачить джерела певного заголовка. При десятках чи сотнях запитів інтерцептори є одним із найкращих способів зберегти чистоту та послідовність у обробці запитів. Використовуйте їх для проблем, які стосуються кількох частин додатку; логіка, специфічна для кожного ендпоїнта, має залишатися в самому ендпоїнті.
Підсумок
Надсилання запитів — це лише частина завдань шару HTTP. Аутентифікація, обробка помилок, логування, формування відповідей та оновлення токенів є скрізь присутніми процесами, і інтерцептори забезпечують їм єдине місце реалізації.
- Розглядайте інтерцептори як середовище між клієнтом та сервером: інтерцептори запитів формують те, що відправляється, а інтерцептори відповідей — те, що надходить.
- Завжди повертайте конфігурацію від інтерцепторів запитів та знову кидаєте нерекуперовані помилки від інтерцепторів відповідей.
- Приєднуйте їх до екземпляра
axios.create(), створеного спеціально для вашого API. - Захищайте логіку повторних спроб для коду 401 від циклів та одночасних оновлень.