阿lin最近公司需要开发小程序和h5,灵光一闪,要小程序以后还需要h5,这这这...
让我们隆重有请真神 uniapp!!!
为什么写这篇文章呢
既然写项目,肯定会封装接口,啊这,那你不用axios?
啊哈哈,那当然是我就想来点跟之前不一样的 (觉得个人用到的功能都差不多)
一.封装
肯定是先来个目录
index.js得有吧 request.js得有吧 你喜欢 哎呀随便命名拉
接下来我们一步一步来
基本的 uni.request 调用
遥想当年,大学时期的我就是这么封装的
export function apiRequest(options) {
return new Promise((resolve, reject) => {
const requestOptions = {
url: options.url,
method: options.method || "GET",
data: options.data || {},
header: options.header || {},
success: (res) => {
if (res.statusCode === 200) {
resolve(res.data);
} else {
reject(res);
}
},
fail: (err) => {
reject(err);
},
};
uni.request(requestOptions);
});
}
与文件上传合并统一
uniapp也有文件上传的接口 uni.uploadFile
这样我们就实现了文件上传也是一致的请求方式
export function apiRequest(options) {
return new Promise((resolve, reject) => {
... 同上
if (options.filePath) {
requestOptions.filePath = options.filePath;
requestOptions.name = options.name || "file";
requestOptions.formData = options.formData || {};
uni.uploadFile(requestOptions);
} else {
uni.request(requestOptions);
}
});
}
校验token
呃呃呃,token不要忘了呀,快把token校验加上
token这就很多东西要说了
- 获取access_token和refresh_token(登录接口返回,请求接口时候带上)
export function apiRequest(options) {
return new Promise((resolve, reject) => {
// 获取token
const access_token = uni.getStorageSync("access_token");
const refresh_token = uni.getStorageSync("refresh_token");
const headers = {
...options.header,
};
// 设置token
headers["Authorization"] = `Bearer ${access_token}`;
... 同上
}
- 进行token校验(token过期或者没有token)
export function apiRequest(options) {
return new Promise((resolve, reject) => {
// 获取token
const access_token = uni.getStorageSync("access_token");
const refresh_token = uni.getStorageSync("refresh_token");
const headers = {
...options.header,
};
// 设置token
headers["Authorization"] = `Bearer ${access_token}`;
... 同上
}
- token过期进行无感刷新
首先需要有refreshToken去获取accessToken接口
function refreshAccessToken() {
return new Promise((resolve, reject) => {
const refresh_token = uni.getStorageSync("refresh_token");
if (!refresh_token) {
reject("No refresh token available");
return;
}
uni.request({
url: `xxx`, // refreshToken获取accessToken的接口
method: "POST",
data: {
refresh_token: refresh_token,
},
success: (response) => {
if (response.statusCode === 200 && response.data.access_token) {
resolve(response.data); // 获取到token
} else {
reject("Failed to refresh token");
}
},
fail: (error) => {
reject(error);
},
});
});
}
- 继续封装
apiRequest
可以弄一个白名单列表,不需要token 比如我们的登录就是白名单
export function apiRequest(options) {
return new Promise((resolve, reject) => {
// whiteList 白名单列表
// whiteList.some 判断请求的接口是否在白名单内 一般请求的接口可能带有路径参数所以用上 includes()
if (!whiteList.some((i) => options.url.includes(i)) && (!access_token || !refresh_token)) {
reject("No tokens available");
return;
}
const requestOptions = {
... 同上
success: (res) => {
uni.hideLoading();
if (res.statusCode === 200) {
resolve(res.data);
} else if (res.statusCode === 401 && !isLoginRequest) {
refreshAccessToken()
.then((newToken) => {
uni.setStorageSync("access_token", newToken.access_token);
uni.setStorageSync("refresh_token", newToken.refresh_token);
})
.catch((err) => {
reject(err);
});
} else {
reject(res);
}
},
... 同上
};
... 同上
});
}
- 处理在token刷新过程中失败的请求
为了处理在token刷新过程中失败的请求,我们需要添加了一个失败的请求队列。当令牌刷新成功时,队列中的请求将被重新发送。如果令牌刷新失败,队列中的请求将被拒绝,并显示错误提示。
isRefreshing跟踪当前是否正在进行access_token的刷新过程
failedRequestsQueue用于暂存因access_token无效而失败的 API 请求。当access_token刷新成功后,这些暂存的请求将使用新的access_token重新发起。
let isRefreshing = false;
let failedRequestsQueue = [];
export function apiRequest(options) {
return new Promise((resolve, reject) => {
... 同上
const requestOptions = {
... 同上
success: (res) => {
if (res.statusCode === 200) {
resolve(res.data);
} else if (res.statusCode === 401 && !isLoginRequest) {
if (!isRefreshing) {
isRefreshing = true;
refreshAccessToken()
.then((newToken) => {
uni.setStorageSync("access_token", newToken.access_token);
uni.setStorageSync("refresh_token", newToken.refresh_token);
isRefreshing = false;
// 重新调用所有失败的请求
failedRequestsQueue.forEach((req) => req.onSuccess(newAccessToken.access_token));
failedRequestsQueue = [];
})
.catch((err) => {
isRefreshing = false;
failedRequestsQueue.forEach((req) => req.onFailure(err));
failedRequestsQueue = [];
reject(err);
});
}
// 暂存请求
const retryRequest = new Promise((resolve, reject) => {
failedRequestsQueue.push({
onSuccess: (newToken) => {
headers["Authorization"] = `Bearer ${newToken}`;
apiRequest(options).then(resolve).catch(reject);
},
onFailure: (err) => {
reject(err);
},
});
});
resolve(retryRequest);
} else {
reject(res);
}
},
fail: (err) => {
reject(err);
},
};
... 同上
});
}
至此,我们的request.js就封装完毕了
二.使用
引入和导出一些白名单之类的东东
export const baseUrl = "https://baseUrl.com/";
export const whiteList = [`${baseUrl}api/xxx/xxx`, `${baseUrl}api/yyy/yyy`];
import { apiRequest } from "./request";
正常请求
const getxyz = (x = "", y = "", x = 1000) =>
apiRequest({
url: `${baseUrl}api/getxyz/getxyz?x=${x}&y=${y}&z=${z}`,
});
const postxyz = (x, y, z) =>
apiRequest({
url: `${baseUrl}api/postxyz/postxyz`,
method: "POST",
data: {
x,
y,
z
},
});
上传接口
const uploadImage = (zzz, filePath) =>
apiRequest({
url: `${baseUrl}api/Upload/xxx/yyy/${zzz}`,
filePath: filePath,
});
总结
至此,封装及使用都完成了,主要封装了双token的无感刷新,及与上传文件接口的统一,后续一些其他的操作可以再自行加入... 最后,阿lin祝大家都能找到好的工作(涨薪)啦😊