axios封装 可请求不同地址
Sonder
2023-05-23
8598字
21分钟
浏览 (1.1k)
实现功能:针对不同请求设置token、code不为成功时的信息提示、code异常情况下的异常处理、屏蔽某些接口、简洁的数据结构响应。同时修改baseURL
可以请求不同地址,让接口不再限制于一个链接。
需要下载vant
、js-cookie
,主要是为做移动端封装的。
接口返回格式是:
{
data: {
body:[]
},
errno: 0,
error:"",
}
request.js
import axios from 'axios'
import {Toast} from 'vant'
import {config} from '@/config'
import cookie from "js-cookie";
// export interface RequestOptions {
// baseURL?: string;// 后端接口地址,适配接口多域名情况
// isToken?: boolean;// 是否携带token
// repeatRequestCancel?: boolean;// 是否开启取消重复请求
// loading?: boolean;// 是否开启loading层效果
// reductDataFormat?: boolean;// 是否开启简洁的数据结构响应
// errorMessageShow?: boolean;// 是否开启接口错误信息展示
// codeMessageShow?: boolean;// 是否开启code不为0时的信息提示
// }
const pendingMap = new Map()
const loadingInstance = {
_target: null,
_count: 0
}
function setBaseURL(name, prefix) {
return config(prefix)[name]
}
function getCookieKey() {
return config().COOKIE_KEY
}
function request(
axiosConfig,
customOptions = {},
loadingOptions = {}
) {
const service = axios.create({
timeout: 30000 // 设置统一的超时时长
})
// 自定义配置
const custom_options = Object.assign({
baseURL: null, // 后端接口地址,适配接口多域名情况
isToken: true, // 是否携带token
repeatRequestCancel: false, // 是否开启取消重复请求
loading: true, // 是否开启loading层效果
reductDataFormat: true, // 是否开启简洁的数据结构响应
errorMessageShow: true, // 是否开启接口错误信息展示
codeMessageShow: true // 是否开启code不为0时的信息提示
}, customOptions)
// 请求拦截
service.interceptors.request.use(
config => {
config.baseURL = custom_options.baseURL || setBaseURL('BASE_URL', customOptions.prefix)
removePending(config)
custom_options.repeatRequestCancel && addPending(config)
// 创建loading实例
if (custom_options.loading) {
loadingInstance._count++
if (loadingInstance._count === 1) {
if (loadingOptions?.message) {
loadingInstance._target = Toast(loadingOptions)
} else {
loadingInstance._target = Toast.loading({
duration: 0,
forbidClick: true
})
}
}
}
// 自动携带token
if (custom_options.isToken) {
if (config && config.headers) { // 多一步判断
config.headers['Authorization'] = `${cookie.get(getCookieKey()) || ''}`; // 请求携带自定义token 请根据实际情况自行修改
}
}
return config
},
error => {
return Promise.reject(error)
}
)
// 响应拦截
service.interceptors.response.use(
response => {
removePending(response.config)
custom_options.loading && closeLoading(custom_options) // 关闭loading
const {errno, error} = response.data
// 关于code码的判断自行修改
if (errno === '401') {
let redirect_uri = encodeURIComponent(window.location.href);
window.location.replace('/login?redirect_uri=' + redirect_uri)
return Promise.reject(response.data)
} else if (errno === '402') { // openid没有绑定
return Promise.reject(response.data)
} else if (custom_options.codeMessageShow && response.data && +errno !== 0) {
Toast({
type: 'fail',
message: error
})
return Promise.reject(response.data)
} else {
return custom_options.reductDataFormat ? response.data : response
}
},
error => {
error.config && removePending(error.config)
custom_options.loading && closeLoading(custom_options) // 关闭loading
custom_options.errorMessageShow && httpErrorStatusHandle(error) // 处理错误状态码
return Promise.reject(error) // 错误继续返回给到具体页面
}
)
return service(axiosConfig)
}
export default {
request,
get: (url,
customOptions = {},
loadingOptions = {}) => request({
url,
method: 'get',
}, customOptions, loadingOptions),
post: (url,
data,
customOptions = {},
loadingOptions = {}) => request({
url,
method: 'post',
data
}, customOptions, loadingOptions)
}
/**
* 处理异常
* @param {*} error
*/
function httpErrorStatusHandle(error) {
// 处理被取消的请求
if (axios.isCancel(error)) return console.error('请求的重复请求:' + error.message)
let message = ''
if (error && error.response) {
switch (error.response.status) {
case 302:
message = '接口重定向了!';
break
case 400:
message = '参数不正确!';
break
case 401:
message = '您未登录,或者登录已经超时,请先登录!';
break
case 403:
message = '您没有权限操作!';
break
case 404:
message = `请求地址出错: ${error.response.config.url}`;
break // 在正确域名下
case 408:
message = '请求超时!';
break
case 409:
message = '系统已存在相同数据!';
break
case 500:
message = '服务器内部错误!';
break
case 501:
message = '服务未实现!';
break
case 502:
message = '网关错误!';
break
case 503:
message = '服务不可用!';
break
case 504:
message = '服务暂时无法访问,请稍后再试!';
break
case 505:
message = 'HTTP版本不受支持!';
break
default:
message = '异常问题,请联系管理员!';
break
}
}
if (error.message.includes('timeout')) message = '网络请求超时!'
if (error.message.includes('Network')) message = window.navigator.onLine ? '服务端异常!' : '您断网了!'
Toast({
type: 'fail',
message
})
}
/**
* 关闭Loading层实例
* @param {*} options
*/
function closeLoading(options) {
if (options.loading && loadingInstance._count > 0) loadingInstance._count--
if (loadingInstance._count === 0) {
loadingInstance._target.close()
loadingInstance._target = null
}
}
/**
* 储存每个请求的唯一cancel回调, 以此为标识
* @param {*} config
*/
function addPending(config) {
const pendingKey = getPendingKey(config)
config.cancelToken = config.cancelToken || new axios.CancelToken((cancel) => {
if (!pendingMap.has(pendingKey)) {
pendingMap.set(pendingKey, cancel)
}
})
}
/**
* 删除重复的请求
* @param {*} config
*/
function removePending(config) {
const pendingKey = getPendingKey(config)
if (pendingMap.has(pendingKey)) {
const cancelToken = pendingMap.get(pendingKey)
// 如你不明白此处为什么需要传递pendingKey可以看文章下方的补丁解释
cancelToken(pendingKey)
pendingMap.delete(pendingKey)
}
}
/**
* 生成唯一的每个请求的唯一key
* @param {*} config
* @returns
*/
function getPendingKey(config) {
let {data} = config
const {url, method, params} = config
if (typeof data === 'string') data = JSON.parse(data) // response里面返回的config.data是个字符串对象
return [url, method, JSON.stringify(params), JSON.stringify(data)].join('&')
}
config.js
export const production = "prod.xxxx.com"
export const development = "dev.xxxx.com"
export const isProd = window.location.host.includes(production);
export const isDevelopment = () => {
return !isProd
}
const prefix = "https://"
const ROUTE = "/api/home"
// const ROUTE = "/api/admin"
export const config = (routePrefix = "") => {
const COOKIE_KEY = isDevelopment() ? "COOKIE_KEY_dev" : "COOKIE_KEY_prod"
const FIELD_PREFIX = isDevelopment() ? "dev" : "prod"
routePrefix = routePrefix || ROUTE
if (isProd) {
return {
BASE_URL: `${prefix}${production}${routePrefix}`,
COOKIE_KEY,
FIELD_PREFIX
};
}
return {
BASE_URL: `${prefix}${development}${routePrefix}`,
COOKIE_KEY,
FIELD_PREFIX
};
};
使用方法
import request from '@/utils/request';
import {config} from '@/config';
// dev.xxxx.com/v/wx/config?id=1
export const test1 = (id) => request.get(`/v/wx/config?id=${id}`, {
baseURL: null, // 后端接口地址,适配接口多域名情况
isToken: true, // 是否携带token
rawaitepeatRequestCancel: false, // 是否开启取消重复请求
loading: true, // 是否开启loading层效果
reductDataFormat: true, // 是否开启简洁的数据结构响应
errorMessageShow: true, // 是否开启接口错误信息展示
codeMessageShow: true // 是否开启code不为0时的信息提示
})
// index.xxx.com/api/wx/test2
export const test2 = (data) => request.post(`/wx/test2`, data, {
baseURL:'index.xxx.com/api'
})
// dev.xxxx.com/test/user/wx/config
export const getConfig = (data) => request.post(`/wx/config`, data, {
loading: false, // 是否开启loading层效果
isToken: false, // 是否携带token
baseURL:config("/test/user").BASE_URL
})