网络框架封装:基于http模块封装拦截器与统一响应类(44)
一、 定义统一的响应数据模型为了规范后端返回的数据结构建议定义一个包含泛型的响应包装类将code、message和核心data统一管理// ResponseResult.ets export class ResponseResultT { code: number; msg: string; data?: T; constructor(code: number, msg: string, data?: T) { this.code code; this.msg msg; this.data data; } }二、 设计并实现拦截器接口通过定义拦截器接口我们可以将“请求前处理”和“响应后处理”抽离出来实现 AOP面向切面的效果// Interceptors.ets import { http } from kit.NetworkKit; // 1. 请求拦截器接口 export interface HttpRequestInterceptor { intercept: (options: http.HttpRequestOptions) http.HttpRequestOptions; } // 2. 响应拦截器接口 export interface HttpResponseInterceptor { intercept: (response: http.HttpResponse) http.HttpResponse; } // 3. 默认请求拦截器示例自动注入 Token export class DefaultHeadersInterceptor implements HttpRequestInterceptor { intercept(options: http.HttpRequestOptions): http.HttpRequestOptions { const headers options.header as Recordstring, string; const token your_auth_token; // 实际项目中可从安全存储中获取 if (token) { headers[Authorization] Bearer ${token}; } return options; } } // 4. 默认响应拦截器示例统一处理错误码 export class DefaultResponseInterceptor implements HttpResponseInterceptor { intercept(response: http.HttpResponse): http.HttpResponse { if (response.responseCode 401) { // 触发全局提示或跳转登录页 console.error(Token 失效请重新登录); } return response; } }三、 封装核心网络请求类 (NetworkManager)将拦截器链、URL 拼接、超时配置以及泛型解析封装在一个核心类中// NetworkManager.ets import { http } from kit.NetworkKit; import { ResponseResult } from ./ResponseResult; import { HttpRequestInterceptor, HttpResponseInterceptor, DefaultHeadersInterceptor, DefaultResponseInterceptor } from ./Interceptors; export class NetworkManager { private baseURL: string; private requestInterceptors: HttpRequestInterceptor[] []; private responseInterceptors: HttpResponseInterceptor[] []; constructor(baseURL: string) { this.baseURL baseURL; // 默认注入基础拦截器 this.requestInterceptors.push(new DefaultHeadersInterceptor()); this.responseInterceptors.push(new DefaultResponseInterceptor()); } // 支持外部动态添加拦截器 addRequestInterceptor(interceptor: HttpRequestInterceptor) { this.requestInterceptors.push(interceptor); } // 核心请求方法支持泛型 T async requestT(path: string, options: http.HttpRequestOptions): PromiseResponseResultT { let fullUrl this.baseURL path; let requestOptions { ...options }; // 1. 执行请求拦截器链 this.requestInterceptors.forEach(interceptor { requestOptions interceptor.intercept(requestOptions); }); const httpRequest http.createHttp(); try { // 2. 发起网络请求 let response await httpRequest.request(fullUrl, requestOptions); // 3. 执行响应拦截器链 this.responseInterceptors.forEach(interceptor { response interceptor.intercept(response); }); // 4. 解析数据并封装为统一格式 const resultData JSON.parse(response.result.toString()) as T; if (response.responseCode http.ResponseCode.OK) { return new ResponseResultT(200, success, resultData); } else { return new ResponseResultT(response.responseCode, 请求失败: ${response.responseCode}); } } catch (error) { return new ResponseResultT(-1, error.message || 网络请求异常); } finally { // 5. 销毁请求实例释放资源 httpRequest.destroy(); } } // 封装常用的 GET 方法 async getT(path: string, params?: Recordstring, any): PromiseResponseResultT { return this.requestT(path, { method: http.RequestMethod.GET, extraData: params // GET请求参数 }); } // 封装常用的 POST 方法 async postT(path: string, data?: object): PromiseResponseResultT { return this.requestT(path, { method: http.RequestMethod.POST, header: { Content-Type: application/json }, extraData: JSON.stringify(data) }); } }四、 业务层调用示例封装完成后业务层如 ViewModel只需关注接口路径和数据类型无需关心底层的 Token 注入和 JSON 解析// UserViewModel.ets import { NetworkManager } from ./NetworkManager; interface UserInfo { id: number; name: string; } const network new NetworkManager(https://api.example.com); export class UserViewModel { async fetchUserProfile(userId: number) { // 发起请求自动带上 Token自动解析为 UserInfo 类型 const res await network.getUserInfo(/user/${userId}); if (res.code 200 res.data) { console.log(获取到用户: ${res.data.name}); } else { console.error(获取失败: ${res.msg}); } } }五、 请求并发控制与防抖节流在鸿蒙 UI 交互中用户快速滑动列表或连续点击按钮极易触发大量重复请求。请求去重防抖在NetworkManager内部维护一个pendingRequests: Mapstring, Promiseany。发起请求前通过URL 参数生成唯一 Key若该 Key 已存在则直接返回缓存的 Promise避免重复发起网络请求。并发数限制对于列表页的瀑布流加载同时发起几十个图片请求会导致 OOM 或系统限流。可以引入类似p-limit的并发控制逻辑限制同时处于Pending状态的请求数量如最大 5 个超出的请求放入等待队列。1. 请求去重防抖与拦截实现通过维护一个Map来缓存正在执行的 Promise可以完美解决快速滑动或连点导致的重复请求问题。class RequestDeduplicationController { // 核心维护一个正在执行的请求池 private pendingRequests: Mapstring, Promiseany new Map(); /** * param key 请求的唯一标识如 URL 序列化后的参数 * param requestFn 实际的网络请求函数 */ async deduplicatedRequestT(key: string, requestFn: () PromiseT): PromiseT { // 1. 拦截如果该 Key 已有请求在执行直接返回缓存的 Promise if (this.pendingRequests.has(key)) { console.info([RequestDedup] 命中去重拦截: ${key}); return this.pendingRequests.get(key) as PromiseT; } // 2. 发起新请求并加入池中 const requestPromise requestFn(); this.pendingRequests.set(key, requestPromise); try { const result await requestPromise; return result; } finally { // 3. 无论成功或失败请求结束后必须从池中移除 this.pendingRequests.delete(key); } } } // 使用场景示例 const dedupController new RequestDeduplicationController(); // 在组件中触发请求 async function fetchUserDetail(userId: string) { const key user_detail_${userId}; return dedupController.deduplicatedRequest(key, async () { // 实际的 HTTP 请求 return await httpRequest.get(/api/users/${userId}); }); }2. 并发数限制类似 p-limit实现对于瀑布流加载等场景我们需要一个任务队列和并发计数器来限制同时处于 Pending 状态的请求数量。class PLimitController { private queue: Array() void []; // 等待队列 private activeCount: number 0; // 当前正在执行的任务数 private concurrency: number; // 最大并发数 constructor(concurrency: number) { this.concurrency concurrency; } /** * 包装异步任务 */ async enqueueT(taskFn: () PromiseT): PromiseT { return new Promise((resolve, reject) { // 将任务包装后推入队列 this.queue.push(async () { try { const result await taskFn(); resolve(result); } catch (error) { reject(error); } finally { // 任务执行完毕释放并发槽位并触发下一个任务 this.activeCount--; this.runNext(); } }); // 尝试启动任务 this.runNext(); }); } private runNext(): void { // 如果未达到并发上限且队列不为空则取出任务执行 if (this.activeCount this.concurrency this.queue.length 0) { this.activeCount; const nextTask this.queue.shift(); nextTask?.(); } } } // 使用场景示例 // 限制图片请求最大并发数为 5 const imageLimit new PLimitController(5); // 在列表加载时批量发起请求 async function loadWaterfallImages(imageUrls: string[]) { const tasks imageUrls.map(url imageLimit.enqueue(() fetchImageFromNetwork(url))); // 等待所有图片下载完成实际并发被严格控制在5个以内 const results await Promise.all(tasks); return results; }3. 进阶优化结合防抖Debounce对于搜索框输入等高频触发事件除了去重还需要加入时间维度的防抖控制class SearchRequestManager { private debounceTimer: number -1; private readonly DEBOUNCE_DELAY: number 300; // 300ms 防抖 triggerSearch(query: string, callback: (res: any) void) { // 清除上一个定时器 clearTimeout(this.debounceTimer); // 重新计时只有用户停顿 300ms 后才真正发起请求 this.debounceTimer setTimeout(async () { const result await fetchSearchResults(query); callback(result); }, this.DEBOUNCE_DELAY) as unknown as number; } }六、 多级缓存策略Cache-First 模式鸿蒙设备通常对流畅度要求极高纯网络请求在弱网下体验极差。内存缓存LruCache对于高频访问且数据量较小的字典类接口使用内存 LRU 缓存实现毫秒级响应。持久化缓存结合鸿蒙的Preferences或RDB关系型数据库将核心数据落盘。在发起网络请求时采用“先展示缓存数据后台静默刷新刷新成功后更新 UI”的策略即 Stale-While-Revalidate 模式。七、 异常精细化处理与重试机制基础的try-catch往往无法应对复杂的网络环境。网络状态监听利用鸿蒙的ohos.net.connection模块监听网络状态。当检测到断网时直接拦截请求并返回离线提示避免无意义的超时等待。智能重试Retry对于502 Bad Gateway、504 Gateway Timeout或网络抖动导致的异常在响应拦截器中实现指数退避重试算法Exponential Backoff自动重试 1~3 次。Token 无感刷新在响应拦截器中捕获401 Unauthorized。此时不要立即跳转登录页而是挂起当前请求调用刷新 Token 接口。刷新成功后使用新 Token 重新发起被挂起的请求若刷新失败再强制登出。八、 拥抱鸿蒙原生能力升级 kit.NetworkKit在 HarmonyOS NEXTAPI 12中官方对网络模块进行了重构。废弃旧模块ohos.net.http已被标记为即将废弃建议全面迁移至kit.NetworkKit。使用 HttpClient新版HttpClient提供了更现代的 API 设计原生支持配置连接池、TLS 证书校验、以及更优雅的异步回调机制大幅提升了底层网络请求的性能和安全性。九、 安全合规与证书固定Certificate Pinning金融级或高安全要求的鸿蒙应用必须防范中间人攻击MITM。证书固定在NetworkManager初始化时通过鸿蒙的caCerts配置项将服务端的公钥证书或 CA 根证书硬编码到客户端。即使攻击者伪造了系统级证书网络请求也会因证书校验失败而被拦截。敏感数据脱敏在请求拦截器打印日志时务必对 Header 中的Authorization和 Body 中的密码、身份证号等字段进行掩码处理防止在 Debug 模式下发生数据泄露。十、 结合 MVVM 与 DI 的终极形态将网络层与前面讨论的架构深度结合DI 注入网络实例通过provide将配置好拦截器和缓存策略的NetworkManager注入到 IoC 容器中。Repository 模式ViewModel 不再直接调用NetworkManager而是调用UserRepository。Repository 内部决定是先读缓存还是先走网络并将结果封装为Flow或StateFlow响应式流View 层只需绑定状态即可实现极致丝滑的数据驱动 UI。1. 配置网络实例与 IoC 容器注入首先我们需要一个配置好拦截器、缓存策略和并发控制的NetworkManager。在鸿蒙或跨平台架构中可以通过 IoC控制反转容器将其作为单例注入。// 1. 定义网络管理器接口面向抽象编程 export interface INetworkManager { getT(url: string, params?: Recordstring, any): PromiseT; } // 2. 实现网络管理器整合拦截器与缓存 export class NetworkManager implements INetworkManager { private httpClient: HttpClient; constructor() { this.httpClient new HttpClient(); // 注入请求拦截器如统一添加 Token this.httpClient.addRequestInterceptor((config) { config.headers[Authorization] Bearer ${getToken()}; return config; }); // 注入响应拦截器统一处理错误码 this.httpClient.addResponseInterceptor((response) { if (response.code ! 200) throw new Error(response.msg); return response.data; }); } async getT(url: string, params?: Recordstring, any): PromiseT { // 内部可结合前面讨论的并发控制和去重逻辑 return this.httpClient.request(url, { method: GET, params }) as PromiseT; } } // 3. IoC 容器注册以伪代码/通用DI框架为例 // 将配置好的 NetworkManager 作为单例注入到全局容器 Container.provide(INetworkManager, () new NetworkManager(), { singleton: true });2. Repository 模式封装数据策略Repository 是数据的统一入口。它不关心数据是来自本地数据库还是网络而是根据业务逻辑如先读缓存无缓存则请求网络并更新缓存协调数据源。export class UserRepository { // 通过构造函数注入网络实例实现解耦 constructor( private networkManager: INetworkManager, private localCache: ILocalCache ) {} /** * 获取用户列表响应式流 */ async *fetchUsers(page: number): AsyncGeneratorUser[], void, undefined { // 1. 优先读取本地缓存实现首屏秒开 const cachedUsers await this.localCache.getUsers(page); if (cachedUsers) yield cachedUsers; // 2. 发起网络请求 try { const remoteUsers await this.networkManager.getUser[](/api/users, { page }); // 3. 将最新数据写入本地缓存 await this.localCache.saveUsers(page, remoteUsers); // 4. 发射最新网络数据触发 UI 更新 yield remoteUsers; } catch (error) { // 如果网络失败且没有缓存抛出异常交由 ViewModel 处理 if (!cachedUsers) throw error; } } }3. ViewModel 消费响应式流ViewModel 负责持有 Repository 并将数据转化为 UI 层可直接绑定的状态如StateFlow。export class UserViewModel { private userRepository: UserRepository; // 定义 UI 状态密封类/联合类型 private _uiState new StateFlowUserUiState(new LoadingState()); public get uiState(): StateFlowUserUiState { return this._uiState; } constructor() { // 从 IoC 容器获取依赖 const network Container.resolveINetworkManager(INetworkManager); const cache Container.resolveILocalCache(ILocalCache); this.userRepository new UserRepository(network, cache); } public async loadUsers(page: number) { try { this._uiState.value new LoadingState(); // 消费 Repository 提供的响应式流 for await (const users of this.userRepository.fetchUsers(page)) { this._uiState.value new SuccessState(users); } } catch (error) { this._uiState.value new ErrorState((error as Error).message); } } }4. View 层声明式绑定在鸿蒙 ArkUI 等声明式 UI 框架中View 层只需监听 ViewModel 的状态变化实现零业务代码的极致丝滑渲染。Component export struct UserListPage { ObjectLink vm: UserViewModel; State currentState: UserUiState new LoadingState(); aboutToAppear() { // 监听 StateFlow 的变化自动映射到组件状态 this.vm.uiState.subscribe((state) { this.currentState state; }); this.vm.loadUsers(1); } build() { Column() { if (this.currentState instanceof LoadingState) { LoadingProgress() } else if (this.currentState instanceof ErrorState) { Text(加载失败: ${this.currentState.message}) } else if (this.currentState instanceof SuccessState) { List() { ForEach(this.currentState.users, (user: User) { ListItem() { Text(user.name) } }) } } } } }

