1. 为什么企业级应用需要更强大的网络请求库在开发一个中型电商App时网络请求就像App的血管系统负责在各个功能模块之间输送数据。你可能已经用过简单的HTTP请求封装但当用户量达到十万级别时问题就开始显现了某个促销活动导致服务器压力激增部分请求超时用户长时间使用后token过期需要自动刷新某些关键接口需要记录完整的请求日志用于排查问题...我去年参与开发的一个跨境电商项目就遇到过这样的困境。最初我们使用简单的请求封装随着业务复杂度提升代码里到处都是重复的错误处理逻辑。后来花了三周时间重构网络层才让系统稳定下来。这段经历让我深刻认识到一个好的网络请求库应该是可配置、可扩展、易维护的。Promise在HarmonyOS中的实现非常成熟它解决了传统回调地狱的问题。但仅仅使用Promise还不够我们还需要拦截器机制在请求发出前和响应返回后插入处理逻辑错误统一处理避免在每个请求里重复写错误提示请求队列管理防止高并发场景下的资源竞争自动重试策略对特定类型的失败请求进行智能重试2. 构建基础Promise请求框架2.1 设计核心接口我们先从基础结构开始定义三个核心类型// 响应数据结构 interface ResponseDataT { code: number message: string data: T timestamp?: number } // 请求配置项 interface RequestConfig { url: string method?: HttpMethod // GET/POST/PUT等 params?: object // URL参数 data?: object // 请求体数据 headers?: Recordstring, string timeout?: number // 单独设置超时 retry?: number // 重试次数 } // 拦截器类型 interface Interceptor { onRequest?: (config: RequestConfig) RequestConfig | PromiseRequestConfig onResponse?: T(response: ResponseDataT) ResponseDataT | PromiseResponseDataT onError?: (error: Error) Promisenever }这个设计比基础封装更完善的地方在于明确区分了URL参数(params)和请求体数据(data)每个请求可以单独配置超时和重试策略预留了扩展字段如timestamp2.2 实现基础请求方法让我们实现一个带Promise的基础请求方法class HttpRequest { private baseURL: string private defaultConfig: RequestConfig { timeout: 10000, retry: 0 } constructor(baseURL: string) { this.baseURL baseURL } async requestT(config: RequestConfig): PromiseResponseDataT { const mergedConfig { ...this.defaultConfig, ...config } const httpRequest http.createHttp() try { const options: HttpRequestOptions { method: mergedConfig.method || HttpMethod.GET, header: mergedConfig.headers, extraData: mergedConfig.data, expectDataType: HttpDataType.OBJECT } return new Promise((resolve, reject) { httpRequest.request( this.buildURL(mergedConfig), options, (err, data) { httpRequest.destroy() if (err) { reject(this.normalizeError(err)) } else { resolve(this.normalizeResponseT(data)) } } ) }) } catch (err) { return Promise.reject(err) } } private buildURL(config: RequestConfig): string { // 处理URL拼接和参数序列化 let url this.baseURL config.url if (config.params) { url ? new URLSearchParams(config.params).toString() } return url } }关键点说明使用配置合并策略允许全局默认值和单个请求特殊配置统一的URL构建方法规范参数处理错误和响应的标准化处理3. 实现拦截器系统3.1 设计拦截器链拦截器是网络库最强大的特性之一。在我们的电商App中这些场景特别有用请求签名给重要接口添加数字签名Token刷新401错误时自动刷新token性能监控记录关键接口耗时数据加解密敏感字段自动加密实现拦截器管理器class InterceptorManager { private interceptors: Interceptor[] [] use(interceptor: Interceptor): number { this.interceptors.push(interceptor) return this.interceptors.length - 1 // 返回拦截器ID用于移除 } eject(id: number): void { this.interceptors[id] null } async runRequestInterceptors(config: RequestConfig): PromiseRequestConfig { let finalConfig { ...config } for (const interceptor of this.interceptors) { if (interceptor?.onRequest) { finalConfig await interceptor.onRequest(finalConfig) } } return finalConfig } async runResponseInterceptorsT(response: ResponseDataT): PromiseResponseDataT { let finalResponse { ...response } for (const interceptor of this.interceptors) { if (interceptor?.onResponse) { finalResponse await interceptor.onResponse(finalResponse) } } return finalResponse } }3.2 典型拦截器示例Token自动刷新拦截器const tokenRefreshInterceptor: Interceptor { onError: async (error) { if (error.status 401 !error.config._isRetry) { // 标记当前请求避免死循环 error.config._isRetry true // 调用刷新token接口 const newToken await refreshToken() // 更新后续请求的header error.config.headers.Authorization Bearer ${newToken} // 重试原始请求 return http.request(error.config) } return Promise.reject(error) } }请求日志拦截器const loggerInterceptor: Interceptor { onRequest: (config) { console.log([Request] ${config.method} ${config.url}, { params: config.params, data: config.data }) return config }, onResponse: (response) { console.log([Response] ${response.code}, response.data) return response } }4. 高级功能实现4.1 请求队列与并发控制在商品秒杀场景下突然爆发的请求可能导致服务器过载。我们需要实现class RequestQueue { private maxConcurrent: number private activeCount 0 private queue: Array() Promiseany [] constructor(maxConcurrent 5) { this.maxConcurrent maxConcurrent } add(requestFn: () Promiseany): Promiseany { return new Promise((resolve, reject) { const task async () { try { this.activeCount const result await requestFn() resolve(result) } catch (error) { reject(error) } finally { this.activeCount-- this.next() } } this.queue.push(task) this.next() }) } private next() { while (this.activeCount this.maxConcurrent this.queue.length) { const task this.queue.shift() task() } } }使用方式const queue new RequestQueue(3) // 限制3个并发 async function fetchProductDetail(id: string) { return queue.add(() http.request({ url: /products/${id}, method: HttpMethod.GET }) ) }4.2 智能重试机制不是所有失败都值得重试我们实现一个带策略的重试async function requestWithRetryT( config: RequestConfig, retryCount 3 ): PromiseResponseDataT { let lastError: Error for (let i 0; i retryCount; i) { try { const response await http.requestT({ ...config, // 指数退避 timeout: config.timeout * (i 1) }) return response } catch (error) { lastError error // 只有网络错误和5xx错误才重试 if (!isNetworkError(error) !isServerError(error)) { break } if (i retryCount) { await sleep(1000 * Math.pow(2, i)) // 指数退避等待 } } } return Promise.reject(lastError) }5. 完整实现与最佳实践5.1 组装完整网络库现在我们把所有部件组合起来class HttpClient { private requestQueue new RequestQueue() private interceptors new InterceptorManager() constructor(private baseURL: string) {} async requestT(config: RequestConfig): PromiseResponseDataT { try { // 执行请求拦截器 const processedConfig await this.interceptors.runRequestInterceptors(config) // 加入队列管理 const response await this.requestQueue.add(() makeActualRequestT(processedConfig) ) // 执行响应拦截器 return this.interceptors.runResponseInterceptors(response) } catch (error) { // 执行错误拦截器 for (const interceptor of this.interceptors) { if (interceptor?.onError) { return interceptor.onError(error) } } throw error } } // 添加便捷方法 getT(url: string, params?: object) { return this.requestT({ url, method: HttpMethod.GET, params }) } postT(url: string, data?: object) { return this.requestT({ url, method: HttpMethod.POST, data }) } use(interceptor: Interceptor) { return this.interceptors.use(interceptor) } }5.2 在电商App中的典型应用商品详情页的请求处理// 初始化客户端 const http new HttpClient(https://api.ecommerce.com) // 添加全局拦截器 http.use(tokenRefreshInterceptor) http.use(loggerInterceptor) http.use({ onRequest: (config) { // 自动添加认证token if (!config.headers) config.headers {} config.headers.Authorization Bearer ${getToken()} return config } }) // 在页面中使用 async function loadProductDetail(productId: string) { try { const { data } await http.getProduct(/products/${productId}, { timeout: 5000, retry: 2 }) // 更新UI... } catch (error) { if (error.status 404) { showProductNotFound() } else { showNetworkError() } } }关键优化点所有商品请求自动携带认证信息重要接口设置较短超时和重试策略统一的错误处理逻辑完整的请求日志记录6. 性能优化与调试技巧6.1 网络性能监控在Interceptor中添加性能统计const perfInterceptor: Interceptor { onRequest: (config) { config.metadata { startTime: Date.now() } return config }, onResponse: (response) { const duration Date.now() - response.config.metadata.startTime trackApiPerformance(response.config.url, duration) return response } }6.2 调试技巧开发阶段可以添加这些调试工具// 在开发环境添加调试拦截器 if (process.env.NODE_ENV development) { http.use({ onRequest: (config) { console.groupCollapsed(%c ${config.method} ${config.url}, color: #4CAF50) console.log(Params:, config.params) console.log(Data:, config.data) console.groupEnd() return config }, onResponse: (response) { console.groupCollapsed(%c Response ${response.code}, color: #2196F3) console.log(Data:, response.data) console.groupEnd() return response } }) }7. 安全加固方案7.1 数据加密拦截器对敏感接口启用加密const cryptoInterceptor: Interceptor { onRequest: (config) { if (config.needEncrypt) { config.data { encrypted: encrypt(JSON.stringify(config.data)), timestamp: Date.now() } } return config }, onResponse: (response) { if (response.data?.encrypted) { response.data JSON.parse(decrypt(response.data.encrypted)) } return response } }7.2 防CSRF处理自动添加CSRF Tokenconst csrfInterceptor: Interceptor { onRequest: (config) { if (config.method ! HttpMethod.GET) { config.headers[X-CSRF-Token] getCSRFToken() } return config } }8. 测试策略8.1 单元测试重点为网络库编写测试时要覆盖基础请求功能拦截器执行顺序错误处理流程队列管理逻辑重试机制示例测试用例describe(HttpClient, () { let http: HttpClient let mockServer: MockServer beforeEach(() { mockServer new MockServer() http new HttpClient(mockServer.getBaseUrl()) }) it(should execute request interceptors in order, async () { const order: number[] [] http.use({ onRequest: () (order.push(1), {}) }) http.use({ onRequest: () (order.push(2), {}) }) await http.get(/test) expect(order).toEqual([1, 2]) }) it(should retry on network failure, async () { mockServer.setResponse(/retry, { status: 500 }) try { await http.get(/retry, { retry: 2 }) } catch (error) { expect(mockServer.getCallCount(/retry)).toBe(3) } }) })8.2 压力测试建议使用真实场景测试模拟100个用户同时抢购测试token过期时的自动刷新模拟弱网环境下的请求表现验证内存泄漏情况9. 与简单封装的对比9.1 代码复杂度对比传统方式在每个请求中处理错误async function getProduct(id: string) { try { const response await fetch(/products/${id}) if (!response.ok) throw new Error(Request failed) const data await response.json() if (data.code ! 200) { if (data.code 401) { // 处理token过期 await refreshToken() return getProduct(id) } throw new Error(data.message) } return data } catch (error) { console.error(Failed to load product, error) throw error } }使用我们的封装后// 初始化时配置一次 http.use(tokenRefreshInterceptor) http.use(errorHandlerInterceptor) // 业务代码变得极其简洁 function getProduct(id: string) { return http.getProduct(/products/${id}) }9.2 维护成本对比当需要修改所有请求的超时时间时传统方式需要修改每个请求调用处我们的方案只需修改默认配置或添加拦截器10. 常见问题解决方案10.1 内存泄漏预防确保做到每个请求完成后销毁httpRequest对象拦截器中避免保留请求引用使用WeakMap存储请求元数据10.2 取消请求实现HarmonyOS目前没有内置的取消机制我们可以模拟实现class CancelToken { private promise: Promisenever private resolve: (reason?: any) void constructor() { this.promise new Promise((resolve) { this.resolve resolve }) } cancel(reason?: string) { this.resolve(new Error(reason || Request cancelled)) } get token(): Promisenever { return this.promise } } // 使用方式 const cancelToken new CancelToken() http.get(/data, { cancelToken: cancelToken.token }).catch(err { if (err.message Request cancelled) { console.log(请求被取消) } }) // 需要取消时 cancelToken.cancel()11. 实际项目集成建议11.1 目录结构规划推荐这样组织代码src/ network/ http/ HttpClient.ts # 核心实现 interceptors/ # 各种拦截器 auth.ts logger.ts ... types/ # 类型定义 utils/ # 工具函数 index.ts # 入口文件11.2 多环境配置通过工厂方法创建不同环境的客户端function createHttpClient(env: dev | prod) { const baseURL { dev: https://dev.api.com, prod: https://api.com }[env] const client new HttpClient(baseURL) if (env dev) { client.use(devLoggerInterceptor) } return client }12. 扩展思路12.1 WebSocket集成可以扩展为统一的网络模块class NetworkManager { private http: HttpClient private websocket: WebSocketClient constructor(config: NetworkConfig) { this.http new HttpClient(config.http) this.websocket new WebSocketClient(config.ws) } // 统一的消息处理 onMessage(handler: (msg: Message) void) { this.websocket.onMessage(handler) } }12.2 离线缓存策略添加缓存拦截器const cacheInterceptor: Interceptor { onRequest: async (config) { if (config.cache) { const cached await cacheStore.get(config.url) if (cached) return cached } return config }, onResponse: (response) { if (response.config.cache) { cacheStore.set(response.config.url, response) } return response } }在电商App的商品详情页这种读多写少的场景特别有用可以显著减少网络请求。