uni-app 封装网络请求

708 阅读2分钟

uni-app 封装网络请求

[记录一下在项目开发过程中使用uni-request的封装]

[本文分以下几部分来讲解封装的思路]

  • promise
  • 请求前处理(拦截器)
  • 错误处理
  • 取消请求
  • 重复请求处理
  • ts

一、promise基础封装

import baseUrl from './baseUrl' // 这里引入基础请求路径;

export function request(options) {
  return new Promise((resolve, reject) => {
    uni.request({
      ...options,
      fail(err) {
        // 错误捕捉
      },

      success(res) {
        // 成功处理
      },
    });
  });
}

二、请求前处理

类似于axios的请求拦截器,uni-app内置了这个功能。 详见 添加拦截器

import baseUrl from "./baseUrl"; // 这里引入基础请求路径;
import { isBlank } from '@/utils/is';

// 添加拦截器
const httpInterceptor = {
  // 拦截前触发
  invoke(options) {
    // 1. 非 http 开头需拼接地址
    if (!options.url.startsWith('http')) {
      options.url = baseUrl + options.url;
    }
    // 2. 请求超时, 默认 60s
    options.timeout = 60 * 1000;

    // 3. 初始化header
    if (isBlank(options.header)) {
      options.header = {};
    }

    // 4. 添加 token 请求头标识
    const token = store.getters['user/access_token']; // 获取本地存储的token
    if (token) {
      options.header.Authorization = `Bearer ${token}`;
    }
  },
};
uni.addInterceptor('request', httpInterceptor);

export function request(options) {
  return new Promise((resolve, reject) => {
    uni.request({
      ...options,
      fail(err) {
        // 错误捕捉
      },

      success(res) {
        // 成功处理
      },
    });
  });
}

三、错误处理

import baseUrl from './baseUrl'; // 这里引入基础请求路径;
import { isBlank } from '@/utils/is';

function showToast(msg, icon = 'error') {
  uni.showToast({
    title: msg,
    icon,
  });
}

// 添加拦截器
const httpInterceptor = {
  // 拦截前触发
  invoke(options) {
    // 1. 非 http 开头需拼接地址
    if (!options.url.startsWith('http')) {
      options.url = baseUrl + options.url;
    }
    // 2. 请求超时, 默认 60s
    options.timeout = 60 * 1000;

    // 3. 初始化header
    if (isBlank(options.header)) {
      options.header = {};
    }

    // 4. 添加 token 请求头标识
    const token = store.getters['user/access_token']; // 获取本地存储的token
    if (token) {
      options.header.Authorization = `Bearer ${token}`;
    }
  },
};
uni.addInterceptor('request', httpInterceptor);

export function request(options) {
  return new Promise((resolve, reject) => {
    uni.request({
      ...options,
      fail(err) {
        // 错误捕捉
        showToast('网络错误,换个网络试试');
        reject(err);
      },

      success(res) {
        // 成功处理
        const rawData = res.data;
        if (res.statusCode >= 200 && res.statusCode < 300) {
          // 判断跟后端约定好的成功标识
          if (rawData.code !== 0) {
            showToast(rawData.message, 'none');
          }
          resolve(rawData);
        } else if (res.statusCode === 401) {
          // token 失效处理
          reject(res);
        } else {
          switch (res.statusCode) {
            case 404:
              showToast('请求资源不存在');
              break;
            case 500:
              showToast('服务器错误');
              break;
            case 502:
              showToast('找不到对应服务器');
              break;
            default:
              showToast(rawData?.message || '请求异常');
              break;
          }
          reject(res);
        }
      },
    });
  });
}

四、取消请求

某些场景下可能需要手动的触发去取消请求,可以参考浏览器apiAbortController以及uni-app自带的请求取消去实现

import baseUrl from './baseUrl'; // 这里引入基础请求路径;
import { isBlank } from '@/utils/is';

function showToast(msg, icon = 'error') {
  uni.showToast({
    title: msg,
    icon,
  });
}

// 定义一个 AbortController 类
class AbortController {
  requestTask = null;

