封装思路
index.ts
咱们须要在src的根目录下创立一个axios文件夹,其中创立一个index.ts文件,这个文件次要用来封装axios的配置(实例化申请配置、申请拦截器、相应拦截器)及相应的办法(登录跳转、音讯提醒、错误处理等)
base.ts
这个文件次要用于我的项目扩大的状况下 不同模块须要调用不同接口(申请的base地址 baseURL )而后期做的筹备,便于前期的保护
request.ts
次要用于封装基于axios配置的get/post/put/delete等应用办法。
api.ts
在前面的 main.ts 中引入该模块,包含所有接口数据信息写入该文件中。
index.ts
封装如下。思考到繁多职责,index这块只封装axios
// index.ts
import axios, { AxiosRequestConfig, Method } from "axios";
import router from "@/router";
import store from "@/store";
import { message } from 'ant-design-vue'
import { storage } from "../storage/storage";
import { dataList } from "@/components/aspin/data";
/**
* 跳转登录页
* 携带以后页面路由,以期在登录页面实现登录后返回以后页面
*/
const toLogin = () => {
router.replace({
name: 'LoginPage',
});
}
/**
* 申请失败后的谬误对立解决
* @param {Number} status 申请失败的状态码
*/
const errorHandle = (status: number, other: string) => {
// 状态码判断
switch (status) {
case 302: message.error('接口重定向了!');
break;
case 400:
message.error("收回的申请有谬误,服务器没有进行新建或批改数据的操作==>" + status)
break;
// 401: 未登录
// 未登录则跳转登录页面,并携带以后页面的门路
// 在登录胜利后返回以后页面,这一步须要在登录页操作。
case 401: //重定向
message.error("token:登录生效==>" + status + ":" + store.state.Roles)
storage.remove(store.state.Roles)
storage.get(store.state.Roles)
router.replace({
path: '/Login',
});
break;
// 403 token过期
// 革除token并跳转登录页
case 403:
message.error("登录过期,用户失去受权,然而拜访是被禁止的==>" + status)
// store.commit('token', null);
setTimeout(() => {
router.replace({
path: '/Login',
});
}, 1000);
break;
case 404:
message.error("网络申请不存在==>" + status)
break;
case 406:
message.error("申请的格局不可得==>" + status)
break;
case 408: message.error(" 申请超时!")
break;
case 410:
message.error("申请的资源被永恒删除,且不会再失去的==>" + status)
break;
case 422:
message.error("当创立一个对象时,产生一个验证谬误==>" + status)
break;
case 500:
message.error("服务器产生谬误,请查看服务器==>" + status)
break;
case 502:
message.error("网关谬误==>" + status)
break;
case 503:
message.error("服务不可用,服务器临时过载或保护==>" + status)
break;
case 504:
message.error("网关超时==>" + status)
break;
default:
message.error("其余谬误谬误==>" + status)
}
}
// 定义接口
interface PendingType {
url?: string;
method?: Method;
params: any;
data: any;
cancel: any;
}
// 勾销反复申请
const pending: Array<PendingType> = [];
const CancelToken = axios.CancelToken;
// 移除反复申请
const removePending = (config: AxiosRequestConfig) => {
for (const key in pending) {
const item: number = +key;
const list: PendingType = pending[key];
// 以后申请在数组中存在时执行函数体
if (list.url === config.url && list.method === config.method && JSON.stringify(list.params) === JSON.stringify(config.params) && JSON.stringify(list.data) === JSON.stringify(config.data)) {
// 执行勾销操作
list.cancel('操作太频繁,请稍后再试');
// 从数组中移除记录
pending.splice(item, 1);
}
}
};
/* 实例化申请配置 */
const instance = axios.create({
headers: {
//php 的 post 传输申请头肯定要这个 不然报错 接管不到值
"Content-Type": "application/json;charset=UTF-8",
"Access-Control-Allow-Origin-Type": '*'
},
// 申请时长
timeout: 1000 * 30,
// 申请的base地址 TODO:这块当前依据不同的模块调不同的api
baseURL: process.env.VUE_APP_API_URL,
// ? "测试"
// : "正式",
// 示意跨域申请时是否须要应用凭证
withCredentials: false,
})
/**
* 申请拦截器
* 每次申请前,如果存在token则在申请头中携带token
*/
instance.interceptors.request.use(
config => {
removePending(config);
config.cancelToken = new CancelToken((c) => {
pending.push({ url: config.url, method: config.method, params: config.params, data: config.data, cancel: c });
});
// 登录流程管制中,依据本地是否存在token判断用户的登录状况
// 然而即便token存在,也有可能token是过期的,所以在每次的申请头中携带token
// 后盾依据携带的token判断用户的登录状况,并返回给咱们对应的状态码
// 而后咱们能够在响应拦截器中,依据状态码进行一些对立的操作。
// const token = store.state.token;
// localStorage.setItem('token', token);
if (storage.get(store.state.Roles)) {
store.state.Roles
config.headers.Authorization = storage.get(store.state.Roles);
}
return config;
},
error => {
message.error(error.data.error.message);
return Promise.reject(error.data.error.message);
}
)
// 响应拦截器
instance.interceptors.response.use(function (config) {
dataList.show = true
removePending(config.config);
// 申请胜利
if (config.status === 200 || config.status === 204) {
setTimeout(() => {
dataList.show = false
}, 400)
return Promise.resolve(config);
} else {
return Promise.reject(config);
}
// 申请失败
}, function (error) {
const { response } = error;
if (response) {
errorHandle(response.status, response.data.message);
// 超时从新申请
const config = error.config;
// 全局的申请次数,申请的间隙
const [RETRY_COUNT, RETRY_DELAY] = [3, 1000];
if (config && RETRY_COUNT) {
// 设置用于跟踪重试计数的变量
config.__retryCount = config.__retryCount || 0;
// 查看是否曾经把重试的总数用完
if (config.__retryCount >= RETRY_COUNT) {
return Promise.reject(response || { message: error.message });
}
// 减少重试计数
config.__retryCount++;
// 发明新的Promise来解决指数后退
const backoff = new Promise<void>((resolve) => {
setTimeout(() => {
resolve();
}, RETRY_DELAY || 1);
});
// instance重试申请的Promise
return backoff.then(() => {
return instance(config);
});
}
return Promise.reject(response);
} else {
// 解决断网的状况
// eg:申请超时或断网时,更新state的network状态
// network状态在app.vue中管制着一个全局的断网提醒组件的显示暗藏
// 后续减少断网状况下做的一些操作
store.commit('networkState', false);
}
}
)
// 只须要思考繁多职责,这块只封装axios
export default instance
base.ts
辨别每个模块的 baseUrl 不便前期保护治理
// base.ts
export class Base {
/* 公共模块 */
static env = process.env.NODE_ENV === "development"
? "http://localhost:8087"
: "https://produceCommon.com(生产线地址)"
}
也能够间接在index.ts中设置这样就不须要base.ts
const instance = axios.create({
// 申请的base地址 TODO:这块当前依据不同的模块调不同的api
baseURL: process.env.VUE_APP_API_URL,
})
须要配置根目录
.env.development
NODE_ENV = 'development'
# VUE_APP_API_URL = 'https://localhost:5001/'
VUE_APP_API_URL = 'http://localhost:8087/'
.env.production
# 生产环境的申请接口
NODE_ENV = 'production'
VUE_APP_API_URL = 'http://129.xxxxx/'
request.ts
封装axios的get、post办法,其余对于接口调用的办法也可写入该文件中,便于管理。
// request.ts
import axios from "./index";
import qs from "qs";
export class Request {
/**
* get办法
* @param {string} url 门路
* @param {object} params 参数
*/
static get = (url: string, params?: any) => {
return new Promise((resolve, reject) => {
axios.get(url, { params: params }).then(res => {
resolve(res);
}).catch(err => {
reject(err);
})
})
}
static post = (url: string, params?: any) => {
return new Promise((resolve, reject) => {
axios.post(url, qs.stringify(params)).then(res => {
resolve(res);
}).catch(err => {
reject(err);
})
})
}
}
api.ts
vue页面须要应用的api接口
// 其中应用 install 的目标在于 ts在main.ts中
// 不能通过Vue.prototype.$Api这个形式间接调用
//,在全局办法中会说到应用 插件的形式去挂载。
// api.ts
import { Base } from "./base";
import { Request } from "./request";
class api {
/* api接口模块 */
public static article = {
// 间接在index.ts中设置不须要Base模块
genre: () => Request.get('/api/SnArticle/GetCountAsync'),
// 基于Base模块封装调用
genres: () => Request.get(`${Base.env}/api/SnArticle/GetCountAsync`),
}
}
export {
api
}
index.vue
import { api } from '../../utils/api/api'
import { onMounted } from 'vue'
onMounted(async () => {
await QueryAll()
api.article.genre().then((res: any) => {
console.log('genre' + res.data)
})
api.article.genres().then((res: any) => {
console.log('genres' + res.data)
})
})
参考
https://blog.csdn.net/qq_4003…
发表回复