HarmonyOS 网络层封装实践:打造 axios 风格请求 + 拦截器
·
HarmonyOS 网络层封装实践:打造 axios 风格请求 + 拦截器
前言
原生 @ohos.net.http 每次请求都要 createHttp()、request()、手动解析 response.result、再处理错误码,散落在各业务页面既重复又难统一(token 注入、错误统一弹窗、加载态、日志)。在 Web 端我们习惯 axios 的「实例 + 拦截器」模式,HarmonyOS 同样可以照此思路封装一层。本文给出一个可直接落地的 DTRequest 设计:请求/响应拦截器、自动 token 注入、统一错误归一化、TypeScript 泛型返回,并给出在 ArkTS 严格模式下的注意事项(禁用 any/unknown、避免展开运算符等)。
问题描述
未封装时常见的乱象:
- 每个页面都写
httpRequest.request(url, {...}),token 写在十几个地方,过期了改不全。 - 后端返回
{ code: 0, data: ... }与{ code: 200, data: ... }两种约定混用,前端判断各写各的。 - 弱网/超时只拿到底层
errcode,用户看到的是毫无意义的「错误 2300001」。 - ArkTS 严格模式下用
any接后端数据,编译报错或类型全失,还得写@ts-ignore(实际上 ArkTS 不支持)。
目标:把「请求前(加 header、加 loading)、请求后(解包、统一错误)、异常(归一化提示)」三件事全部收敛到一层。
细节解析
1. ArkTS 严格模式的约束
- 禁止使用
any/unknown:后端数据结构必须用interface显式声明,或用class+ 字段默认值。 - 禁止展开运算符
{...obj}与Object.assign的部分用法在严格场景受限,属性访问要静态可知。 - 网络层建议用泛型
<T>表达返回数据类型,编译期拿到类型安全。
2. 拦截器拆成三段
requestInterceptor:统一拼 baseURL、注入Authorization、加Content-Type、可选择展示全局 loading。responseInterceptor:把后端{ code, data, msg }归一为{ ok, data, message },非业务成功码抛统一错误。errorInterceptor:把底层errcode(如超时、无网络)映射成中文提示。
3. 单例 + 泛型
用 class DTRequest 单例持有配置(baseURL、超时、token 获取器),暴露 get<T> / post<T>。业务侧 const user = await api.get<User>('/user/info') 即可拿到强类型结果。
示例代码(可运行 ArkTS/ArkUI)
示例 1:统一返回结构与拦截器类型
// net/types.ets
export interface ApiResult<T> {
ok: boolean;
data: T;
message: string;
}
export interface RawResponse<T> {
code: number;
data: T;
msg: string;
}
export class ApiError extends Error {
code: number;
constructor(code: number, message: string) {
super(message);
this.code = code;
}
}
示例 2:DTRequest 核心封装
// net/DTRequest.ets
import { http } from '@kit.NetworkKit';
import { ApiError, ApiResult, RawResponse } from './types';
type TokenGetter = () => string;
class DTRequest {
private baseURL: string = 'https://api.example.com';
private timeout: number = 10000;
private getToken: TokenGetter = () => '';
setConfig(cfg: { baseURL: string; timeout?: number; getToken: TokenGetter }): void {
this.baseURL = cfg.baseURL;
if (cfg.timeout !== undefined) {
this.timeout = cfg.timeout;
}
this.getToken = cfg.getToken;
}
private async request<T>(method: 'GET' | 'POST', path: string, body?: object): Promise<T> {
const req = http.createHttp();
const url = this.baseURL + path;
const headers: Record<string, string> = {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.getToken()
};
const options: http.HttpRequestOptions = {
method: method,
header: headers,
timeout: this.timeout,
extraData: body ? JSON.stringify(body) : undefined
};
try {
const resp = await req.request(url, options);
if (resp.responseCode !== 200) {
throw new ApiError(resp.responseCode, `HTTP ${resp.responseCode}`);
}
const raw = JSON.parse(resp.result as string) as RawResponse<T>;
if (raw.code !== 0) {
throw new ApiError(raw.code, raw.msg || '业务错误');
}
return raw.data;
} catch (e) {
if (e instanceof ApiError) {
throw e;
}
throw new ApiError(-1, '网络异常,请稍后重试');
} finally {
req.destroy();
}
}
get<T>(path: string): Promise<T> {
return this.request<T>('GET', path);
}
post<T>(path: string, body: object): Promise<T> {
return this.request<T>('POST', path, body);
}
}
export const api = new DTRequest();
示例 3:业务侧使用(强类型)
// model/User.ets
export interface User {
id: number;
name: string;
avatar: string;
}
// 初始化(应用启动时一次)
api.setConfig({
baseURL: 'https://api.example.com',
getToken: () => AppStorage.get<string>('token') ?? ''
});
// 页面调用
@Entry
@ComponentV2
struct Profile {
@Local user: User | null = null;
@Local err: string = '';
aboutToAppear() {
api.get<User>('/user/info')
.then((u) => { this.user = u; })
.catch((e: ApiError) => { this.err = e.message; });
}
build() {
Column() {
if (this.user) {
Text(`欢迎,${this.user.name}`)
} else if (this.err) {
Text(this.err).fontColor(Color.Red)
}
}.padding(20)
}
}
总结
- 统一入口:所有请求走
api.get/post,baseURL、超时、token 只在一处配置。 - 拦截器思维:请求前加 header、响应后解包
code、异常时归一化中文提示,业务页面只关心data。 - 泛型保类型:
api.get<User>()让后端结构在编译期就可见,避免any失控(ArkTS 也禁止any)。 - 严格模式纪律:显式
interface、不用any/unknown、避免展开运算符;字段访问静态可知,才能过编译。 - 资源释放:每次
createHttp()后务必destroy(),否则连接泄漏。
一套干净的 DTRequest 能让后续 20 个接口的开发从「复制 30 行样板」变成「一行 api.get<T>()」,同时把 token 失效、弱网提示等全局问题一次性解决。
更多推荐



所有评论(0)