相关新闻

股票行业数据

股票行业数据

# -*- coding: utf-8 -*- import tushare as ts import time import pandas as pd import csv# Tushare token TUSHARE_TOKEN "ee5c0e991e17949cdafbcf8ec42321ef4bac94e9ca3474e4d62313a3" ts.set_token(TUSHARE_TOKEN) pro ts.pro_api()# 获取申万三级行业列表 d…

2026/7/24 21:56:51阅读更多 →
Betaflight配置器:让无人机飞控调参变得像玩游戏一样简单

Betaflight配置器:让无人机飞控调参变得像玩游戏一样简单

Betaflight配置器:让无人机飞控调参变得像玩游戏一样简单 【免费下载链接】betaflight-configurator Cross platform configuration and management application for the Betaflight firmware 项目地址: https://gitcode.com/gh_mirrors/be/betaflight-configurat…

2026/7/24 21:54:51阅读更多 →
地域环境对家电设备的损耗分析:贵阳本地维保方案实测与经验总结

地域环境对家电设备的损耗分析:贵阳本地维保方案实测与经验总结

作为长期扎根贵阳的居家技术爱好者,我一直习惯从设备工况、环境适配、损耗逻辑、运维成本的角度,复盘家用及小型商用电器的使用问题。在日常交流中发现,很多用户存在一个普遍性认知误区:家电故障、耗电激增、寿命缩短,…

