开箱即用的uni.request 2.0

封装 uni.request 的通用请求工具

在开发微信小程序时,网络请求是不可避免的一部分。为了简化代码、提高可维护性,我们可以封装一个通用的请求工具。本文将详细介绍如何封装一个支持自动重试、处理 token 过期以及错误处理的请求工具。

需求背景

封装一个通用的请求工具可以大大简化网络请求的代码,提高代码的可维护性和复用性。通过统一的错误处理和token管理机制,可以避免重复代码,减少错误,提高开发效率。此外,通过处理

token过期和自动重试机制,可以提高应用的可靠性和用户体验。通过将每个请求封装在独立的文件中,可以使代码更加模块化,方便管理和维护。

目录结构

我们将创建以下目录和文件:

  • api/
    • index.js:导出所有API请求函数。
    • user.js:封装用户相关的请求。
    • auth.js:处理 token 获取和刷新逻辑。
    • errorHandler.js:处理不同的错误逻辑。
    • request.js:封装网络请求的核心逻辑。

request.js

这是我们的主要请求工具文件,负责处理网络请求,自动重试,处理 token 过期等。

// api/request.js

import { handleError } from './errorHandler.js';
import { getTokens, refreshToken, login } from './auth.js';

const baseURL = 'https://your-api-base-url.com'; // 替换为你的API基础URL

/**
 * 通用请求函数
 * @param {Object} options 请求选项
 * @param {number} retryCount 最大重试次数,默认2次
 * @returns {Promise} 返回一个Promise对象
 */
const request = (options, retryCount = 2) => {
  return new Promise((resolve, reject) => {
    const { url, method = 'GET', data = {}, header = { 'Content-Type': 'application/json' }, timeout = 10000, retry = true } = options;
    const tokens = getTokens();

    /**
     * 发起请求的内部函数
     * @param {number} retryAttempt 当前重试次数
     */
    const makeRequest = (retryAttempt) => {
      uni.request({
        url: `${baseURL}${url}`, // 完整的请求URL
        method, // 请求方法,默认为GET
        data, // 请求数据,默认为空对象
        header: {
          ...header, // 合并传入的header
          Authorization: `Bearer ${tokens.accessToken || ''}`, // 添加授权头
        },
        timeout, // 请求超时时间,默认为10秒
        success: (res) => {
          if (res.statusCode === 200) {
            resolve(res.data); // 请求成功,解析Promise
          } else if (res.statusCode === 401) {
            // 处理未授权错误
            handleUnauthorized(retryAttempt, resolve, reject, options);
          } else {
            handleError(res); // 调用错误处理函数
            reject(res); // 请求失败,拒绝Promise
          }
        },
        fail: (err) => {
          handleError(err); // 调用错误处理函数
          if (retry && retryAttempt < retryCount) {
            // 如果需要重试且未达到最大重试次数,重新发起请求
            makeRequest(retryAttempt + 1);
          } else {
            reject(err); // 请求失败,拒绝Promise
          }
        },
      });
    };

    // 初次调用请求函数
    makeRequest(0);
  });
};

/**
 * 处理未授权错误
 * @param {number} retryAttempt 当前重试次数
 * @param {function} resolve Promise的resolve函数
 * @param {function} reject Promise的reject函数
 * @param {Object} options 请求选项
 */
const handleUnauthorized = (retryAttempt, resolve, reject, options) => {
  // 调用刷新token函数
  refreshToken().then(() => {
    // 刷新token成功,重新发起请求
    request(options).then(resolve).catch(reject);
  }).catch((err) => {
    // 刷新token失败,进行登录逻辑
    console.error('刷新token失败:', err);
    login(); // 调用登录函数
    reject(new Error('未授权,需要登录')); // 拒绝Promise
  });
};

export default request;

auth.js

这个文件处理 token 的获取、存储和刷新逻辑。

// api/auth.js

/**
 * 获取本地存储的token
 * @returns {Object} 返回包含accessToken和refreshToken的对象
 */
export const getTokens = () => {
  return {
    accessToken: uni.getStorageSync('accessToken'),
    refreshToken: uni.getStorageSync('refreshToken')
  };
};

/**
 * 设置本地存储的token
 * @param {string} accessToken 新的accessToken
 * @param {string} refreshToken 新的refreshToken
 */