  abort() {
    if (this.requestTask) {
      this.requestTask.abort();
    }
  }
}

// 添加拦截器
const httpInterceptor = {
  // 拦截前触发
  invoke(options) {
    // 1. 非 http 开头需拼接地址
    if (!options.url.startsWith('http')) {
      options.url = baseUrl + options.url;
    }
    // 2. 请求超时, 默认 60s
    options.timeout = 60 * 1000;

    // 3. 初始化header
    if (isBlank(options.header)) {
      options.header = {};
    }

    // 4. 添加 token 请求头标识
    const token = store.getters['user/access_token']; // 获取本地存储的token
    if (token) {
      options.header.Authorization = `Bearer ${token}`;
    }
  },
};
uni.addInterceptor('request', httpInterceptor);

export function request(options) {
  // 每次请求的时候创建一个 AbortController 实例
  const controller = new AbortController();
  
  const promise = new Promise((resolve, reject) => {
    // 把request的返回值绑定在实例的requestTask上
    controller.requestTask = uni.request({
      ...options,
      fail(err) {
        // 错误捕捉
        showToast('网络错误,换个网络试试');
        reject(err);
      },

      success(res) {
        // 成功处理
        const rawData = res.data;
        if (res.statusCode >= 200 && res.statusCode < 300) {
          // 判断跟后端约定好的成功标识
          if (rawData.code !== 0) {
            showToast(rawData.message, 'none');
          }
          resolve(rawData);
        } else if (res.statusCode === 401) {
          // token 失效处理
          reject(res);
        } else {
          switch (res.statusCode) {
            case 404:
              showToast('请求资源不存在');
              break;
            case 500:
              showToast('服务器错误');
              break;
            case 502:
              showToast('找不到对应服务器');
              break;
            default:
              showToast(rawData?.message || '请求异常');
              break;
          }
          reject(res);
        }
      },
    });
  });

  // 需要将 controller 一并return出去才能在外部使用
  return { promise, controller };
}

这里其实不太方便,在使用的时候需要.promise才能触发实际请求,但是目前来说暂时想不到好的优化方法,有想法的朋友可以在评论区讨论哦

import { request } from './request';

export function LoginApi(data = {}) {
  return request({ url: 'users/Login', method: 'POST', data }).promise;
}

五、重复请求

可以通过缓存的方式去处理重复请求

import baseUrl from './baseUrl'; // 这里引入基础请求路径;
import { isBlank } from '@/utils/is';

function showToast(msg, icon = 'error') {
  uni.showToast({
    title: msg,
    icon,
  });
}

// 定义一个 AbortController 类
class AbortController {
  requestTask = null;

  abort() {
    if (this.requestTask) {
      this.requestTask.abort();
    }
  }
}

// 添加一个 Map 存储缓存的请求
const requestCache = new Map();

// 添加拦截器
const httpInterceptor = {
  // 拦截前触发
  invoke(options) {
    // 1. 非 http 开头需拼接地址
    if (!options.url.startsWith('http')) {
      options.url = baseUrl + options.url;
    }
    // 2. 请求超时, 默认 60s
    options.timeout = 60 * 1000;

    // 3. 初始化header
    if (isBlank(options.header)) {
      options.header = {};
    }

    // 4. 添加 token 请求头标识
    const token = store.getters['user/access_token']; // 获取本地存储的token
    if (token) {
      options.header.Authorization = `Bearer ${token}`;
    }
  },
};
uni.addInterceptor('request', httpInterceptor);