2026/7/24 21:54:51阅读更多 →
AI辅助学术写作:从文献精华提取到摘要生成

AI辅助学术写作:从文献精华提取到摘要生成

1. 项目概述:AI如何重塑学术写作体验去年帮导师审阅研究生论文时,我发现一个有趣现象:超过70%的学术新手在摘要写作环节平均耗时3小时以上,且普遍存在"重要数据遗漏"或"关键结论模糊"的问题。这正是"好写…

2026/7/25 3:37:50阅读更多 →
YOLO算法在智慧工地车辆清洗监测中的应用实践

YOLO算法在智慧工地车辆清洗监测中的应用实践

1. 项目背景与核心价值在现代化建筑施工领域,工程车辆进出工地时的清洁管理一直是环保监管的重点难点。传统人工检查方式存在效率低下、标准不统一、记录难追溯等问题。这个基于YOLO算法的智慧工地车辆清洗监测系统,正是为了解决这些痛点而生。我参与过多…

2026/7/25 3:37:50阅读更多 →
Linux用户权限管理与安全实践详解

Linux用户权限管理与安全实践详解

1. Linux用户与权限系统概述在Linux系统中,用户和权限管理是系统安全的核心机制。这套体系源自Unix的多用户设计哲学,通过严格的权限控制实现不同用户对系统资源的隔离访问。我管理过的生产服务器上,曾因为一个配置不当的sudo权限导致整个数据…

