当前位置:首页>鸿蒙APP>鸿蒙基础网络请求库封装实现:链式调用+拦截器+多格式请求

鸿蒙基础网络请求库封装实现:链式调用+拦截器+多格式请求

  • 2026-10-24 03:21:10
鸿蒙基础网络请求库封装实现:链式调用+拦截器+多格式请求

为啥要封装一个网络请求库

直接使用鸿蒙官方的 http 模块,每个请求都要 createHttp()、设 header、解析响应、destroy()……太繁琐了。

今天分享一套我自己封装的网络库,支持全局配置、链式 API、请求/响应拦截器、JSON/表单/二进制/文件上传,完整代码已整理上传。

httpDemo:https://gitee.com/HarmonyOS-UI-Basics/harmony-os-ui-basics.git。

先看效果

将网络请求模块化处理:我们需要准备啥?以登录接口为例:

1. 登录请求数据参数类
// 1. 定义请求体接口interface LoginRequest {  username: string;  password: string;}
2. 接收数据响应类
// 定义响应体接口 依据后端返回数据格式interface ApiResponse<T> {  code: number;  message: string;  data: T;}interface AuthTokenModel {  accessToken: string;  refreshToken: string;  userId: string;}
3. 构造请求参数
const request: LoginRequest = {   username: this.username.trim(),   password: encryptedPassword };
4. 发起登录请求
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 和错误。

前后对比

痛点
解决方案
重复创建/销毁 httpRequest
库内部自动管理生命周期
baseUrl
、超时散落各处
HttpConfig
 单例集中配置
响应数据需手动解析 JSON
自动处理 string/ArrayBuffer/Object 并转为 JSON
无法统一处理 token、401
请求/响应拦截器
进度回调、请求取消
原生支持
大文件内存溢出
本库只负责小文件(≤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 的大文件上传/下载组件(支持后台运行、断点续传、系统通知栏、任务分组、任务恢复)。

敬请期待!

最新文章

随机文章