axios封装优化

registService.js

// 引入axios库
import axios from "axios";
import apiList from "./apiList";
import {Message, MessageBox, Notification} from 'element-ui'
import {getAccessToken, getRefreshToken,refreshToken , setToken} from './auth'
import { getNextUrl } from './urlManager';
console.log(process.env.NODE_ENV,"<<process.env.NODE_ENV");

// 设置请求头
const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
    "Authorization": "Bearer sk-vL5NugbRg65n5s346KxEo3foCebFJURWTk5iGpGntMZFsJMB"
};

// ---------------------------------------分界线--------------------------------------------------------
switch (process.env.NODE_ENV) {
    //可以在根目录的 package.json 配置 NODE_ENV
    case "production":
        axios.defaults.baseURL = "生产环境地址";
        break;
    case "test":
        axios.defaults.baseURL = "测试环境地址";
    default:
        axios.defaults.baseURL = "开发环境地址";
}


// Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现
// 请求队列
let requestList = []
// 是否正在刷新中
let isRefreshToken = false

/*
  设置请求传输数据的格式
  只支持POST请求,根据实际要求决定设置不设置
  */
axios.defaults.headers["Content-Type"] = "application/json";
// axios.defaults.headers["Content-Type"] = "application/x-www-form-urlencoded";
// ---------------------------------------分界线--------------------------------------------------------
// 创建axios实例
const axiosInstance = axios.create({
    headers: headers,
    timeout: 60000 // 设置请求超时时间
});

/*
 * 设置请求超时时间和跨域是否允许携带凭证
 */
axios.defaults.timeout = 0;
axios.defaults.withCredentials = true;
// 定义请求拦截器
axiosInstance.interceptors.request.use(
    (config) => {

        config.baseURL = getNextUrl(config.url); // 设置下一个 URL         //负载均衡
        // 请求发送前对请求配置进行处理,例如加入token等操作
        // 在这里可以做一些全局的请求拦截处理
        //获取本地存储中的token
        if (config.url.endsWith("loginByPhone")) {//登录接口不携带Authorization
            // config.headers['Authorization'] = ''//什么都不做
        }else {
            config.headers['Authorization'] = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
        }
        return config;
    },
    (error) => {
        // 对请求错误进行处理
        return Promise.reject(error);
    }
);

// 响应拦截器
axiosInstance.interceptors.response.use(async res => {
        // 未设置状态码则默认成功状态
        const code = res.data.code || 200;
        // 获取错误信息
        const msg = res.data.msg
        if (code === 401) {
            // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
            if (!isRefreshToken) {
                isRefreshToken = true;
                // 1. 如果获取不到刷新令牌,则只能执行登出操作
                // setTimeout(() =>{},1000)
                if (getRefreshToken() == null || getRefreshToken().trim().length == 0) {
                    isRefreshToken = false
                    return handleAuthorized();
                }
                // 2. 进行刷新访问令牌
                try {
                    const refreshTokenRes = await refreshToken()
                    if (refreshTokenRes.code == 0){
                        // 2.1 刷新成功,则回放队列的请求 + 当前请求
                        setToken(refreshTokenRes.data)
                        requestList.forEach(cb => cb())
                        return axiosInstance(res.config)
                    }
                } catch (e) {// 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
                    // 2.2 刷新失败,只回放队列的请求
                    // requestList.forEach(cb => cb())
                    // 提示是否要登出。即不回放当前请求!不然会形成递归
                    requestList = []
                    isRefreshToken = false
                    return handleAuthorized();
                    // return;
                } finally {
                    requestList = []
                    isRefreshToken = false
                }
            } else {
                // 添加到队列,等待刷新获取到新的令牌
                return new Promise(resolve => {
                    requestList.push(() => {
                        res.config.headers['Authorization'] = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
                        resolve(axiosInstance(res.config))
                    })
                })
            }
        }else if (code !== 200) {
            if (msg === '无效的刷新令牌' || msg === '用户未扫码') { // hard coding:忽略这个提示,直接登出
            } else {
                Notification.error({
                    title: msg
                })
            }
            return Promise.reject('error')
        } else {
            return res
        }
    }, error => {
        let {message} = error;
        if (message === "Network Error") {
            message = "系统正在维护升级中";
        } else if (message.includes("timeout")) {
            message = "系统接口请求超时";
        } else if (message.includes("Request failed with status code")) {
            if (message.substr(message.length - 3) === '401'){
                return handleAuthorized()
            }else {
                message = "系统接口" + message.substr(message.length - 3) + "异常";
            }
        }
        Message({
            message: message,
            type: 'error',
            duration: 5 * 1000
        })
        return Promise.reject(error)
    }
)

function handleAuthorized() {
    if (!window.location.href.endsWith("loginPage")){//loginPage页面不显示提示
        Message({
            message: '无效的会话,或者会话已过期,请重新登录。',
            type: 'warning',
            duration: 1 * 1000
        })
    }

    return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
}