2026/7/25 3:37:50阅读更多 →
Unity UGUI拖拽功能实现:从事件处理到边界限制的完整指南

Unity UGUI拖拽功能实现:从事件处理到边界限制的完整指南

1. 项目概述与核心价值最近在社区里看到不少朋友在讨论Unity的UI交互,尤其是拖拽功能的应用。很多新手在尝试制作背包、仓库、技能栏这类系统时,第一个拦路虎往往就是“怎么让图标跟着鼠标走,还能限制它不乱跑”。这确实是个挺典型的痛点&…

2026/7/25 3:37:50阅读更多 →
Agent产品在医疗行业的落地探索:合规、数据安全与场景适配的三重挑战

Agent产品在医疗行业的落地探索:合规、数据安全与场景适配的三重挑战

Agent产品在医疗行业的落地探索:合规、数据安全与场景适配的三重挑战 一、为什么医疗行业的AI落地总是"雷声大雨点小"? 过去两年,几乎每一家大模型厂商都发布了"AI医疗"的解决方案。但走进医院信息科实地调研时会发现&am…

2026/7/25 3:37:50阅读更多 →
多模态大模型技术突破与应用实践

多模态大模型技术突破与应用实践

1. 多模态大模型的技术突破最近在AI领域出现了一个引起广泛关注的技术进展——新一代多模态大模型在多个基准测试中取得了突破性表现。这种模型架构能够同时处理文本、图像、音频等多种数据形式,在理解、推理和生成能力上都展现出显著优势。作为一名长期跟踪AI技术发…

