// 环境的切换
if (process.env.NODE_ENV == 'development') {
axios.defaults.baseURL = 'https://www.baidu.com';}
else if (process.env.NODE_ENV == 'debug') {
axios.defaults.baseURL = 'https://www.ceshi.com';
}
else if (process.env.NODE_ENV == 'production') {
axios.defaults.baseURL = 'https://www.production.com';
}
import axios from 'axios';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Session } from '/@/utils/storage';
// 配置新建一个 axios 实例
const request = axios.create({
baseURL: import.meta.env.VITE_API_URL as any,
timeout: 50000,
headers: { 'Content-Type': 'application/json' },
});
// 添加请求拦截器
request.interceptors.request.use(
(config) => {
// 在发送请求之前做些什么 token
if (Session.get('token')) {
config.headers.common['Authorization'] = `${Session.get('token')}`;
}
return config;
},
(error) => {
// 对请求错误做些什么
return Promise.reject(error);
}
);
// 添加响应拦截器
request.interceptors.response.use(
(response) => {
// 对响应数据做点什么
const res = response.data;
if (res.code && res.code !== 0) {
// `token` 过期或者账号已在别处登录
if (res.code === 401 || res.code === 4001) {
Session.clear(); // 清除浏览器全部临时缓存
window.location.href = '/'; // 去登录页
ElMessageBox.alert('你已被登出,请重新登录', '提示', {})
.then(() => {})
.catch(() => {});
}
return Promise.reject(request.interceptors.response);
} else {
return response.data;
}
},
(error) => {
// 对响应错误做点什么
if (error.message.indexOf('timeout') != -1) {
ElMessage.error('网络超时');
} else if (error.message == 'Network Error') {
ElMessage.error('网络连接错误');
} else {
if (error.response.data) ElMessage.error(error.response.statusText);
else ElMessage.error('接口路径找不到');
}
return Promise.reject(error);
}
);
// 导出 axios 实例
export default request;
import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import store from '@/store'
import { getToken } from '@/utils/auth'
let refreshTokenCall
// create an axios instance
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 5000 // request timeout
})
// request interceptor
service.interceptors.request.use(
config => {
// do something before request is sent
if (config.url.includes('refresh-token')) {
return config
}
if (store.getters.token == null) {
return config
}
const { access_token, expires_in } = getToken()
if (store.getters.token && expires_in > new Date()) {
config.headers['Authorization'] = `Bearer ${access_token}`
return config
}
// refresh the token if it's expired
if (store.getters.token && expires_in < new Date()) {
if (!refreshTokenCall) {
refreshTokenCall = store.dispatch('user/refreshToken')
.then((res) => {
config.headers['Authorization'] = `Bearer ${res.access_token}`
refreshTokenCall = undefined
})
}
return refreshTokenCall.then(() => {
return config
})
}
},
error => {
// do something with request error
console.log(error) // for debug
return Promise.reject(error)
}
)
// response interceptor
service.interceptors.response.use(
/**
* If you want to get http information such as headers or status
* Please return response => response
*/
/**
* Determine the request status by custom code
* Here is just an example
* You can also judge the status by HTTP Status Code
*/
response => {
const res = response.data
const contentType = response.headers['content-type']
if (contentType !== 'application/xml') {
// if the custom code is not 20000, it is judged as an error.
if (res.code !== 20000 && res.code !== 200) {
Message({
message: res.message || 'Error',
type: 'error',
duration: 5 * 1000
})
// 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
// to re-login
MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
confirmButtonText: 'Re-Login',
cancelButtonText: 'Cancel',
type: 'warning'
}).then(() => {
store.dispatch('user/resetToken').then(() => {
location.reload()
})
})
}
return Promise.reject(new Error(res.message || 'Error'))
} else {
return res
}
} else {
return res
}
},
error => {
console.log('err' + error) // for debug
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
if (error.response && error.response.code && error.response.code === 401) {
// to re-login
MessageBox.confirm('You have been logged out, you can cancel to stay on this page, or log in again', 'Confirm logout', {
confirmButtonText: 'Re-Login',
cancelButtonText: 'Cancel',
type: 'warning'
}).then(() => {
store.dispatch('user/resetToken').then(() => {
location.reload()
})
})
}
return Promise.reject(error)
}
)
export default service
import Cookies from 'js-cookie'
const TokenKey = 'Admin-Token'
export function getToken() {
const token = Cookies.get(TokenKey)
return token ? JSON.parse(token) : null
}
export function getAccessToken() {
const token = Cookies.get(TokenKey)
return token ? JSON.parse(token).access_token : null
}
export function setToken(token) {
return Cookies.set(TokenKey, token)
}
export function removeToken() {
return Cookies.remove(TokenKey)
}
// /src/request/index.js
// /src/request/index.js
import axios from "axios";
import Qs from "qs";
import { addPendingRequest, removePendingRequest, Download } from "./method.js";
import { codeMessage } from "./constant.js";
import { history } from "../router";
import { notification } from "antd";
const request = axios.create({
timeout: 5000,
// 对params进行序列化,delete 和 get 请求就可以传递数组类型的参数
paramsSerializer: function (params) {
// indices: false 传入 ids: [1, 2, 3] 体现形式:ids=1&ids=2
// arrayFormat: 'brackets' 传入 ids: [1, 2, 3] 体现形式:ids[]=1&ids[]=2&ids[]=3
// arrayFormat: 'repeat' 传入 ids: [1, 2, 3] 体现形式: ids=1&ids=2&ids=3
// 此项配置 体现形式 ids[0]=1&ids[1]=2
return Qs.stringify(params, { arrayFormat: "repeat" });
},
withCredentials: true,
});
// 此处判断用来更改基础 url前缀
console.log(process.env.NODE_ENV);
if (process.env.NODE_ENV === "production") {
// 生产环境 production
request.defaults.baseURL = process.env.REACT_APP_BASE_URL || "/save/api/";
} else if (process.env.NODE_ENV === "test") {
// 测试环境 test
request.defaults.baseURL = process.env.REACT_APP_BASE_URL || "/test/api/";
} else {
// 开发环境 development
request.defaults.baseURL = "https://mock.mengxuegu.com/mock/6136f5af5c91b95c0946d275/api";;
}
// 请求拦截器
request.interceptors.request.use(
(config) => {
// 每次发送请求之前判断是否存在token,如果存在,则统一在request请求的header都加上token,不用每次请求都手动添加了
// 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
// const token = store.state.token;
// token && (config.headers.Authorization = token);
if (sessionStorage.getItem("Authorization")) {
config.headers.Authorization = sessionStorage.getItem("Authorization");
}
// if (["post", "put"].includes(config.method.toLocaleLowerCase())) {
// // 参数统一处理,请求都使用data传参
// config.data = config.data.data;
// } else if (["get", "delete"].includes(config.method.toLocaleLowerCase())) {
// // 参数统一处理
// config.params = config.data;
// delete config.data;
// } else {
// alert("不允许的请求方法:" + config.method);
// }
// 根据请求方式更改请求头
config.headers = Object.assign(
config.method.toLocaleLowerCase() === "get"
? {
Accept: "application/json",
"Content-Type": "application/json; charset=UTF-8",
}
: {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
// "Content-Type": "application/json; charset=UTF-8",
// "x-csrf-token": getCookie("csrfToken")
},
config.headers
);
removePendingRequest(config);
addPendingRequest(config);
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截
request.interceptors.response.use(
async (response) => {
removePendingRequest(response.config);
const { data, code, message } = response.data || {};
if (response && response.data) {
if (response.data instanceof Blob) {
const down = new Download(response);
const { code, message } = await down.inspect();
if (code === 200) {
down.xlsx();
return { code, message };
}
notification.error({
message: "下载失败",
description: code + ":" + message,
});
return { code, message };
} else {
// 此处做一个接口成功响应 但操作失败全局提示
if (code !== 200) {
notification.error({
message: "操作失败",
});
}
}
}
return {
data: data || undefined, // 这里之所以用undefined,作为默认值是因为后端可能会返回为null,而null是不能给默认值的,避免成功后我们在数据处理代码中将result赋值对应类型值后直接使用方法造成报错
code,
message,
};
},
(error) => {
const { data, status } = error.response || {};
const { message } = data || {};
console.log(message);
removePendingRequest(error.config || {});
if (axios.isCancel(error)) {
console.log("已取消的重复请求: " + error.message);
} else {
if (status === 401) {
notification.error({
message: "授权过期",
description: "请重新登录!",
});
sessionStorage.removeItem("Authorization");
sessionStorage.removeItem("userId");
//重新请求逻辑....
//store.dispatch('user/resetToken').then(() => {
//location.reload()
//})
history.replace("/login");
// window.location.href = "/login"; // 此项会造成页面刷新 不要使用
} else {
notification.error({
message: status,
description: status + ":" + codeMessage[status],
});
}
}
return Promise.reject(error);
}
);
export default request;
//.method.js
/*
* @Descripttion:
* @Date: 2022-03-16 10:16:43
*/
// /src/http/tool.method.js
import Qs from "qs";
import axios from "axios";
/**
* 获取cookie 上的csrfToken
* @param name {string} 要获取的参数名
* */
export function getCookie(name) {
const str = document.cookie.split("; ");
for (let i = 0; i < str.length; i++) {
const temp = str[i].split("=");
if (temp[0] === name) return unescape(temp[1]);
}
return "";
}
/**
* 请求记录map
* @type {Map}
*/
const pendingRequest = new Map();
/**
* 用于把当前请求信息添加到pendingRequest对象中
* @param config {object} 请求对象
*/
export function addPendingRequest(config) {
const requestKey = generateReqKey(config);
config.cancelToken =
config.cancelToken ||
new axios.CancelToken((cancel) => {
if (!pendingRequest.has(requestKey)) {
pendingRequest.set(requestKey, cancel);
}
});
}
/**
* 用于根据当前请求的信息,生成请求 Key
* @param config {object} 请求对象
*/
export function generateReqKey(config) {
const { method, url, params, data } = config;
return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&");
}
/**
* 删除请求
* @param config {object} 请求对象
*/
export function removePendingRequest(config) {
const requestKey = generateReqKey(config);
if (pendingRequest.has(requestKey)) {
const cancelToken = pendingRequest.get(requestKey);
cancelToken(requestKey);
pendingRequest.delete(requestKey);
}
}
/**
* 下载类
* setName 根据请求体自动设置文件名 如 需要自定义 请直接使用 实列filename赋值
* zip 压缩包 传入blob文件流 完成后自动执行下载方法
* xlsx exel表格文件 传入blob文件流 完成后自动执行下载方法
* down 下载方法
*
*/
export class Download {
constructor(response) {
this.blob = {};
this.filename = "";
this.response = response;
}
/**
* 检测是否为 文件流
* @param {Object|Blob} [responseData]
*/
inspect(responseData) {
return new Promise((resolve) => {
let { data } = this.response || {};
if (!data && responseData) {
data = responseData;
} else if (!data && !responseData) {
resolve({
code: 600,
message: "检测到必要变量不存在",
});
}
let file = new FileReader();
file.onload = function (event) {
const result = event.target.result;
try {
const value = JSON.parse(result);
resolve(value);
} catch (e) {
// 此处表示 当前为数据流
resolve({
code: 200,
message: "下载成功",
});
}
};
file.readAsText(data);
});
}
/**
* 默认自动设置文件名 需要传一个备选文件名;
* @param {Object} [request] 请求体
* @param {string} [customName] 备选文件名 默认值新文件
*/
autoFileName(request, customName = "新文件") {
let disposition = null;
if (this.response && !request) {
disposition = this.response.headers
? this.response.headers["content-disposition"]
: null;
} else if (request && !this.response) {
disposition = request.headers
? request.headers["content-disposition"]
: null;
}
if (disposition) {
let name = disposition.split("=")[1];
if (name) return (this.filename = decodeURI(name));
this.filename = customName + new Date().getTime();
}
}
/**
* 压缩包
* @param {Blob} [blobData] 文件流
*/
zip(blobData) {
let { data } = this.response || {};
if (!data && blobData) {
data = blobData;
}
this.blob = new Blob([data], { type: "zip;charset=utf-8" });
this.autoFileName();
this.down();
}
/**
* xlsx 文件格式
* @param {Blob} [blobData] 文件流
*/
xlsx(blobData) {
let { data } = this.response || {};
if (!data && blobData) {
data = blobData;
}
this.blob = new Blob([data], { type: "application/vnd.ms-excel" });
this.autoFileName();
this.down();
}
/**
* 下载 最终调用
*/
down() {
let url = window.URL.createObjectURL(this.blob);
let link = document.createElement("a");
link.style.display = "none";
link.href = url;
link.download = `${this.filename}`;
document.body.appendChild(link);
link.click();
window.URL.revokeObjectURL(link.href);
document.body.removeChild(link);
}
}
//constant.js
export const codeMessage = {
200: '服务器成功返回请求的数据。',
201: '新建或修改数据成功。',
202: '一个请求已经进入后台排队(异步任务)。',
204: '删除数据成功。',
400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
401: '用户没有权限(令牌、用户名、密码错误)。',
403: '用户得到授权,但是访问是被禁止的。',
404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
406: '请求的格式不可得。',
410: '请求的资源被永久删除,且不会再得到的。',
422: '当创建一个对象时,发生一个验证错误。',
500: '服务器发生错误,请检查服务器。',
502: '网关错误。',
503: '服务不可用,服务器暂时过载或维护。',
504: '网关超时。',
};
/**axios封装
* 请求拦截、相应拦截、错误统一处理
*/
import axios from 'axios';import QS from 'qs';
import { Toast } from 'vant';
import store from '../store/index'
// 环境的切换
if (process.env.NODE_ENV == 'development') {
axios.defaults.baseURL = '/api';
} else if (process.env.NODE_ENV == 'debug') {
axios.defaults.baseURL = '';
} else if (process.env.NODE_ENV == 'production') {
axios.defaults.baseURL = 'http://api.123dailu.com/';
}
// 请求超时时间
axios.defaults.timeout = 10000;
// post请求头
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
// 请求拦截器
axios.interceptors.request.use(
config => {
// 每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了
// 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
const token = store.state.token;
token && (config.headers.Authorization = token);
return config;
},
error => {
return Promise.error(error);
})
// 响应拦截器
axios.interceptors.response.use(
response => {
if (response.status === 200) {
return Promise.resolve(response);
} else {
return Promise.reject(response);
}
},
// 服务器状态码不是200的情况
error => {
if (error.response.status) {
switch (error.response.status) {
// 401: 未登录
// 未登录则跳转登录页面,并携带当前页面的路径
// 在登录成功后返回当前页面,这一步需要在登录页操作。
case 401:
router.replace({
path: '/login',
query: { redirect: router.currentRoute.fullPath }
});
break;
// 403 token过期
// 登录过期对用户进行提示
// 清除本地token和清空vuex中token对象
// 跳转登录页面
case 403:
Toast({
message: '登录过期,请重新登录',
duration: 1000,
forbidClick: true
});
// 清除token
localStorage.removeItem('token');
store.commit('loginSuccess', null);
// 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面
setTimeout(() => {
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
});
}, 1000);
break;
// 404请求不存在
case 404:
Toast({
message: '网络请求不存在',
duration: 1500,
forbidClick: true
});
break;
// 其他错误,直接抛出错误提示
default:
Toast({
message: error.response.data.message,
duration: 1500,
forbidClick: true
});
}
return Promise.reject(error.response);
}
}
);
/**
* get方法,对应get请求
* @param {String} url [请求的url地址]
* @param {Object} params [请求时携带的参数]
*/
export function get(url, params){
return new Promise((resolve, reject) =>{
axios.get(url, {
params: params
})
.then(res => {
resolve(res.data);
})
.catch(err => {
reject(err.data)
})
});
}
/**
* post方法,对应post请求
* @param {String} url [请求的url地址]
* @param {Object} params [请求时携带的参数]
*/
export function post(url, params) {
return new Promise((resolve, reject) => {
axios.post(url, QS.stringify(params))
.then(res => {
resolve(res.data);
})
.catch(err => {
reject(err.data)
})
});
}
个人笔记