开发 UniApp 项目时,每个页面都手写 uni.request 既重复又容易漏带 Token。把请求收敛成一个 Promise 风格的封装,页面里只关心数据和错误,一套代码同时适配 H5 与微信小程序。

实现思路

新建 request.js 导出 request 主函数:调用前从本地存储读取 Token 并写入请求头,成功回调里先判断 HTTP 状态码,401 时清登录态并跳登录页,再按业务 code 统一处理;页面通过 http.get / http.post 等快捷方法调用。

封装文件

// utils/request.js
export function request(options) {
    return new Promise((resolve, reject) => {
        const token = uni.getStorageSync("token");
        const header = { ...(options.header || {}) };
        if (token) {
            header.Authorization = "Bearer " + token;
        }
        uni.request({
            url: "/api" + options.url,
            method: options.method || "GET",
            data: options.data || {},
            header,
            success: (res) => {
                if (res.statusCode === 401) {
                    uni.removeStorageSync("token");
                    uni.navigateTo({ url: "/pages/login/index" });
                    reject(res.data);
                    return;
                }
                if (res.data.code !== 0) {
                    uni.showToast({ title: res.data.message || "请求失败", icon: "none" });
                    reject(res.data);
                    return;
                }
                resolve(res.data.data);
            },
            fail: (err) => {
                uni.showToast({ title: "网络异常,请稍后重试", icon: "none" });
                reject(err);
            }
        });
    });
}

export const http = {
    get(url, params) { return request({ url, method: "GET", data: params }); },
    post(url, data) { return request({ url, method: "POST", data }); },
    put(url, data) { return request({ url, method: "PUT", data }); },
    del(url, data) { return request({ url, method: "DELETE", data }); }
};

页面调用

接口前缀与超时可在 request 里统一配置,页面里直接用快捷方法并搭配 async/await,代码非常清爽。

// pages/order/list.vue
import { http } from "/utils/request";

const list = ref([]);

async function loadList() {
    uni.showLoading({ title: "加载中" });
    try {
        const data = await http.get("/order/list", { page: 1, size: 20 });
        list.value = data.records;
    } catch (e) {
        console.log("加载失败", e);
    } finally {
        uni.hideLoading();
    }
}

登录后写入 Token

登录接口成功后将 Token 存入本地,下次请求自动带上;Token 过期时后端返回 401,前端统一清空并跳转登录页,实现全局拦截。

// pages/login/index.vue
import { http } from "/utils/request";

async function doLogin() {
    const data = await http.post("/auth/login", {
        username: form.username,
        password: form.password
    });
    uni.setStorageSync("token", data.token);
    uni.switchTab({ url: "/pages/index/index" });
}

需要注意

  • 上传文件的 uni.uploadFile 默认不带自定义 Header,需要手动把 Token 放进 header 参数;
  • 请求拦截统一弹错后,页面里不要重复 showToast,避免提示叠加;
  • 401 跳转前建议用 uni.reLaunch 而不是 navigateTo,防止返回按钮回到失效页面;
  • H5 端跨域需在 vite.config 或后端配置 CORS,小程序端则要在后台配置合法域名。

小结

请求封装是 UniApp 工程的地基之一,统一入口后 Token、错误、Loading 都有章可循,后续增加签名或埋点也只需改一处。