Axios Interceptor: Hiểu sâu kiến trúc Middleware và ứng dụng thực tế trong React
Khi mới làm quen với việc gọi API trong React, chúng ta thường viết các đoạn code gắn Token hoặc xử lý lỗi 401 trực tiếp vào từng hàm fetch hoặc axios.get(). Cách làm này hoạt động được, nhưng khi hệ thống phình to lên hàng trăm API, nó sẽ lập tức phá vỡ nguyên tắc DRY (Don't Repeat Yourself) và khiến code base trở nên cực kỳ khó bảo trì.
Để giải quyết bài toán này ở tầm nhìn kiến trúc, chúng ta cần một cơ chế can thiệp ở cấp độ toàn cục (global). Đó chính là lúc Interceptor Pattern lên tiếng.
1. Bản chất của Interceptor trong Kỹ thuật phần mềm
Trong Kỹ thuật phần mềm (Software Engineering), Interceptor là một mẫu thiết kế (Design Pattern) thuộc nhóm cấu trúc. Nó cho phép một ứng dụng chèn thêm các bước xử lý vào một luồng thực thi (pipeline) có sẵn mà không cần phải thay đổi mã nguồn cốt lõi của luồng đó.
Khái niệm này có sự tương đồng rất lớn với Middleware trong các backend framework như Express.js hay NestJS.
Interceptor sinh ra để giải quyết một khái niệm cốt lõi: Cross-cutting Concerns (Các mối quan tâm cắt ngang). Đây là những logic không thuộc về nghiệp vụ chính (Business Logic) của một hàm, nhưng lại bắt buộc phải có mặt ở khắp mọi nơi. Điển hình nhất là:
Authentication & Authorization: Xác thực người dùng bằng
Token.Logging: Ghi nhận thời gian và thông số của mọi request.
Error Handling: Bắt và xử lý các lỗi hệ thống (ví dụ:
500 Internal Server Error,401 Unauthorized).Data Transformation: Mã hóa/Giải mã payload trước khi truyền tải.
Thay vì phân tán các logic này, Interceptor gom chúng lại và đặt tại một "trạm kiểm soát" duy nhất.
2. Vòng đời của một Request và cơ chế Promise Chaining trong Axios
Dưới mui xe (under the hood), thư viện axios quản lý luồng dữ liệu dựa trên Promise Chaining (Chuỗi Promise). Khi bạn khai báo một Interceptor, thực chất bạn đang đẩy thêm các hàm resolve và reject vào mảng Promise của Axios.
Luồng đi của một API Call thông qua Axios diễn ra như sau:
Request Interceptors: Khi bạn gọi
axios.get(), request chưa bay ra network ngay. Nó sẽ chạy qua tất cả các hàm Request Interceptor mà bạn đã đăng ký (theo thứ tự LIFO hoặc FIFO tùy cấu hình). Mỗi interceptor nhận vàoconfigobject, chỉnh sửa nó, và bắt buộc phải return lạiconfigđó để truyền cho mắt xích tiếp theo.Dispatch Request: Axios thực hiện gửi
HTTP Requestthực tế lên Server và đợi kết quả.Response Interceptors: Khi Server trả về
Response, dữ liệu lại đi qua các Response Interceptor. Tại đây, bạn có thể kiểm traHTTP Status Code. Nếu thành công (2xx), luồng đi vào nhánhFulfilled. Nếu thất bại, luồng rẽ sang nhánhRejected.Hàm gọi API của bạn: Cuối cùng, dữ liệu (hoặc lỗi) mới được trả về hàm
.then()hoặc.catch()trong component của bạn.
3. Thực hành: Cấu hình Request Interceptor
Ứng dụng lớn nhất của Request Interceptor là tự động gắn Authorization Header. Code thực thi rất đơn giản, nhưng bạn cần nhớ quy tắc nền tảng: Luôn phải return lại config. Nếu không, chuỗi Promise sẽ bị đứt gãy và request sẽ bị treo.
JavaScript
import axios from 'axios';
// 1. Khởi tạo một instance riêng biệt
const apiClient = axios.create({
baseURL: 'https://api.domaincuaban.com/v1',
timeout: 10000,
});
// 2. Thiết lập Request Interceptor
apiClient.interceptors.request.use(
(config) => {
const token = localStorage.getItem('accessToken');
// Can thiệp vào gói tin: Gắn thêm Header trước khi gửi
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
// Quan trọng: Trả lại config để Axios đẩy request đi tiếp
return config;
},
(error) => {
// Bắt lỗi nếu quá trình setup config gặp sự cố
return Promise.reject(error);
}
);
4. Thực hành: Cấu hình Response Interceptor
Response Interceptor là nơi lý tưởng để làm 2 việc: Bóc tách dữ liệu rườm rà và xử lý lỗi tập trung.
JavaScript
apiClient.interceptors.response.use(
(response) => {
// Nhánh Fulfilled: Status code 2xx
// Trích xuất trực tiếp phần 'data' để Component không phải gọi response.data.data
return response.data;
},
(error) => {
// Nhánh Rejected: Status code nằm ngoài 2xx
if (error.response) {
// Ví dụ: Xử lý tập trung lỗi 401 (Hết phiên đăng nhập)
if (error.response.status === 401) {
console.warn("Token expired. Redirecting to login...");
localStorage.removeItem('accessToken');
window.location.href = '/login';
}
// Xử lý các lỗi HTTP khác (403, 500...) tại đây
}
// Đẩy lỗi về cuối chuỗi Promise để Component có thể catch() nếu cần
return Promise.reject(error);
}
);
export default apiClient;
5. Lời kết
Việc sử dụng Axios Interceptor không đơn thuần là việc học cách dùng một API của thư viện, mà là việc thực hành tư duy kiến trúc. Bằng cách tách biệt phần xử lý mạng (Network Logic) và logic xác thực (Auth Logic) ra khỏi logic hiển thị của React (UI Logic), bạn đang tạo ra một code base tuân thủ nguyên tắc Single Responsibility (Đơn trách nhiệm), giúp ứng dụng dễ dàng mở rộng và bảo trì trong tương lai.