章
目
录
UniApp开发与后端API进行交互时,后端服务可能会因各种原因(如维护、过载等)返回503状态码,这意味着服务临时不可用 。为了提升应用的健壮性和用户体验,避免手动编写繁琐的重试逻辑,我们可以使用一个专为UniApp框架设计的增强型HTTP请求封装模块。该模块基于uni.request
,用TypeScript编写,下面来详细了解一下。
一、模块功能特性介绍
(一)503自动重试
当调用后端接口遇到503 Service Unavailable
错误时,该模块无需手动干预,会自动根据预设或自定义的规则进行重试。这一功能避免了在每个请求中都去编写重试代码的麻烦,极大地简化了开发流程。
(二)灵活的配置选项
此模块支持全局设置默认的重试次数和延迟时间,同时也允许在单次请求时覆盖这些默认配置。开发者可以根据不同的业务场景,灵活调整重试策略。
(三)Promise化处理
模块完全基于Promise,这使得它能完美支持async/await
语法。使用这种方式编写异步代码,逻辑更加清晰,可读性更高,也方便进行错误处理。
(四)辅助函数便捷调用
模块提供了httpGet
、httpPost
、httpPut
、httpDelete
等常用方法的快捷方式。通过这些辅助函数,开发者可以更简便地发起不同类型的HTTP请求,减少重复代码的编写。
(五)结构化错误处理
当请求失败时,模块会reject
一个包含详细信息的HttpRequestError
对象。这个对象包含错误消息、原始错误、状态码以及是否为网络错误等信息,有助于开发者进行精细化的错误处理,快速定位和解决问题。
(六)可控的错误提示
请求失败时,模块默认会通过uni.showToast
弹出提示,告知用户请求失败的信息。不过,如果开发者有自定义UI反馈的需求,可以通过设置hideErrorToast: true
选项来禁用默认提示。
(七)类型安全保障
借助TypeScript的泛型<T>
,开发者可以指定期望的响应数据类型。这样在编译阶段就能进行更严格的类型检查,有效减少因类型不匹配导致的错误,提高代码的稳定性。
(八)独立纯净的设计
该模块不依赖外部状态管理库(如Pinia/Vuex),具有很强的独立性。这使得它能轻松集成到任何UniApp项目中,不会给项目引入额外的依赖负担。
二、适用场景分析
这个HTTP请求封装模块适用于多种场景:
- 任何涉及与后端API交互的UniApp项目,都可以使用该模块来简化网络请求的处理过程。
- 对于那些希望提高应用对后端临时故障容错能力的项目,该模块的503自动重试功能能有效提升应用的稳定性。
- 寻求更简洁、统一的网络请求处理方式的开发者,也可以通过使用这个模块,优化项目中的请求代码结构。
三、使用方法详解
使用该模块时,先将request.ts
(假设模块文件名为request.ts
)放置在项目的工具(utils)目录下。然后,在需要发起网络请求的页面或组件中,导入http
或其辅助函数(如httpGet
、httpPost
等)进行调用即可。下面是一个简单的示例:
// 示例:
import { http } from '@/utils/request'; // 假设你放在 utils 目录下
async function fetchData() {
try {
const data = await http.get<MyDataType>('/api/data');
console.log(data);
} catch (error) {
console.error('请求失败:', error);
// 处理错误...
}
}
四、代码解析
(一)配置常量
// --- 配置常量 ---
const DEFAULT_MAX_RETRIES = 2; // 默认最大重试次数 (总尝试次数 = 1 + 重试次数)
const DEFAULT_RETRY_DELAY = 1000; // 默认两次重试之间的延迟时间 (毫秒)
这里定义了两个常量,DEFAULT_MAX_RETRIES
表示默认的最大重试次数,总尝试次数是1(首次请求)加上重试次数;DEFAULT_RETRY_DELAY
表示默认的两次重试之间的延迟时间,单位为毫秒。
(二)TypeScript接口定义
// --- TypeScript 接口定义 ---
/**
* 扩展 UniApp 的 RequestOptions,增加自定义的重试配置
*/
interface RetryRequestOptions extends UniApp.RequestOptions {
/** 针对 503 错误的最大重试次数 (默认: 2) */
retries?: number;
/** 每次重试之间的延迟时间,单位毫秒 (默认: 1000) */
retryDelay?: number;
/** 设置为 true 可隐藏自动弹出的错误提示 Toast (默认: false) */
hideErrorToast?: boolean;
}
/**
* 定义一个结构化的 HTTP 请求错误对象,用于 Promise 的 reject
*/
interface HttpRequestError {
/** 错误消息文本 */
message: string;
/** uni-app 返回的原始错误对象或请求失败的响应对象 */
originalError: UniApp.GeneralCallbackResult | UniApp.RequestSuccessCallbackResult;
/** 标志是否为网络连接错误 (即 fail 回调触发) */
isNetworkError?: boolean;
/** HTTP 状态码 (如果是非 2xx 的成功响应) */
statusCode?: number;
}
RetryRequestOptions
接口扩展了UniApp原有的RequestOptions
,增加了retries
(最大重试次数)、retryDelay
(重试延迟时间)和hideErrorToast
(是否隐藏错误提示Toast)三个自定义配置项。HttpRequestError
接口则定义了请求失败时Promise
被reject
时传递的错误对象结构,包含错误消息、原始错误对象、是否为网络错误标志以及状态码等信息。
(三)核心HTTP请求函数
// --- 核心 HTTP 请求函数 ---
/**
* 发起一个 HTTP 请求 (基于 uni.request),并带有 503 自动重试逻辑。
*
* @template T - 期望的响应数据 `data` 的类型,默认为 `any`。
* @param {RetryRequestOptions} options - 请求配置,包含 uni.request 的标准选项以及自定义的重试选项。
* @returns {Promise<T>} - 返回一个 Promise。成功时 resolve 响应的 `data` 部分;失败时 reject 一个 `HttpRequestError` 对象。
*/
export const http = <T = any>(options: RetryRequestOptions): Promise<T> => {
// 解构选项,分离出重试配置和标准的 uni.request 配置
const {
retries = DEFAULT_MAX_RETRIES, // 获取重试次数,若未提供则使用默认值
retryDelay = DEFAULT_RETRY_DELAY, // 获取重试延迟,若未提供则使用默认值
hideErrorToast = false, // 获取是否隐藏错误提示的标志
...requestOptions // 剩余的选项作为 uni.request 的标准参数
} = options;
/**
* 内部函数,用于执行单次请求尝试
* @param currentAttempt - 当前是第几次尝试 (从 0 开始)
*/
const attemptRequest = (currentAttempt: number): Promise<T> => {
// 返回一个新的 Promise 来包装单次 uni.request 调用
return new Promise<T>((resolve, reject) => {
uni.request({
...requestOptions, // 传入标准的 uni.request 选项 (url, method, data, header 等)
// 请求成功的回调 (HTTP 状态码不一定是 2xx)
success(res) {
// --- 成功情况 (状态码 200-299) ---
if (res.statusCode >= 200 && res.statusCode < 300) {
// 假设服务器返回的有效数据在 res.data 中
resolve(res.data as T); // 使用 T 类型断言
return; // 成功,退出回调
}
// --- 需要重试的情况 (状态码 503 且 未达到最大重试次数) ---
if (res.statusCode === 503 && currentAttempt < retries) {
const attemptNum = currentAttempt + 1; // 计算下一次尝试的序号
console.warn(
`[HTTP] 请求失败,状态码 503。将在 ${retryDelay}ms 后进行第 ${attemptNum}/${retries} 次重试... URL: ${requestOptions.url}`
);
// 设置延迟执行下一次尝试
setTimeout(() => {
// 递归调用 attemptRequest,并将结果通过 then/catch 传递给外层 Promise
attemptRequest(attemptNum).then(resolve).catch(reject);
}, retryDelay);
return; // 等待重试,退出当前回调
}
// --- 其他失败情况 (非 2xx 状态码,或 503 重试次数已用尽) ---
// 构造错误消息
const errorMessage = `请求失败 [状态码: ${res.statusCode}]`;
console.error(`[HTTP] 请求错误 ${res.statusCode}: ${requestOptions.url}`, res);
// 如果用户没有选择隐藏提示,则显示 Toast
if (!hideErrorToast) {
uni.showToast({
icon: 'none',
// 尝试从响应体中获取更具体的错误信息,否则使用通用信息
title: (res.data as any)?.message || errorMessage,
duration: 2000,
});
}
// 用结构化的错误对象 reject Promise
reject({
message: errorMessage,
originalError: res, // 保留原始响应对象
statusCode: res.statusCode, // 记录状态码
} as HttpRequestError);
},
// 请求失败的回调 (网络层面的错误,例如 DNS 解析失败、网络不可达等)
fail(err) {
// --- 网络错误情况 ---
const errorMessage = '网络连接错误,请检查网络设置或稍后重试';
console.error(`[HTTP] 网络错误: ${requestOptions.url}`, err);
// 如果用户没有选择隐藏提示,则显示 Toast
if (!hideErrorToast) {
uni.showToast({
icon: 'none',
title: errorMessage,
duration: 2000,
});
}
// 用结构化的错误对象 reject Promise
reject({
message: errorMessage,
originalError: err, // 保留原始错误对象
isNetworkError: true, // 标记为网络错误
} as HttpRequestError);
},
// complete 回调在 success 或 fail 后都会执行,此处 Promise 的 resolve/reject 已处理最终状态,故无需操作
// complete() { }
});
});
};
// 发起第一次请求尝试 (currentAttempt = 0)
return attemptRequest(0);
};
http
函数是模块的核心,它基于uni.request
进行封装,并实现了503自动重试逻辑。函数接收一个RetryRequestOptions
类型的参数options
,通过解构获取重试配置和标准的uni.request
配置。内部定义的attemptRequest
函数用于执行单次请求尝试,根据请求结果(成功、需要重试、其他失败情况)进行不同处理。成功时,若状态码在200 – 299之间,解析响应数据并resolve
;遇到503状态码且未达到最大重试次数时,设置延迟后递归调用attemptRequest
进行重试;其他失败情况则构造错误消息,根据配置决定是否显示Toast,并reject
一个HttpRequestError
对象。最后,http
函数发起第一次请求尝试(currentAttempt = 0
)并返回attemptRequest(0)
的结果。
(四)便捷的辅助函数
// --- 便捷的辅助函数 ---
/** 定义辅助函数的选项类型,排除掉 url, method, data 这三个由辅助函数自身处理的参数 */
type HttpHelperOptions = Omit<RetryRequestOptions, 'url' | 'method' | 'data'>;
/**
* 发起 GET 请求 (带 503 重试)
* @template T - 期望的响应数据类型
* @param url - 请求地址
* @param data - 查询参数 (uni.request 中 GET 请求的 query 参数通常放在 data 对象里)
* @param options - 其他配置,如 headers, retries, hideErrorToast 等
*/
export const httpGet = <T = any>(
url: string,
data?: UniApp.RequestOptions['data'],
options?: HttpHelperOptions
): Promise<T> => {
return http<T>({
url,
data, // 对于 GET, uni.request 将 data 作为 query 参数附加到 URL
method: 'GET',
...options, // 合并其他选项
});
};
/**
* 发起 POST 请求 (带 503 重试)
* @template T - 期望的响应数据类型
* @param url - 请求地址
* @param data - 请求体数据
* @param options - 其他配置
*/
export const httpPost = <T = any>(
url: string,
data?: UniApp.RequestOptions['data'],
options?: HttpHelperOptions
): Promise<T> => {
return http<T>({
url,
data, // 请求体
method: 'POST',
...options,
});
};
/**
* 发起 PUT 请求 (带 503 重试)
* @template T - 期望的响应数据类型
* @param url - 请求地址
* @param data - 请求体数据
* @param options - 其他配置
*/
export const httpPut = <T = any>(
url: string,
data?: UniApp.RequestOptions['data'],
options?: HttpHelperOptions
): Promise<T> => {
return http<T>({
url,
data, // 请求体
method: 'PUT',
...options,
});
};
/**
* 发起 DELETE 请求 (带 503 重试)
* @template T - 期望的响应数据类型
* @param url - 请求地址
* @param data - 查询参数或请求体 (根据后端 API 设计,DELETE 有时也用 data 传递参数)
* @param options - 其他配置
*/
export const httpDelete = <T = any>(
url: string,
data?: UniApp.RequestOptions['data'],
options?: HttpHelperOptions
): Promise<T> => {
return http<T>({
url,
data,
method: 'DELETE',
...options,
});
};
// (可选) 将辅助函数附加到主 http 函数上,提供类似 axios 的调用风格
http.get = httpGet;
http.post = httpPost;
http.put = httpPut;
http.delete = httpDelete;
这部分代码定义了httpGet
、httpPost
、httpPut
、httpDelete
四个辅助函数,它们基于核心的http
函数,为常见的HTTP请求方法提供了更便捷的调用方式。每个辅助函数接收URL、数据(GET请求的查询参数或其他请求的请求体数据)和其他配置选项,然后调用http
函数并传入相应参数。最后,通过将这些辅助函数附加到主http
函数上,实现了类似axios的调用风格,方便开发者使用。
五、总结
这个UniApp的HTTP请求封装模块,通过提供503自动重试、灵活配置、Promise化处理、辅助函数等功能,为我们在处理网络请求时提供了极大的便利,不仅简化了开发流程,还增强了应用对后端临时故障的容错能力,是UniApp项目中处理HTTP请求的一个优秀选择。