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 };
}