export const setTokens = (accessToken, refreshToken) => {
  uni.setStorageSync('accessToken', accessToken);
  uni.setStorageSync('refreshToken', refreshToken);
};

/**
 * 刷新token
 * @returns {Promise} 返回一个Promise对象,刷新成功则解析,否则拒绝
 */
export const refreshToken = () => {
  return new Promise((resolve, reject) => {
    const { refreshToken } = getTokens();
    uni.request({
      url: 'https://your-api-base-url.com/refresh-token', // 替换为刷新token的URL
      method: 'POST',
      data: { token: refreshToken },
      success: (res) => {
        if (res.statusCode === 200) {
          const { accessToken, refreshToken } = res.data;
          setTokens(accessToken, refreshToken);
          resolve();
        } else {
          reject(new Error('刷新token失败'));
        }
      },
      fail: (err) => {
        reject(err);
      }
    });
  });
};

/**
 * 登录逻辑
 */
export const login = () => {
  // 你的登录逻辑,这里是一个示例
  uni.login({
    provider: 'weixin',
    success: (loginRes) => {
      // 假设调用登录接口获取token
      uni.request({
        url: 'https://your-api-base-url.com/login', // 替换为登录的URL
        method: 'POST',
        data: { code: loginRes.code },
        success: (res) => {
          if (res.statusCode === 200) {
            const { accessToken, refreshToken } = res.data;
            setTokens(accessToken, refreshToken);
            uni.showToast({ title: '登录成功', icon: 'none' });
          } else {
            uni.showToast({ title: '登录失败', icon: 'none' });
          }
        },
        fail: () => {
          uni.showToast({ title: '登录失败', icon: 'none' });
        }
      });
    }
  });
};

errorHandler.js

这个文件定义了不同的错误处理逻辑。

// api/errorHandler.js

/**
 * 处理错误
 * @param {Object} error 错误对象
 */
export const handleError = (error) => {
  if (error.statusCode) {
    switch (error.statusCode) {
      case 401:
        // 未授权错误在request.js中已经处理,不需要在这里处理
        break;
      case 404:
        handleNotFoundError();
        break;
      default:
        handleDefaultError();
        break;
    }
  } else {
    handleNetworkError();
  }
};

/**
 * 处理未找到错误
 */
const handleNotFoundError = () => {
  uni.showToast({ title: '请求地址不存在', icon: 'none' });
};

/**
 * 处理默认错误
 */
const handleDefaultError = () => {
  uni.showToast({ title: '请求失败,请稍后再试', icon: 'none' });
};

/**
 * 处理网络错误
 */
const handleNetworkError = () => {
  uni.showToast({ title: '网络错误,请检查您的网络连接', icon: 'none' });
};

user.js

这个文件封装用户相关的请求,例如登录、获取用户信息等。

// api/user.js

import request from './request';

/**
 * 用户登录
 * @param {Object} data 登录数据
 * @returns {Promise} 返回一个Promise对象
 */
export const login = (data) => {
  return request({
    url: '/login',
    method: 'POST',
    data,
  });
};

/**
 * 获取用户信息
 * @returns {Promise} 返回一个Promise对象
 */
export const getUserInfo = () => {
  return request({
    url: '/user/info',
    method: 'GET',
  });
};

index.js

这个文件导出所有API请求函数。

// api/index.js

import * as user from './user';

export default {
  user,
};

使用示例

在页面或组件中使用这个工具时,只需要调用相应的函数即可:

import api from './api';

export default {
  data() {
    return {
      responseData: null,
    };
  },
  methods: {
    fetchData() {
      api.user.getUserInfo().then((data) => {
        this.responseData = data;
      }).catch((error) => {
        console.error('请求失败:', error);
      });
    }
  },
  mounted() {
    this.fetchData();
  }
};

详细解释

  1. 目录结构:将所有API相关的逻辑放在api文件夹中,每个请求封装在独立的文件中,方便管理和维护。

  2. request.js:封装通用的请求逻辑,包括自动重试和处理 token 过期。

  3. auth.js:处理 token 的获取、存储和刷新逻辑,提供 getTokenssetTokensrefreshTokenlogin 函数。

  4. errorHandler.js:定义错误处理逻辑,根据不同的错误类型执行不同的处理。

  5. user.js:封装用户相关的请求,例如登录和获取用户信息。

  6. index.js:导出所有API请求函数,方便在项目中统一调用。

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值