import type { RequestOptions } from '@@/plugin-request/request'; import { RequestConfig, request } from '@umijs/max'; import { message } from 'antd'; import { history } from 'umi'; // 错误处理方案: 错误类型 enum ErrorShowType { SILENT = 0, WARN_MESSAGE = 1, ERROR_MESSAGE = 2, NOTIFICATION = 3, REDIRECT = 9, } // 与后端约定的响应数据格式 interface ResponseStructure { code: number; data: any; message?: string; } // 添加一个刷新 token 的函数 const refreshToken = async () => { try { let refreshTokenId = localStorage.getItem('refreshToken'); if (!refreshTokenId) { return false; } let userId = JSON.parse(localStorage.getItem('userInfo') ?? "{}").id; // 这里实现刷新 token 的逻辑 const response = await request('/lms/User/RefreshToken', { method: 'POST', headers: { "Content-Type": "application/json" }, data: { refreshToken: refreshTokenId, "userId": userId, "deviceInfo": "2" }, }); if (response.code == 1) { localStorage.setItem('token', response.data); return true; } return false; } catch (error) { console.error('刷新 token 失败', error); return false; } }; const retryRequest = async (url: string, opts: RequestOptions) => { if (url) { try { const response = await request(url, { ...opts, headers: { ...opts.headers, 'Authorization': `Bearer ${localStorage.getItem('token')}`, }, }); return response; } catch (error) { console.error('重试请求失败', error); throw error; } } else { throw new Error('url is required for retryRequest'); } }; /** * @name 错误处理 * pro 自带的错误处理, 可以在这里做自己的改动 * @doc https://umijs.org/docs/max/request#配置 */ export const errorConfig: RequestConfig = { // 错误处理: umi@3 的错误处理方案。 errorConfig: { // 错误抛出 errorThrower: (res) => { const { code, data, message } = res as unknown as ResponseStructure; if (code != 1) { const error: any = new Error(message); error.name = 'BizError'; error.info = { message }; throw error; // 抛出自制的错误 } }, // 错误接收及处理 errorHandler: async (error: any, opts: any) => { let url = error.config.url; if (opts?.skipErrorHandler) throw error; // 我们的 errorThrower 抛出的错误。 if (error.name === 'BizError') { } else if (error.response) { // 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围 const { status } = error.response; if (status === 401) { if (url.toLowerCase().includes('/refreshtoken')) { message.error('登录已过期,请重新登录'); localStorage.removeItem('token'); localStorage.removeItem('refreshToken'); localStorage.removeItem('userInfo'); history.push('/user/login'); } else { // 尝试刷新 token const refreshSuccess = await refreshToken(); if (refreshSuccess) { // 刷新成功,重试原请求 // 这里需要实现重试逻辑,可能需要修改您的请求库 message.success('已重新获取授权,正在重试请求'); // 刷新成功,重试原请求 try { const retryResponse = await retryRequest(url, opts); // 如果重试成功,返回重试的响应 return retryResponse; } catch (retryError) { message.error('重试请求失败,请重新登录'); localStorage.removeItem('token'); localStorage.removeItem('refreshToken'); localStorage.removeItem('userInfo'); history.push('/user/login'); } // 重试原请求的逻辑 } else { // 刷新失败,重定向到登录页面 message.error('授权已过期,请重新登录'); localStorage.removeItem('token'); localStorage.removeItem('refreshToken'); localStorage.removeItem('userInfo'); history.push('/user/login'); } } // 这边要判断是不是刷新token的时候401,如果是刷新token的时候401,就不跳转到登录页面 // message.error('未授权,请重新登录'); // history.push('/user/login'); // 重定向到登录页面 } } else if (error.request) { // 请求已经成功发起,但没有收到响应 // \`error.request\` 在浏览器中是 XMLHttpRequest 的实例, // 而在node.js中是 http.ClientRequest 的实例 message.error('None response! Please retry.'); } else { // 发送请求时出了点问题 message.error('Request error, please retry.'); } }, }, // 请求拦截器 requestInterceptors: [ (config: RequestOptions) => { // 拦截请求配置,进行个性化处理。 // 添加校验头 // config.baseURL = 'https://localhost:44362'; config.baseURL = window.location.origin.includes('localhost') ? 'https://localhost:44362' : window.location.origin; const headers = { ...config.headers, // 保留已有的请求头 'Authorization': `Bearer ${localStorage.getItem('token')}`, // 添加新的请求头 // 你可以根据需要添加更多的请求头 }; return { ...config, headers }; }, ], // 响应拦截器 responseInterceptors: [ (response) => { // 拦截响应数据,进行个性化处理 const { data } = response as unknown as ResponseStructure; if (data?.success === false) { message.error('请求失败!'); } return response; }, ], };