Skip to content

前端

Remix Antd Admin 是一个全站项目,目的是在一个项目里面完成一个较大的项目。前端部分在 React Router 中需要注意一下内容:

不是 SPA 模式

本质上 React Router 不是一个 SPA 模式,而是 Client Only Client 渲染。

这样做的好处是走的 SSR 模式,在需要 SSR 渲染地方可以直接使用 SSR 渲染。

API

Remix Antd Admin 遵循传统前端的 API 数据获取方式,使用 axios 获取。

由于使用了 双 Token 的机制,我们对 Axios 进行了封装:

ts
import axios from "axios";
import { simpleStorage } from "./simpleStorage";

const api = axios.create({
  baseURL: "/api/v1",
  timeout: 10000,
  headers: {
    "Content-Type": "application/json",
  },
});

// 请求拦截器
api.interceptors.request.use(
  (config) => {
    // 在发送请求之前处理(如添加 token)
    const token = localStorage.getItem("token");
    if (token) {
      config.headers["Authorization"] = `Bearer ${token}`;
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  },
);

// 响应拦截器
api.interceptors.response.use(
  async (response) => {
    const result = response.data;
    if (needRefreshToken(result)) {
      // 需要刷新 token
      const refreshResponse: any = await api.post("/auth/refresh_token", {
        refresh_token: simpleStorage.getRefreshToken(),
      });
      console.log("refreshResponse", refreshResponse);
      if (refreshResponse && refreshResponse.code === 0) {
        // 刷新成功,重新请求
        simpleStorage.setToken(refreshResponse.data.token);
        simpleStorage.setRefreshToken(refreshResponse.data.refresh_token);
        response.config.headers["Authorization"] =
          `Bearer ${refreshResponse.data.token}`;
        return await api.request(response.config);
      } else {
        // 刷新失败,跳转到登录页
        window.location.href = "/login";
      }
    }

    // 对响应数据做处理(比如统一的错误处理)
    return response.data; // 只返回数据,去掉不必要的包装
  },
  async (error) => {
    if (error.response.status === 401) {
      // 跳转到登录页
      window.location.href = "/login";
    }
    // 统一的错误处理
    if (error.response) {
      console.error("Response Error:", error.response.data);
    } else if (error.request) {
      // 请求被发出去,但没有响应
      console.error("Request Error:", error.request);
    } else {
      // 其他错误
      console.error("Error:", error.message);
    }
    return Promise.reject(error);
  },
);

function needRefreshToken(result: any) {
  if (
    result.code === 1 &&
    result.message === `"exp" claim timestamp check failed`
  ) {
    return true;
  }
  return false;
}

export default api;
  • 目前支持拦截请求
  • 对于过期的请求会执行 refresh_token 和队列保存,然后重试。