一、特点
- 可以类似axios一样对网络请求的统一拦截
- 统一使用Promise方式返回
- 文件上传和网络请求使用统一拦截器处理
- 支持请求的取消
二、使用教程
1.新建HttpUtil.js文件
代码如下所示:
//构建短的uuid
function generateShortUUID() {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let shortUUID = '';
for (let i = 0; i < 8; i++) {
const randomIndex = Math.floor(Math.random() * chars.length);
shortUUID += chars.charAt(randomIndex);
}
return shortUUID;
}
//二次封装网络请求
export default class HttpUtil {
//请求的默认配置
#config = {
baseUrl: "",
timeout: 60000
};
//请求任务队列,结构如下:{type:{uuid:task}}
#requestTask = {};
//拦截器
#interceptors = {
preRequest: undefined,
preReponse: undefined,
errorReponse: undefined
}; //拦截器,支持单个拦截够用了
constructor(config) {
if (config) {
this.#config = config;
}
this.#requestTask = {};
}
get(url, data) {
return this.request({
url,
data,
method: "GET",
});
}
post(url, data) {
return this.request({
url,
data,
method: "POST",
});
}
put(url, data) {
return this.request({
url,
data,
method: "PUT",
});
}
delete(url, data) {
return this.request({
url,
data,
method: "DELETE",
});
}
/**
* 拦截器-发送请求前
* @param {Promise} preRequest 返回修改后的config
*/
useRequestInterceptor(preRequest) {
this.#interceptors.preRequest = preRequest;
}
/**
* 拦截器-得到响应结果前
* @param {Promise} onSuccess 必须返回一个Promise对象
* @param {Promise} onError 必须返回一个Promise对象
*/
useReponseInterceptor(onSuccess, onError) {
this.#interceptors.preReponse = onSuccess;
this.#interceptors.errorReponse = onError;
}
/**
* 发送请求
* @param {*} config wx.request参数配置
* @param {String} type 可以用于取消请求,细看:当前类的abort方法
*/
request(config, type) {
return this.#httpTask(config, false, type);
}
/**
* 文件上传
* @param {String} url 地址
* @param {String} name 文件对应的字段名
* @param {String} filePath 文件地址
* @param {*} otherConfig 其他配置
* @param {Function} onProgressUpdate 进度监听
* @param {String} type 可以用于取消请求,细看:当前类的abort方法
*
*/
uploadFile(url, name, filePath, otherConfig, onProgressUpdate, type) {
const config = {
...otherConfig,
url,
name,
filePath,
onProgressUpdate,
};
return this.#httpTask(config, true, type);
}
/**
* 取消请求,按照type作为大类,相同type的都统一被取消掉
* 使用场景:页面切换,当前页面发起的,但是没有完成的请求都没用了,就可以取消掉
* 例如:我们定义了图片上传页面的type值为:uploadImg,但是页面离开了,你就可以在生命周期的onUnload函数中调用httpUtil.abort("uploadImg"),将还没有上传的完成的图片取消掉
* @param {*} type 分类值
*/
abort(type = "default") {
const tasks = this.#requestTask[type];
if (tasks) {
for (const uuid in tasks) {
const task = tasks[uuid];
task.abort();
}
delete this.#requestTask[type];
}
}
/**
* 添加请求任务
* @param {String} type 分类
* @param {String} uuid 唯一标识
* @param {*} requestTask 请求任务
*/
#requestTaskAdd(type, uuid, requestTask) {
if (!this.#requestTask[type]) {
this.#requestTask[type] = {};
}
this.#requestTask[type][uuid] = requestTask;
}
/**
* 移除请求任务
* @param {String} type 分类
* @param {String} uuid 唯一标识
*/
#requestTaskremove(type, uuid) {
if (this.#requestTask[type] && this.#requestTask[type][uuid]) {
delete this.#requestTask[type][uuid];
}
}
/**
* 统一发送请求处理
* @param {*} config 配置参考wx.request
* @param {*} isFile 是否是文件上传
* @param {*} type 分类
*/
#httpTask(config, isFile = false, type = "default") {
return new Promise((resolve, reject) => {
const uuid = generateShortUUID();
config.timeout = config.timeout || this.#config.timeout;
//相对路径补上baseUrl
if (/^(\/|\.\.?\/)/.test(config.url)) {
config.url = this.#config.baseUrl + config.url;
}
if (this.#interceptors.preRequest) {
config = this.#interceptors.preRequest(config);
}
//请求成功
config.success = (res) => {
this.#requestTaskremove(type, uuid);
// 是否有拦截器
if (this.#interceptors.preReponse) {
this.#interceptors.preReponse(res).then(resolve).catch(reject);
} else {
resolve(res);
}
};
//请求失败
config.fail = (err) => {
this.#requestTaskremove(type, uuid);
// 是否有拦截器
if (this.#interceptors.errorReponse) {
this.#interceptors.errorReponse(err).then(resolve).catch(reject);
} else {
reject(err);
}
};
let task = null;
if (isFile) {
const onProgressUpdate = config.onProgressUpdate;
delete config.onProgressUpdate;
//start------处理一下文件上传返回的结果是字符串,不是json对象
const success = config.success;
config.success = (res) => {
res.data = JSON.parse(res.data)
success(res);
}
//end-------处理一下文件上传返回的结果是字符串,不是json对象
task = wx.uploadFile(config);
//如果设置了监听
if (onProgressUpdate) {
task.onProgressUpdate(onProgressUpdate)
}
} else {
task = wx.request(config);
}
this.#requestTaskAdd(type, uuid, task);
})
}
}
2.构建拦截器
const request = new HttpUtil({
baseUrl: "xxxxx"
})
/**
* 请求发送前拦截器
*/
request.useRequestInterceptor(config => {
const token = getToken();//TODO 获取token
if (token) {
config.header = {
...config.header,
token: token
}
}
return config;
});
/**
* 得到响应结果前的拦截器
*/
request.useReponseInterceptor( res => {
const data = res.data;
//接口调用成功
if (res.statusCode === 200 && data.code === 200) {
return Promise.resolve(res.data.data);
}
const msg = data.msg || data.error || data.message || res.errMsg;
//TODO 授权失败
if (res.statusCode === 401 || data.code === 401) {
//没权限处理
}else {
//TODO 提示等处理
}
return Promise.reject(new Error(msg));
}, error => {
// 接口调用失败
if (error.errMsg.indexOf("request:fail abort") !== -1) {
return Promise.reject(new Error(error.errMsg));
}
wx.showToast({
title: error.errMsg,
icon: "error",
duration: 1500
});
return Promise.reject(new Error(error.errMsg));
})
export default request;
3.调用方式
// 登录
request.post('/v1/login', data).then(data=>{});
// 获取用户信息
request.get('/v1/userInfo', params).then(data=>{});
// 修改用户信息
request.put('/v1/userInfo', data).then(data=>{});
// 删除用户
request.delete('/v1/userInfo', data).then(data=>{});
// 复杂请求,请参考wx.request参数
request.request({
url:"xxxxx",
header:{
a:1
},
data:{
b:2
},
method:"POST"
}).then(data=>{});
//统一文件上传接口
request.uploadFile("/v1/file/upload", "file", filePath).then(data=>{});
//取消请求----具体细看HttpUtil.js类中的abort函数
request.abort()