随着项目规模增大,如果每发起一次HTTP请求,就要把这些比如设置超时时间、设置请求头、根据项目环境判断使用哪个请求地址、错误处理等等操作,都需要写一遍
这种重复劳动不仅浪费时间,而且让代码变得冗余不堪,难以维护。为了提高我们的代码质量,我们应该在项目中二次封装一下 axios 再使用
请求步骤:发起请求--请求拦截(进行请求头等信息处理)--获取到数据--响应拦截(对返回信息进行处理判断)--得到处理后的数据 (axios中文文档 (javasoho.com))
import axios from "axios";
import store from "../store";
import { Message, MessageBox } from "element-ui";
import db from "@/utils/localstorage";
import { Base64 } from "js-base64";
// 请求添加条件,如token
axios.interceptors.request.use(
config => {
const isToken =
config.headers["X-isToken"] === false
? config.headers["X-isToken"]
: true;
const token = db.get("TOKEN", "");
if (token && isToken) {
config.headers.token = "Bearer " + token;
}
//以上在请求拦截中对header进行处理
const isTenant =
config.headers["X-isTenant"] === false
? config.headers["X-isTenant"]
: true;
if (isTenant && process.env.VUE_APP_IS_MULTI_TENANT_TYPE !== "NONE") {
config.headers.tenant = db.get("TENANT", "");
}
const clientId = process.env.VUE_APP_CLIENT_ID;
const clientSecret = process.env.VUE_APP_CLIENT_SECRET;
config.headers["Authorization"] = `Basic ${Base64.encode(
`${clientId}:${clientSecret}`
)}`;
//设置内容类型
config.headers["Content-Type"] = "application/json";
}
return config;
},
error => {
return Promise.reject(error);
}
);
// 接口返回处理
axios.interceptors.response.use(
response => {
return response;
},
error => {
return Promise.reject(error);
}
);
//也可以放在响应拦截中
function handleError(error, reject, opts) {
// debugger // 方便network TODO: 20210910 开发暂时停止
let isAlert = opts.custom ? opts.custom["isAlert"] : true;
isAlert = isAlert === undefined ? true : isAlert;
if (isAlert) {
if (error.code === "ECONNABORTED") {
Message({
message: "请求超时"
});
} else if (error.response && error.response.data) {
const resData = error.response.data;
//对状态码进行判断
if (error.response.status === 403 || error.response.status === 401) {
MessageBox.alert(resData.msg || "登录已失效,请重新登录", "提醒", {
confirmButtonText: "确定",
callback: () => {
db.clear();
window.location.hash = "/login";
}
});
} else if (error.response.status === 500) {
Message({
message: error.response.data
});
} else {
//对返回的code进行判断
const resData = error.response.data;
if (
resData.code === 40000 ||
resData.code === 40001 ||
resData.code === 40002 ||
resData.code === 40003 ||
resData.code === 40005 ||
resData.code === 40006 ||
resData.code === 40008 ||
resData.code === 40009
) {
MessageBox.alert(resData.msg || resData.message, "提醒", {
confirmButtonText: "确定",
callback: () => {
db.clear();
window.location.hash = "/login";
}
});
} else if (resData.msg) {
Message({
message: resData.msg
});
} else if (resData.message) {
Message({
message: resData.message
});
}
}
} else if (error.message) {
Message({
message: error.message
});
}
}
reject(error);
}
function handleSuccess(res, resolve, opts) {
let isAlert = opts.custom ? opts.custom["isAlert"] : true;
isAlert = isAlert === undefined ? true : isAlert;
const resData = res.data;
if (resData.isSuccess === false) {
// 未登录
if (
resData.code === 40000 ||
resData.code === 40001 ||
resData.code === 40002 ||
resData.code === 40003 ||
resData.code === 40005 ||
resData.code === 40006 ||
resData.code === 40008
) {
debugger;
MessageBox.alert(resData.msg, "提醒", {
confirmButtonText: "确定",
callback: () => {
window.location.hash = "/login";
}
});
} else {
if (isAlert) {
Message.error(resData.msg);
}
}
}
resolve(res);
}
// http请求
const httpServer = opts => {
// 公共参数
const publicParams = {
ts: Date.now()
};
// http默认配置
const method = opts.method.toUpperCase();
// baseURL
// 开发环境: /api // 开发环境在 vue.config.js 中有 devServer.proxy 代理
// 生产环境: http://IP:PORT/api // 生产环境中 代理失效, 故需要配置绝对路径
//对请求参数进行处理
const httpDefaultOpts = {
method,
//baseURL根据环境不同选择不同的baseURL
baseURL:
process.env.VUE_APP_PROD_REQUEST_DOMAIN_PREFIX +
process.env.VUE_APP_BASE_API,
url: opts.url,
responseType: opts.responseType || "",
// timeout: (opts.custom && opts.custom["timeout"]) || 30000
timeout: 30 * 60 * 1000 //链接请求改为30min
};
if (opts["meta"]) {
httpDefaultOpts.headers = opts["meta"];
}
//对不同的请求方法进行处理
const dataRequest = ["PUT", "POST", "DELETE", "PATCH"];
if (dataRequest.includes(method)) {
httpDefaultOpts.data = opts.data || {};
httpDefaultOpts.params = publicParams;
} else {
httpDefaultOpts.params = {
...publicParams,
...(opts.data || {})
};
}
// formData转换
if (opts.formData) {
httpDefaultOpts.transformRequest = [
data => {
const formData = new FormData();
if (data) {
Object.entries(data).forEach(item => {
formData.append(item[0], item[1]);
});
}
return formData;
}
];
}
const promise = new Promise((resolve, reject) => {
axios(httpDefaultOpts)
.then(response => {
handleSuccess(response, resolve, opts);
})
.catch(error => {
handleError(error, reject, opts);
});
});
return promise;
};
export default httpServer;
使用:
import axiosApi from "../AxiosApi.js";
export default {
getFirstUnitClass(data) {
return axiosApi({
method: "get",
url: `receive/transferUnit/top`,
data
});
},
}
在需要使用当前接口的vue文件中导入此文件 request.getFirstUnitClass(params).then()
简易版:
import axios, { AxiosInstance } from 'axios';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Session } from '/@/utils/storage';
import qs from 'qs';
// 配置新建一个 axios 实例
const service: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_URL,
timeout: 50000,
headers: { 'Content-Type': 'application/json' },
paramsSerializer: {
serialize(params) {
return qs.stringify(params, { allowDots: true });
},
},
});
// 添加请求拦截器
service.interceptors.request.use(
(config) => {
// 在发送请求之前做些什么 token
if (Session.get('token')) {
config.headers!['Authorization'] = `${Session.get('token')}`;
}
return config;
},
(error) => {
// 对请求错误做些什么
return Promise.reject(error);
}
);
// 添加响应拦截器
service.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(service.interceptors.response);
} else {
return res;
}
},
(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 service;
使用:
import request from '/@/utils/request';
/**
* (不建议写成 request.post(xxx),因为这样 post 时,无法 params 与 data 同时传参)
*
* 登录api接口集合
* @method signIn 用户登录
* @method signOut 用户退出登录
*/
export function useLoginApi() {
return {
signIn: (data: object) => {
return request({
url: '/user/signIn',
method: 'post',
data,
});
},
signOut: (data: object) => {
return request({
url: '/user/signOut',
method: 'post',
data,
});
},
};
}