| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
- import { ElMessage } from 'element-plus';
- import { Local, } from '/@/utils/storage';
- import {clearAccessAfterReload} from "/@/utils/axios-utils";
- import { getApiPublicBase } from '/@/utils/api-public-base';
- // 定义请求中止控制器映射表
- const abortControllerMap: Map<string, AbortController> = new Map();
- // 配置新建一个 axios 实例
- export const service = axios.create({
- baseURL: getApiPublicBase() as any,
- timeout: 50000,
- //headers: { 'Content-Type': 'application/json' }, 这个会导致生成代码的上传文件 file为空
- });
- // token 键定义
- export const accessTokenKey = 'access-token';
- export const refreshAccessTokenKey = `x-${accessTokenKey}`;
- // 获取 token
- export const getToken = () => {
- return Local.get(accessTokenKey);
- };
- function stringifyErrors(errors: unknown): string | undefined {
- if (!errors) return undefined;
- if (typeof errors === 'string') return errors;
- if (Array.isArray(errors)) {
- const parts = errors.map((item) => stringifyErrors(item)).filter(Boolean);
- return parts.length ? parts.join(';') : undefined;
- }
- if (typeof errors === 'object') {
- const parts = Object.entries(errors as Record<string, unknown>)
- .flatMap(([key, value]) => {
- const text = stringifyErrors(value);
- return text ? `${key}: ${text}` : [];
- });
- return parts.length ? parts.join(';') : undefined;
- }
- return undefined;
- }
- function extractResponseMessage(payload: unknown): string | undefined {
- if (!payload) return undefined;
- if (typeof payload === 'string') return payload.trim() || undefined;
- if (typeof payload !== 'object') return undefined;
- const data = payload as Record<string, unknown>;
- return (
- stringifyErrors(data.errors) ||
- stringifyErrors(data.message) ||
- stringifyErrors(data.title) ||
- stringifyErrors(data.error) ||
- stringifyErrors(data.detail) ||
- // B3 修复:ASP.NET Core [ApiController] 默认 400 返回顶层 ModelState
- // 字典格式(如 {"CompanyRefId":["公司不能为空"]}),上述 5 个业务字段
- // 均未命中时,直接把整个 payload 作为 ModelState 展开为"字段: 消息"文本,
- // 避免降级到 axios 的 "Request failed with status code 400" 技术文案。
- stringifyErrors(data)
- );
- }
- function extractRequestErrorMessage(error: any): string {
- return (
- extractResponseMessage(error?.response?.data) ||
- (typeof error?.message === 'string' && error.message.trim() ? error.message.trim() : undefined) ||
- (typeof error?.response?.statusText === 'string' && error.response.statusText.trim() ? error.response.statusText.trim() : undefined) ||
- '请求失败'
- );
- }
- // axios 默认实例
- export const axiosInstance: AxiosInstance = axios;
- // 添加请求拦截器
- service.interceptors.request.use(
- (config) => {
- // // 在发送请求之前做些什么 token
- // if (Session.get('token')) {
- // (<any>config.headers).common['Authorization'] = `${Session.get('token')}`;
- // }
- // 记录中止控制信息
- const controller = new AbortController();
- config.signal = controller.signal;
- const url = config.url || '';
- abortControllerMap.set(url, controller);
- // 获取本地的 token
- const accessToken = Local.get(accessTokenKey);
- if (accessToken) {
- // 将 token 添加到请求报文头中
- config.headers!['Authorization'] = `Bearer ${accessToken}`;
- // 判断 accessToken 是否过期
- const jwt: any = decryptJWT(accessToken);
- const exp = getJWTDate(jwt.exp as number);
- // token 已经过期
- if (new Date() >= exp) {
- // 获取刷新 token
- const refreshAccessToken = Local.get(refreshAccessTokenKey);
- // 携带刷新 token
- if (refreshAccessToken) {
- config.headers!['X-Authorization'] = `Bearer ${refreshAccessToken}`;
- }
- }
- // debugger
- // get请求映射params参数
- if (config.method?.toLowerCase() === 'get' && config.data) {
- let url = config.url + '?' + tansParams(config.data);
- url = url.slice(0, -1);
- config.data = {};
- config.url = url;
- }
- }
- return config;
- },
- (error) => {
- // 对请求错误做些什么
- return Promise.reject(error);
- }
- );
- // 添加响应拦截器
- service.interceptors.response.use(
- (res) => {
- // 请求结束后清除中止控制项
- const url = res.config.url || '';
- abortControllerMap.delete(url);
- // 获取状态码和返回数据
- var status = res.status;
- var serve = res.data;
- // 处理 401
- if (status === 401) {
- clearAccessAfterReload();
- }
- // 处理未进行规范化处理的
- if (status >= 400) {
- throw new Error(res.statusText || 'Request Error.');
- }
- // 处理规范化结果错误
- if (serve && serve.hasOwnProperty('errors') && serve.errors) {
- throw new Error(JSON.stringify(serve.errors || 'Request Error.'));
- }
- // 读取响应报文头 token 信息
- var accessToken = res.headers[accessTokenKey];
- var refreshAccessToken = res.headers[refreshAccessTokenKey];
- // 判断是否是无效 token
- if (accessToken === 'invalid_token') {
- clearAccessAfterReload();
- }
- // 判断是否存在刷新 token,如果存在则存储在本地, 并重新加载页面
- else if (refreshAccessToken && accessToken) {
- Local.set(accessTokenKey, accessToken);
- Local.set(refreshAccessTokenKey, refreshAccessToken);
- }
- // 响应拦截及自定义处理
- if (serve.code === 401) {
- clearAccessAfterReload();
- } else if (serve.code === undefined) {
- return Promise.resolve(res);
- } else if (serve.code !== 200) {
- var message;
- // 判断 serve.message 是否为对象
- if (serve.message && typeof serve.message == 'object') {
- message = JSON.stringify(serve.message);
- } else {
- message = serve.message;
- }
- ElMessage({
- dangerouslyUseHTMLString: true,
- message: message,
- type: 'error',
- });
- throw new Error(message);
- }
- return res;
- },
- (error) => {
- const silentError = Boolean(
- error?.config?.headers?.['X-Silent-Error'] ||
- error?.config?.headers?.['x-silent-error']
- );
- const message = extractRequestErrorMessage(error);
- // 处理响应错误
- if (error.response) {
- if (error.response.status === 401) {
- clearAccessAfterReload();
- }
- }
- if (silentError) {
- return Promise.reject(error);
- }
- // 对响应错误做点什么
- if (typeof error?.message === 'string' && error.message.indexOf('timeout') != -1) {
- ElMessage.error('网络超时');
- } else if (error.message == 'Network Error') {
- ElMessage.error('网络连接错误');
- } else {
- ElMessage.error(message || '接口路径找不到');
- }
- return Promise.reject(error);
- }
- );
- // 取消指定请求
- export const cancelRequest = (url: string | string[]) => {
- const urlList = Array.isArray(url) ? url : [url];
- for (const _url of urlList) {
- abortControllerMap.get(_url)?.abort();
- abortControllerMap.delete(_url);
- }
- }
- // 取消全部请求
- export const cancelAllRequest = () => {
- for (const [_, controller] of abortControllerMap) {
- controller.abort();
- }
- abortControllerMap.clear();
- }
- /**
- * 参数处理
- * @param {*} params 参数
- */
- export function tansParams(params: any) {
- let result = '';
- for (const propName of Object.keys(params)) {
- const value = params[propName];
- var part = encodeURIComponent(propName) + '=';
- if (value !== null && value !== '' && typeof value !== 'undefined') {
- if (typeof value === 'object') {
- for (const key of Object.keys(value)) {
- if (value[key] !== null && value[key] !== '' && typeof value[key] !== 'undefined') {
- let params = propName + '[' + key + ']';
- var subPart = encodeURIComponent(params) + '=';
- result += subPart + encodeURIComponent(value[key]) + '&';
- }
- }
- } else {
- result += part + encodeURIComponent(value) + '&';
- }
- }
- }
- return result;
- }
- /**
- * 解密 JWT token 的信息
- * @param token jwt token 字符串
- * @returns <any>object
- */
- export function decryptJWT(token: string): any {
- token = token.replace(/_/g, '/').replace(/-/g, '+');
- var json = decodeURIComponent(escape(window.atob(token.split('.')[1])));
- return JSON.parse(json);
- }
- /**
- * 将 JWT 时间戳转换成 Date
- * @description 主要针对 `exp`,`iat`,`nbf`
- * @param timestamp 时间戳
- * @returns Date 对象
- */
- export function getJWTDate(timestamp: number): Date {
- return new Date(timestamp * 1000);
- }
- /**
- * Ajax请求,如果成功返回result字段,如果不成功提示错误信息
- * @description Ajax请求
- * @config AxiosRequestConfig 请求参数
- * @returns 返回对象
- */
- export function request2(config: AxiosRequestConfig<any>): any {
- return new Promise((resolve, reject) => {
- service(config)
- .then((res) => {
- if (res.data.type == 'success') {
- resolve(res.data.result);
- } else {
- console.log('res', res);
- ElMessage.success(res.data.message);
- }
- })
- .catch((res) => {
- console.log('res', res);
- ElMessage.error(res);
- reject(res);
- });
- });
- }
- /**
- * 使用新的令牌登录
- * @param accessInfo
- */
- export function reLoadLoginAccessToken(accessInfo: any) {
- if (accessInfo?.accessToken && accessInfo?.refreshToken) {
- Local.set(accessTokenKey, accessInfo.accessToken);
- Local.set(refreshAccessTokenKey, accessInfo.refreshToken);
- setTimeout(() => location.href = "/", 300);
- }
- }
- // 导出 axios 实例
- export default service;
|