export function request(options) {
  // 每次请求的时候创建一个 AbortController 实例
  const controller = new AbortController();

  // 如果缓存存在我们就直接return出去
  const cacheKey = JSON.stringify(options);
  if (requestCache.has(cacheKey)) {
    return requestCache.get(cacheKey);
  }

  const promise = new Promise((resolve, reject) => {
    // 把request的返回值绑定在实例的requestTask上
    controller.requestTask = uni.request({
      ...options,
      fail(err) {
        // 错误捕捉
        showToast('网络错误,换个网络试试');
        reject(err);
      },

      success(res) {
        // 成功处理
        const rawData = res.data;
        if (res.statusCode >= 200 && res.statusCode < 300) {
          // 判断跟后端约定好的成功标识
          if (rawData.code !== 0) {
            showToast(rawData.message, 'none');
          }
          resolve(rawData);
        } else if (res.statusCode === 401) {
          // token 失效处理
          reject(res);
        } else {
          switch (res.statusCode) {
            case 404:
              showToast('请求资源不存在');
              break;
            case 500:
              showToast('服务器错误');
              break;
            case 502:
              showToast('找不到对应服务器');
              break;
            default:
              showToast(rawData?.message || '请求异常');
              break;
          }
          reject(res);
        }
      },
    });
  });

  // 存入缓存
  requestCache.set(cacheKey, { promise, controller });

  promise.finally(() => {
    // 请求结束删除缓存
    requestCache.delete(cacheKey);
  });

  // 需要将 controller 一并return出去才能在外部使用
  return { promise, controller };
}

六、加入ts

import baseUrl from './baseUrl'; // 这里引入基础请求路径;
import { isBlank } from '@/utils/is';

function showToast(msg, icon: MyGlobalTypes.ErrorIcon = 'error') {
  uni.showToast({
    title: msg,
    icon,
  });
}

// 定义一个 AbortController 类
class AbortController {
  requestTask: UniApp.RequestTask | null = null;

  abort() {
    if (this.requestTask) {
      this.requestTask.abort();
    }
  }
}

// 添加一个 Map 存储缓存的请求
const requestCache = new Map();

// 添加拦截器
const httpInterceptor: UniNamespace.InterceptorOptions = {
  // 拦截前触发
  invoke(options: UniApp.RequestOptions) {
    // 1. 非 http 开头需拼接地址
    if (!options.url.startsWith('http')) {
      options.url = baseUrl + options.url;
    }
    // 2. 请求超时, 默认 60s
    options.timeout = 60 * 1000;

    // 3. 初始化header
    if (isBlank(options.header)) {
      options.header = {};
    }

    // 4. 添加 token 请求头标识
    const token = store.getters['user/access_token']; // 获取本地存储的token
    if (token) {
      options.header.Authorization = `Bearer ${token}`;
    }
  },
};
uni.addInterceptor('request', httpInterceptor);

export function request<T extends MyGlobalTypes.IRequestResult>(
  options: UniApp.RequestOptions
): { promise: Promise<T>; controller: AbortController } {
  // 每次请求的时候创建一个 AbortController 实例
  const controller = new AbortController();

  // 如果缓存存在我们就直接return出去
  const cacheKey = JSON.stringify(options);
  if (requestCache.has(cacheKey)) {
    return requestCache.get(cacheKey);
  }

  const promise = new Promise<T>((resolve, reject) => {
    // 把request的返回值绑定在实例的requestTask上
    controller.requestTask = uni.request({
      ...options,
      fail(err) {
        // 错误捕捉
        showToast('网络错误,换个网络试试');
        reject(err);
      },

      success(res) {
        // 成功处理
        const rawData = res.data;
        if (res.statusCode >= 200 && res.statusCode < 300) {
          // 判断跟后端约定好的成功标识
          if (rawData.code !== 0) {
            showToast(rawData.message, 'none');
          }
          resolve(rawData);
        } else if (res.statusCode === 401) {
          // token 失效处理
          reject(res);
        } else {
          switch (res.statusCode) {
            case 404:
              showToast('请求资源不存在');
              break;
            case 500:
              showToast('服务器错误');
              break;
            case 502:
              showToast('找不到对应服务器');
              break;
            default:
              showToast(rawData?.message || '请求异常');
              break;
          }
          reject(res);
        }
      },
    });
  });

  // 存入缓存
  requestCache.set(cacheKey, { promise, controller });

  promise.finally(() => {
    // 请求结束删除缓存
    requestCache.delete(cacheKey);
  });

  // 需要将 controller 一并return出去才能在外部使用
  return { promise, controller };
}