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;
}