uniapp-vue3,封装类似于axios的请求方法

request.js

import { rootUrl } from "@/config/app-config.js"
import handleCache from '@/utils/cache/cache.js';
import { showToast } from "@/utils/vant"

import { clearAccountInfo } from '@/utils/clear/clear';
import { filterUnBlackRes } from '@/hooks/black-config/black-config-api';

import { store } from '@/store'; // store实例
import { commonBus } from '@/utils/bus/bus';

let isTokenRefreshing = false; // token是否正在刷新
let requestList = []; //正在等待请求的接口队列

const service = (options) => {
  return new Promise((resolve, reject) => {
    handleRequest(options, resolve, reject)
  })
}

function handleRequest(options, resolve, reject) {
  /* 基本请求配置 */
  const requestConfig = {
    url: `${rootUrl}${options.url}`,
    timeout: 60000, // 请求超时时间 毫秒
    withCredentials: true, // 异步请求时是否携带cookie
    header: {
      // 设置后端需要的传参类型
      'Content-Type': 'application/json',
      Client: 'front', // 所属客户端
      // "Authorization": "",
      // "X-Requested-With": "XMLHttpRequest",
    },
    method: options.method.toUpperCase(),
  }



  /* 根据传参自定义部分请求配置*/
  if (options.data || options.params) { //请求的参数
    requestConfig.data = options.data || options.params
  }
  if (options.params && requestConfig.method == 'POST') { //处理post请求且需要接口拼参的情况
    requestConfig.header['Content-Type'] = 'application/x-www-form-urlencoded'
  }
  if (options.headers) {
    Object.assign(requestConfig.header, options.headers)
  }
  if (options.responseType) {
    requestConfig.responseType = options.responseType
  }
  if (options.timeout) {
    requestConfig.timeout = options.timeout
  }
  if (options.apiConfig) {
    requestConfig.apiConfig = options.apiConfig
  }
  // console.log(requestConfig)



  /* 处理token相关 */
  let tokenInfo = '';
  if (!options.url.includes('userLogin/refreshtoken')) { //判断是否为刷新token请求--否
    tokenInfo = handleCache.get('accessToken') || '';
  } else { //判断是否为刷新token请求--是
    tokenInfo = handleCache.get('refreshToken') || '';
  }
  if (tokenInfo) { //配置请求token
    requestConfig.header['Authorization'] = tokenInfo;
  }



  /* 接口请求成功的处理函数 */
  requestConfig.success = (response) => {
    // console.log(response)
    //和uni.request不同,uni.uploadFile返回结果是字符串对象,需要转一下格式
    if (requestConfig.uploadType == 'formdataUpload' && response.data) {
      response.data = JSON.parse(response.data)
    }
    const res = response.data;

    if (res.code == 100009 || res.code == 100005) {
      // 对通用token令牌过期或验证失败的处理
      if (!isTokenRefreshing) {
        //判断当前不处于刷新阶段--则进行刷新操作
        if (!store.state.accountInfo.refreshToken) {
          //判断刷新token也不存在,则直接判定登录失效
          clearAccountInfo();
          if (requestConfig?.apiConfig?.unMsg !== true) {
            showToast('您尚未登录或登录已过期,请登录');
          }
          return resolve({
            code: 900009,
            msg: '您尚未登录或登录已过期,请登录',
          });
        }

        isTokenRefreshing = true; // 上锁

        const refreshOptions = {
          url: `/iam/userLogin/refreshtoken`,
          method: 'get',
        };
        return service(refreshOptions)
          .then((_res) => {
            if (_res.code == 200000) {
              //刷新成功--将新的通用token和刷新token都进行更新;并执行requestList等待队列里的接口请求
              // 保存通用token
              let accessTokenExpireTime = _res.data.expire_in / 1000;
              handleCache.set('accessToken', _res.data.access_token, accessTokenExpireTime);
              // 保存刷新token
              let refreshTokenExpireTime = _res.data.refresh_expire_in / 1000;
              handleCache.set('refreshToken', JSON.stringify(_res.data.refresh_token), refreshTokenExpireTime);

              //执行后续推入到requestList队列中的请求,(requestList中存的不是请求参数,而是请求的Promise函数,这里直接拿来执行就好)
              requestList.forEach((run) => run());
              //将请求队列置空
              requestList = [];

              //重新执行一下本次未执行成功的请求并返回
              return service(options);
            } else {
              let needReload = false;
              if (_res.code == 100010) {
                //刷新token也过期了--移除用户登录相关信息
                needReload = true; //需要执行页面刷新
                clearAccountInfo();
                // return Promise.reject(new Error(_res.msg || "Error"));
                _res.msg = '您尚未登录或登录已过期,请登录';
              }
              if (_res?.msg && requestConfig?.apiConfig?.unMsg !== true) {
                showToast(_res.msg);
              }
              if (needReload) {
                //需要进行页面刷新
                let timer = setTimeout(() => {
                  commonBus.emit('loginExpire'); //利用事件总线在app.vue里监听此事件,并做对应处理
                  clearTimeout(timer);
                }, 150);
              }
              return resolve(_res);
            }
          })
          .catch(() => {
            showToast('服务器开小差了呢,请稍后再试~');
          })
          .finally(() => {
            isTokenRefreshing = false; //解锁
          });
      } else {
        //判断当前处于token刷新阶段--将后续接口推入等待请求队列,等待刷新token接口请求完毕后执行后续请求队列
        return new Promise((resolve) => {
          //这里加入的是一个promise的解析函数,将响应的config配置对应解析的请求函数存到requestList中,等到刷新token回调后再执行
          requestList.push(() => {
            resolve(service(options));
          });
        });
      }
    } else {
      // 其他情况则返回结果,对应状态code需在具体请求函数里判断
      if (res.code !== 200000 && res.code !== 100010 && res?.msg && requestConfig?.apiConfig?.unMsg !== true) {
        showToast(res.msg);
      }
      filterUnBlackRes(response, options.url); //剔除用户拉黑或被拉黑列表中的数据
      return resolve(res);
    }
  }



  /* 接口请求失败的处理函数*/
  requestConfig.fail = (error) => {
    if (handleCache.get('loadWxPayOpenId') != 'yes') {
      // showToast(error.message || '服务器开小差了呢,请稍后再试~');
      showToast('服务器开小差了呢,请稍后再试~');
    }
    return Promise.reject(error);
  }

  if (requestConfig.header['Content-Type'] == 'multipart/form-data') { //formdata为浏览器对象,app不支持;这里做处理
    requestConfig.header['Content-Type'] = 'application/x-www-form-urlencoded'
    requestConfig.uploadType = 'formdataUpload' //用于区分fomrdata文件上传
    requestConfig.filePath = requestConfig.data.path
    requestConfig.name = 'file'
    // requestConfig.formData = requestConfig.data
    delete requestConfig.data
    uni.uploadFile(requestConfig)
  } else {
    uni.request(requestConfig)
  }
}

export default service;

demo.js

// demo页
import request from '@/services/config/request';

/**
 * 这是一个get请求,使用params传参
 * @param {*} params 所有参数
 */
async function getVideoInfo(params) {
  return request({
    url: `/video/getvideo`,
    method: 'get',
    params: params,
  });
}

/**
 * 这是一个get请求,直接在接口里带参
 */
async function getVideoInfoById(id) {
  return request({
    url: `/video/getvideo/${id}`,
    method: 'get',
  });
}

/**
 * 这是一个post请求,使用data传参
 * @param {*} params 所有参数
 * @param {String} type 类型
 * @param {Number} id 序号
 */
async function addVideoInfo(params) {
  return request({
    url: `/video/addvideo`,
    method: 'post',
    data: params,
  });
}

export default {
  getVideoInfo,
  getVideoInfoById,
  addVideoInfo,
};

调用

import {getVideoInfo} from "./demo.js"

const params = {
  id: xxx,
  status: xxx
}
getVideoInfo(params).then(()=>{

}).catch(()=>{

}).finally(()=>{

})
posted @ 2024-01-25 16:29  huihuihero  阅读(753)  评论(0编辑  收藏  举报