为啥要封装一个网络请求库
直接使用鸿蒙官方的 http 模块,每个请求都要 createHttp()、设 header、解析响应、destroy()……太繁琐了。
今天分享一套我自己封装的网络库,支持全局配置、链式 API、请求/响应拦截器、JSON/表单/二进制/文件上传,完整代码已整理上传。
httpDemo:https://gitee.com/HarmonyOS-UI-Basics/harmony-os-ui-basics.git。
先看效果
将网络请求模块化处理:我们需要准备啥?以登录接口为例:
// 1. 定义请求体接口interface LoginRequest { username: string; password: string;}
// 定义响应体接口 依据后端返回数据格式interface ApiResponse<T> { code: number; message: string; data: T;}interface AuthTokenModel { accessToken: string; refreshToken: string; userId: string;}
const request: LoginRequest = { username: this.username.trim(), password: encryptedPassword };
const resp = await httpClient.post(APIConstants.API_LOGIN) .json(request) .execute<ApiResponse<AuthTokenModel>>();// 使用响应if (resp.status === 200 && resp.data.code === 200) { this.authToken.accessToken = resp.data.data.accessToken; this.authToken.refreshToken = resp.data.data.refreshToken; this.authToken.userId = resp.data.data.userId; promptAction.showToast({ message: '登录成功' });} else { promptAction.showToast({ message: resp.data.message || '登录失败' });}
注意:
httpClient.post(url) 中的 url 是相对路径(如 'api/v1/auth/login'),baseUrl 已在 HttpConfig 中全局配置。
.json(parameter) 自动序列化并设置 Content-Type: application/json。
通过泛型 <ApiResponse<AuthTokenModel>> 直接获得类型安全的响应数据。
无需手动 createHttp()、解析 response.result、destroy(),库内部已处理。
全局配置
在应用启动时(例如 EntryAbility 的 onCreate 或 Index 页面的 aboutToAppear),对网络库进行全局配置:
import { httpClient, HttpConfig } from '@happy/http';import { APIConstants } from '../common/APIConstants';// 配置全局网络参数(只需执行一次)HttpConfig.getInstance() .setBaseUrl(APIConstants.BASE_URL) .setTimeout(30000) // 全局超时(毫秒) .setDefaultHeader('Content-Type', 'application/json');// 请求拦截器:自动注入 tokenhttpClient.addRequestInterceptor({ onRequest: (options) => { options.token = token; return options; }});// 响应拦截器:统一处理 401httpClient.addResponseInterceptor({ onResponse: (response) => { if (response.status === 401) console.log('未授权,请重新登录'); return response; }, onError: (error) => console.error('网络错误:', error.message)});
配置后,所有请求都会自动携带 baseUrl、默认超时、默认 header,并通过拦截器统一处理 token 和错误。
前后对比
| |
|---|
| |
baseUrl | HttpConfig |
| 自动处理 string/ArrayBuffer/Object 并转为 JSON |
| |
| |
| 本库只负责小文件(≤10MB),大文件用后续 request.agent 方案 |
整体架构
调用层(业务代码) │导出层(index.ets) ← 仅暴露 httpClient 单例和类型 │接口层(HttpClientInterface) ← get/post/put/delete/upload + 拦截器管理 │构建器层(GetBuilder/PostBuilder/...) ← 链式参数、请求体 │核心层(HttpClient) ← 发送请求、执行拦截器、解析响应 │配置层(HttpConfig) ← baseUrl、默认超时、默认头
所有 Builder 类不对外导出,只能通过 httpClient 单例获取,保证封装性。
核心代码片段
1. 全局配置(单例)
// config/HttpConfig.etsexport class HttpConfig { private static instance: HttpConfig; public baseUrl = ''; public defaultTimeout = 30000; public defaultHeaders: Record<string, string> = {}; static getInstance(): HttpConfig { if (!HttpConfig.instance) { HttpConfig.instance = new HttpConfig(); } return HttpConfig.instance; } setBaseUrl(url: string): HttpConfig { this.baseUrl = url; return this; } setTimeout(ms: number): HttpConfig { this.defaultTimeout = ms; return this; } setDefaultHeader(key: string, value: string): HttpConfig { this.defaultHeaders[key] = value; return this; }}
2. 抽象构建器 BaseBuilder
所有构建器继承此类,统一处理 URL 参数、header、超时、进度、取消。
// builder/BaseBuilder.ets(关键方法)export abstract class BaseBuilder<T> { protected options: RequestOptions; private onProgressCallback?: (loaded: number, total: number) => void; private httpRequest?: http.HttpRequest; public param(key: string, value: string | number | boolean): BaseBuilder<T> { if (!this.options.params) this.options.params = new url.URLParams(); this.options.params.append(key, String(value)); return this; } public header(key: string, value: string): BaseBuilder<T> { if (!this.options.headers) this.options.headers = {}; this.options.headers[key] = value; return this; } public token(token: string): BaseBuilder<T> { this.options.token = token; return this; } public timeout(ms: number): BaseBuilder<T> { this.options.timeout = ms; return this; } public onProgress(cb: (loaded: number, total: number) => void): BaseBuilder<T> { this.onProgressCallback = cb; return this; } public cancel(): void { this.httpRequest?.destroy(); } public async execute<TRes = object>(): Promise<HttpResponse<TRes>> { return this.send<TRes>(); }}
3. 具体构建器(以 PostBuilder 为例)
// builder/PostBuilder.etsexport class PostBuilder extends BaseBuilder<PostBuilder> { constructor(url: string) { super(url, 'POST'); } public json(data: object): PostBuilder { this.options.body = JSON.stringify(data); this.header('Content-Type', 'application/json'); return this; } public form(data: Record<string, string>): PostBuilder { const parts = Object.entries(data).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`); this.options.body = parts.join('&'); this.header('Content-Type', 'application/x-www-form-urlencoded'); return this; } public binary(data: ArrayBuffer): PostBuilder { this.options.body = data; return this; }}
4. 拦截器接口
// interceptors/Interceptor.etsexport interface RequestInterceptor { onRequest: (options: RequestOptions) => RequestOptions | Promise<RequestOptions>;}export interface ResponseInterceptor { onResponse: (response: HttpResponse<object>) => HttpResponse<object> | Promise<HttpResponse<object>>; onError?: (error: Error) => void;}
5. 核心发送器 HttpClient(精简)
// core/HttpClient.etsexport class HttpClient implements HttpClientInterface { // ... 单例、get/post/put/delete/upload 方法 public async send<T>( options: RequestOptions, onProgress?: (loaded: number, total: number) => void, builder?: BaseBuilder<object> ): Promise<HttpResponse<T>> { // 1. 执行请求拦截器 const intercepted = await this.applyRequestInterceptors(options); // 2. 创建 httpRequest const httpRequest = http.createHttp(); builder?.setHttpRequest(httpRequest); // 3. 设置 header、超时、进度 // 4. 发起请求 const raw = await httpRequest.request(intercepted.url, reqOptions); // 5. 自动解析响应(string / ArrayBuffer / Object) let data: T; const result = raw.result; if (typeof result === 'string') { try { data = JSON.parse(result) as T; } catch { data = result as T; } } else if (result instanceof ArrayBuffer) { const str = new util.TextDecoder().decodeToString(new Uint8Array(result)); try { data = JSON.parse(str) as T; } catch { data = str as T; } } else { data = result as T; } // 6. 执行响应拦截器 // 7. 销毁请求 return { status: raw.responseCode, data }; }}
6. 导出层 index.ets
export { HttpConfig } from './src/main/ets/config/HttpConfig';export { httpClient } from './src/main/ets/core/HttpClient';export { RequestInterceptor, ResponseInterceptor } from './src/main/ets/interceptors/Interceptor';export type { HttpResponse, HttpMethod, RequestOptions } from './src/main/ets/model/types';export type { GetBuilder, PostBuilder, PutBuilder, DeleteBuilder, UploadBuilder } from './src/main/ets/builder';
使用示例
1. POST 登录请求同(一、先看效果)
2. GET 请求
interface User { id: number; name: string;}const users = await httpClient.get('/users') .param('page', 1) .param('size', 10) .execute<User[]>();
3. 文件上传(multipart)
测试请基于真实后台接口修改参数。
interface UploadResponse { url: string;}const fileBuffer = await readFileToArrayBuffer(selectedImageUri); // 自行实现const result = await httpClient.upload('/api/upload') .field('description', '我的照片') .file('file', fileBuffer, 'photo.jpg', 'image/jpeg') .timeout(60000) .onProgress((loaded, total) => console.log(`${loaded}/${total}`)) .execute<UploadResponse>();
4. 手动取消请求
const builder = httpClient.get('/long-report').param('year', 2025);const promise = builder.execute();builder.cancel();promise.catch(err => console.log('已取消:', err.message));
踩坑必读
1. ArkTS 不允许匿名对象字面量
所有请求体、参数对象、响应类型都必须先用 interface 定义,不能在泛型里写 { token: string }。
2. 链式调用顺序约束必须先调用请求体方法(.json()、.form()、.file()),再调用公共方法(.param()、.timeout())。因为公共方法返回基类 BaseBuilder<T>,会丢失子类特有方法。execute 始终在最后。
3. 当前不适合大文件下载http 模块会将整个响应加载到内存,超过 10MB 的文件会导致 OOM。大文件请等待后续 request.agent 方案。
4. 进度回调仅适用于小文件同样因为内存原因,不要用它监听几十 MB 的文件。
5. 响应数据类型自动解析库内部已经处理了 string/ArrayBuffer/Object 到 JSON 的转换,你只需要用泛型指定业务类型即可。
6.请求结束后自动销毁不需要手动 destroy(),取消时调用 builder.cancel()。
7.拦截器异步支持onRequest / onResponse 可以返回 Promise,方便做异步 token 刷新。
完整源码
所有代码已上传。
仓库地址:https://gitee.com/HarmonyOS-UI-Basics/harmony-os-ui-basics.git
仓库结构:
http/├── config/HttpConfig.ets├── model/types.ets├── interceptors/Interceptor.ets├── builder/BaseBuilder.ets├── builder/GetBuilder.ets├── builder/PostBuilder.ets├── builder/PutBuilder.ets├── builder/DeleteBuilder.ets├── builder/UploadBuilder.ets├── core/HttpClient.ets├── interface/HttpClientInterface.ets└── index.ets
下期预告
基于 request.agent 的大文件上传/下载组件(支持后台运行、断点续传、系统通知栏、任务分组、任务恢复)。
敬请期待!