跳到主要内容

请求工具方法

import qs from "qs";
import cache from "./cache";
import { refreshTokenService, loginUserService } from "@/service/index";
import { CLIENT_SECRET, CLIENT_ID } from "@/constants/app";
import { to } from "@/utils/util";

// 默认刷新状态,确保一旦遇到token过期的状况就能更新
let isTokenRefreshing = false;
// 防止多次请求token获取接口(限制三次,三次以后直接显示账号信息错误)
let refreshTokenTimes = 0;
// 防止access_token错误情况下而refresh_token的无限请求边际情况
let wrongAuth = 0;
// 是否存在token
let firstTokenGetting = false;
// 防止获取token次数过多
let tokenFirstGetTimes = 0;
// 被拦截的请求数组
let subscribers: any[] = [];

// 处理被缓存的请求
function onAccessTokenFetched() {
subscribers.forEach((callback) => {
callback();
});
// 处理完后清空缓存请求数组
subscribers = [];
}

const doLogin = async () => {
const [res_code, err_code] = await to(wx.login());
if (err_code) {
console.log("wxlogin出错:", err_code);
}
if (res_code) {
const [res, err] = await loginUserService({
code: res_code.code,
client_id: CLIENT_ID,
});
if (res) {
cache.set(
"access_token",
res.data.data.access_token,
res.data.data.expires_in
);
cache.set("refresh_token", res.data.data.refresh_token);
return res;
}
if (err) {
wx.showToast({
title: "用户认证失败",
icon: "error",
});
}
}
};

// 将请求缓存到请求数组中
const addSubscriber = (callback: any) => {
subscribers.push(callback);
};

const refreshToken = async () => {
if (refreshTokenTimes >= 3) {
console.error("登陆信息过期");
return;
}
console.log("token开始更新");
isTokenRefreshing = true;
refreshTokenTimes++;
const [res] = await refreshTokenService({
access_token: cache.get("refresh_token"),
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
});
isTokenRefreshing = false;
if (res && res.statusCode === 200) {
cache.set(
"access_token",
res.data.data.access_token,
res.data.data.expires_in
);
cache.set("refresh_token", res.data.data.refresh_token);
onAccessTokenFetched();
} else {
refreshToken();
}
};

const getToken = async () => {
if (tokenFirstGetTimes >= 3) {
console.error("登录失败");
return;
}
firstTokenGetting = true;
tokenFirstGetTimes += 1;
console.log("token开始首次获取");
const res = await doLogin();
if (res) {
onAccessTokenFetched();
} else {
getToken();
}
};

const checkHasToken = (url: string, method: any, data: any) => {
// 本地没有token时获取token的函数,这需要添加一个开关,防止重复请求
if (!firstTokenGetting) {
getToken();
}
// 将当前的请求保存在观察者数组中
const retryOriginalRequest = new Promise((resolve) => {
addSubscriber(() => {
resolve(requestBase(url, method, data));
});
});
return retryOriginalRequest;
};

const checkTokenRefreshStatus = (url: string, method: any, data: any) => {
// 刷新token的函数,这需要添加一个开关,防止重复请求
if (isTokenRefreshing) {
refreshToken();
}
isTokenRefreshing = false;
// 将当前的请求保存在观察者数组中
const retryOriginalRequest = new Promise((resolve) => {
addSubscriber(() => {
resolve(requestBase(url, method, data));
});
});
return retryOriginalRequest;
};

const requestBase = (url: string, method: any, data: any) => {
if (wrongAuth >= 5) {
console.error("多个请求授权失败,重新登陆下?");
return;
}
return new Promise((resolve, reject) => {
wx.request({
url: data ? url : url + "?" + qs.stringify(data),
header: {
"content-type": "application/json;charset=UTF-8;",
authorization: `Bearer ${cache.get("access_token")}`,
},
method: method,
data: data,
success(res) {
if (res && res.statusCode !== 200) {
if (res.statusCode === 401) {
wrongAuth++;
try {
if (!cache.directGet("access_token")) {
resolve(checkHasToken(url, method, data));
}
// 注意此处如果过期了那么会直接被后端拦截,不会有code等信息,所以接口类不适用
else if (
res.data.detail === "身份认证信息未提供。" ||
res.data.detail ===
"Authentication credentials were not provided."
) {
resolve(checkTokenRefreshStatus(url, method, data));
}
} catch (e) {
throw e;
}
}
} else {
if (res && res.data.code === 20000) {
resolve(res);
console.log("全局打印请求成功:", res);
} else {
reject(res);
console.log("状态码非20000:", res);
}
}
},
fail(err) {
reject(err);
console.error("全局打印请求失败:", err);
},
});
});
};

export const post = (url: string, data: any) => {
return requestBase(url, "POST", data);
};

export const get = (url: string, data: any) => {
return requestBase(url, "GET", data);
};