// 定义API方法
const registService = (apis, axiosInstance) => {
    const service = {};
    for (let apiName in apis) {
        const { url, method,local,dataType, isFileUpload,isBlob} = apis[apiName];
        service[apiName] = async (params,useLocal = false) => {

            // 是否使用本地json,如果useLocal为true并且json文件,则返回json文件
            if (useLocal && local) {  // 如果指定使用本地并且有local配置
                const localAxios = axios.create();
                return localAxios.get(window.location.origin+'/data/'+local)
                    .then(response => {
                        return response.data;
                    }).catch(error => {
                        throw error;
                    });
            }else {
                try {
                    let response =  null
                    if ((method == "post" && dataType == "params") || method == "get") {
                        response = await axiosInstance({  //如果是get请求或者是post请求但是参数携带和get一样
                            url,
                            method,
                            params: params,
                        });
                    }else if (isFileUpload){//post请求 文件上传
                        response = await axiosInstance({ //post请求(正儿八经post)
                            url,
                            method,
                            data: params,
                            headers: {
                                'Content-Type': 'multipart/form-data'
                            }
                        });
                    }else if (isBlob){//post请求 blob
                        response = await axiosInstance({ //blob post请求
                            url,
                            method,
                            data: params,
                            responseType: 'blob'
                        });
                    }
                    else {
                        response = await axiosInstance({ //post请求(正儿八经post)
                            url,
                            method,
                            data: params,
                        });
                    }
                    return response.data;
                } catch (error) {
                    throw error;
                }
            }

        };
    }
    return service;
};
// 导出API实例
export default registService(apiList, axiosInstance);

apiList.js

const apiList = {
  //获取验证码
  getPhoneCode: {
    url: "/app-api/system/user/send-phone-verification-code",
    method: "post",
  },
  //登录接口
  loginAccount: {
    url: "/app-api/system/auth/loginByPhone",
    method: "post",
    local: "codess.json"  // 新增
  },
  //获取用户信息
  getUserInfo: {
    url: "/app-api/system/user/userinfo",
    method: "get",
  },
  //刷新token
  getNewToken: {
    url: "/app-api/system/auth/refresh-token",
    method: "post",
    dataType: "params"  //data  post请求但是参数和get一样
  },
  //获取验证图片
  getValidateImg: {
    url: "/app-api/captcha/get",
    method: "post",
    dataType: "data"  //data
  },
  //h滑动图片
  getSlideValidate: {
    url: "/app-api/captcha/check",
    method: "post",
    dataType: "data"  //data
  },
  //h滑动图片
  refreshToken: {
    url: "/app-api/system/auth/refresh-token",
    method: "post",
    dataType: "params"  //data
  },
  // 图生图
  genrenateImg: {
    url: "/app-api/sd/txt2img",
    method: "post",
    dataType: "data"  //data
  },
  // 上传文件
  uploadFile: {
    url: "/app-api/backend-api/uploadFile",
    method: "post",
    isFileUpload: true
  },
  // 获得语音生成
  generateSpeech: {
    url: "/app-api/backend-api/generateSpeech",
    method: "post",
    isBlob:true
  },
  //获得用户收藏信息
  favAppPage: {
    url: "/app-api/backend-api/favAppPage",
    method: "post",
  },

  // ---------以下为绘图模块-------------
  //sd上传文件
  uploadSdResource: {
    url: "/app-api/sd/uploadResource",
    method: "post",
    isFileUpload: true
  }
};
export default apiList;

auth.js

import api from './registService'
const AccessTokenKey = 'myToken'
const RefreshTokenKey = 'myRefreshToken'

// ========== Token 相关 ==========
// 刷新访问令牌
export async function refreshToken() {
  const response = await api.refreshToken(
      { refreshToken:getRefreshToken()}
  )

  return response
}

export function getAccessToken() {
  return localStorage.getItem(AccessTokenKey)
}

export function getRefreshToken() {
  return localStorage.getItem(RefreshTokenKey)
}

export function setToken(token) {
  localStorage.setItem(AccessTokenKey, token.accessToken)
  localStorage.setItem(RefreshTokenKey, token.refreshToken)
}

export function removeToken() {
  localStorage.removeItem(AccessTokenKey)
  localStorage.removeItem(RefreshTokenKey)
}

// ========== 账号相关 ==========

const UsernameKey = 'USERNAME'
const PasswordKey = 'PASSWORD'
const RememberMeKey = 'REMEMBER_ME'

export function getUsername() {
  return localStorage.getItem(UsernameKey)
}

export function setUsername(username) {
  localStorage.setItem(UsernameKey, username)
}

export function removeUsername() {
  localStorage.removeItem(UsernameKey)
}

export function removePassword() {
  localStorage.removeItem(PasswordKey)
}

export function getRememberMe() {
  return localStorage.getItem(RememberMeKey) === 'true'
}

export function setRememberMe(rememberMe) {
  localStorage.setItem(RememberMeKey, rememberMe)
}

export function removeRememberMe() {
  localStorage.removeItem(RememberMeKey)
}


export class getToken {
}

urlManager.js(url管理+负载均衡)

const urls = [
  // "http://ipv4.haehnoda.tech:8091",

  "http://ai.huguangzhez.cn:90"

    // 更多 URL...
];

let currentUrlIndex = 0;

export function getNextUrl(uri) {
    if (uri && uri.endsWith("getQrCode")){
        // return  "https://api.huguangzhez.cn.cn" //走信任的ip
        return  "http://ai.huguangzhez.cn:9293" //走信任的ip
    }
    const url = urls[currentUrlIndex];
    currentUrlIndex = (currentUrlIndex + 1) % urls.length;
    return url;
}
发表评论 / Comment

用心评论~