2026/7/25 3:35:49阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/25 1:01:14阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/25 1:01:14阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/25 1:01:14阅读更多 →
突破文档下载限制:kill-doc让你看到的都能保存

突破文档下载限制:kill-doc让你看到的都能保存

突破文档下载限制:kill-doc让你看到的都能保存 【免费下载链接】kill-doc 看到经常有小伙伴们需要下载一些免费文档,但是相关网站浏览体验不好各种广告,各种登录验证,需要很多步骤才能下载文档,该脚本就是为了解决您的…

2026/7/25 0:01:16阅读更多 →
C++ string类模拟实现:从深拷贝到内存管理的完整指南

C++ string类模拟实现:从深拷贝到内存管理的完整指南

1. 项目概述:为什么我们要“手撕”string类?在C的学习道路上,尤其是从C语言过渡到C的“初阶”阶段,string类绝对是一个绕不开的核心。标准库里的std::string用起来太方便了,、find、substr,几个操作符和函数…

2026/7/25 0:01:16阅读更多 →
三角洲寻宝鼠工具:高效文件搜索与资源管理实战指南

三角洲寻宝鼠工具:高效文件搜索与资源管理实战指南

1. 先搞清楚“三角洲寻宝鼠”到底是什么工具从名称来看,“三角洲寻宝鼠”更像是一个资源查找或文件检索类工具,而不是游戏或娱乐软件。这类工具的核心价值在于帮助用户快速定位特定资源,比如文档、图片、压缩包或特定格式的文件。如果你经常需…

2026/7/25 0:01:16阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/24 23:01:03阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/24 19:00:40阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/24 19:00:40阅读更多 →