+9

Xử lý request và refresh token hiệu quả trong React Js với Axios Interceptors

Mở đầu

Hầu hết một Front-end Developer đều đã từng sử dụng Axios để xử lý các request trong dự án của mình. Axios interceptor là một tính năng của thư viện Axios dùng để xử lý các request và response trước khi chúng được gửi đi hoặc nhận về. Nó cho phép bạn tùy chỉnh các request và response trong quá trình giao tiếp giữa ứng dụng của bạn và API. Sau một thời gian làm việc với nó, mình sẽ hướng dẫn các bạn cách config Axios interceptor trong một dự án React sử dụng Typesript

Sử dụng

  1. cài đặt thư viện Axios:
npm install axios

hoặc:

yarn add axios
  1. Tạo một file AxiosInterceptor.ts:
  • Ở trong file này chúng ta sẽ tạo ra 3 hàm để config request trước khi gửi đi và response trả về:
  • Config request:
const onRequestSuccess = (config: AxiosRequestConfig) => {
    const auth = getCookie(Authenticate.AUTH);
    config.timeout = 10000;
    if (auth) {
      config.headers = {
        Authorization: "Bearer " + auth,
        "x-api-key": keyHearder
      };
      // Xử lý params cho request
      if (config.params) {
        config.paramsSerializer = {
          serialize: (params: Record<string, any>) =>
            queryString.stringify(params)
        }
      }
    }
    // Các xử lý khác....
    return config;
  };
  • Config response success:
const onResponseSuccess = (response: AxiosResponse) => {
    return response.data;
  };
  • Config response error:
const onResponseSuccess = (response: AxiosResponse) => {
    return response;
  };
  const onResponseError = (error: AxiosError) => {
    if (
      error.response?.status !== 401 || error.config?.url?.includes(authUrl)
    ) {
      const errMessage = error.response?.data || error?.response || error;
      return Promise.reject(errMessage);
    }
    return refreshToken(error, onUnauthenticated); // gọi hàm để refresh token.
  };
  
  // hàm để refresh token
  const refreshToken = async (error: AxiosError, logout: Function) => {
  const refreshToken = getLocalStorage(Authenticate.REFRESH_TOKEN_ETC);
  if (!refreshToken) {
    logout();
    return;
  }
  try {
      const { data } = await AuthApi.refreshToken({ refreshToken });
      setLocalStorage({ key: Authenticate.REFRESH_TOKEN_ETC, value: data.refreshToken });
      setCookie(
        Authenticate.AUTH,
        JSON.stringify({
          username: data.username,
          accessToken: data.accessToken,
        }),
        0.02
      );
      error.config.headers = {
        Authorization: "Bearer " + data.accessToken,
      }
      return axios(error.config);
    } catch (error) {
      logout();
      return;
    }
}
  • Sau đó, chúng ta sẽ sử dụng các hàm đã định nghĩa ở trên với phương thức request.use và response.use để thêm interceptor vào Axios.:
 axios.interceptors.request.use(onRequestSuccess);
 axios.interceptors.response.use(onResponseSuccess, onResponseError);
  1. Cho tất cả hàm vừa tạo vào 1 hàm để export sử dụng:
export default function AxiosInterceptor(onUnauthenticated: Function // hàm để sử lý logout) {
    const onRequestSuccess = (config: AxiosRequestConfig) {
        // Xử lý reuquest
    }
    const onResponseSuccess = (response: AxiosResponse) => {
        // Xử lý response thành công
  };
  const onResponseError = (error: AxiosError) => {
    // Xử lý response thất bại
  };
}
  1. Sử dụng file AxiosInterceptor.ts chúng ta vừa tạo ở trên: Ở đây mình không khởi tạo một instance bằng cách sử dụng axios.create() mà mình vẫn sử dụng axios mặc định và chỉ override lại nó, nên mình sẽ dụng dụng hàm vừa tạo trong file index.tsx:
const action = bindActionCreators({ clearAuthentication }, store.dispatch);
AxiosInterceptor(() => {
  action.clearAuthentication(); // hàm logout với redux
});
const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement, { identifierPrefix: "vetc-agency" }
);
// code...

Kết thúc

Đây là cách mình hay sử dụng để config các request và refresh token trong dự án của mình. Ngoài ra cũng còn những cách khác để xử lý với thư viện Axios , các bạn có thể tìm hiểu thêm để có những cách phù hợp cho dự án của mình.

Tài liệu tham khảo

https://axios-http.com/


All rights reserved

Bình luận

Đăng nhập để bình luận
Avatar
@nghiepit
thg 1 6, 2023 11:17 SA

Đúng nghĩa refreshToken là phải thực thi lại request sau khi lấy Access Token mới

Avatar
@trungnd11
thg 1 6, 2023 3:59 CH

const refreshToken = async (error: AxiosError, logout: Function) => { const refreshToken = getLocalStorage(Authenticate.REFRESH_TOKEN_ETC); if (!refreshToken) { logout(); return; } try { const { data } = await AuthApi.refreshToken({ refreshToken }); setLocalStorage({ key: Authenticate.REFRESH_TOKEN_ETC, value: data.refreshToken }); setCookie( Authenticate.AUTH, JSON.stringify({ username: data.username, accessToken: data.accessToken, }), 0.02 ); error.config.headers = { Authorization: "Bearer " + data.accessToken, } return axios(error.config); } catch (error) { logout(); return; } }

Mình có gọi lại request nha.

Avatar
@zeroca2503
thg 2 9, 2023 4:41 SA

cho mình hỏi nếu vậy thì có 1 chức năng nào đó gọi lại nhiều hàm , đại loại như Get Profile & Products, lúc này access token hết hạn thì tất nhiên sẽ check bên error để lấy refresh_token ( mặc định refresh_token không hết hạn ) thì có phải sẽ gọi 2 lần api và cùng đó gọi 2 lần hàm refresh_token không ? Giả sử chức năng đó liên quan nhiều API thì mình sẽ thấy 1 hiện trạng là bị duplicate ấy, không hay có lắm. Đoạn này theo mình làm các dự án thì thấy bro chưa chặt chẽ ý, tại còn vài case nữa .

Avatar
@trungnd11
thg 2 13, 2023 12:36 CH

Bạn nói đúng. Mình có thể dùng một mạng để lưu lại cái request bị lỗi sau đó đợi có token mới thì chạy mảng đó gọi lại request.

Avatar
@Van_Tan
thg 4 22, 2023 1:07 SA

axios.interceptors.request.use(onRequestSuccess); đã gặp lỗi "Argument of type 'AxiosRequestConfig<any>' is not assignable to parameter of type '(value: AxiosResponse<any, any>) => AxiosResponse<any, any> | Promise<AxiosResponse<any, any>>'. Type 'AxiosRequestConfig<any>' provides no match for the signature '(value: AxiosResponse<any, any>): AxiosResponse<any, any> | Promise<AxiosResponse<any, any>>'."

Avatar
@wiliamfeng
thg 6 12, 2023 12:31 SA

tks

Avatar
+9