uniapp uni.request 封装请求分享

1,150 阅读3分钟

阿lin最近公司需要开发小程序和h5,灵光一闪,要小程序以后还需要h5,这这这...
让我们隆重有请真神 uniapp!!!

为什么写这篇文章呢

既然写项目,肯定会封装接口,啊这,那你不用axios?
啊哈哈,那当然是我就想来点跟之前不一样的 (觉得个人用到的功能都差不多)

一.封装

肯定是先来个目录
index.js得有吧 request.js得有吧 你喜欢 哎呀随便命名拉 image.png
接下来我们一步一步来

基本的 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这就很多东西要说了

  1. 获取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}`;
        ... 同上
}
  1. 进行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}`;
        ... 同上
}
  1. 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);
            },
        });
    });
}
  1. 继续封装 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);
                }
            },
            ... 同上
        };

        ... 同上
    });
}
  1. 处理在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祝大家都能找到好的工作(涨薪)啦😊