文章目录
-
- 前言
- 基础请求长啥样
- [第一层:统一 baseURL 和超时](#第一层:统一 baseURL 和超时)
- [第二层:请求拦截,自动带 token](#第二层:请求拦截,自动带 token)
- 第三层:响应拦截,统一错误处理
- 业务里怎么用
- 常见误区(小白必踩)
- [完整可运行示例:HTTP 请求封装](#完整可运行示例:HTTP 请求封装)
- 写在最后
前言
几乎每个 app 都要联网。但如果你在每个页面里都写一遍 httpRequest.request(...)、都手写错误处理、都手动拼 token,代码会又臭又长,改个 baseURL 要改二十处。
正确做法:封装一个统一的网络层。这篇文章我带你写一个带"请求拦截(自动加 token)、响应拦截(统一错误处理)"的 HTTP 封装,以后业务里一行调用就完事。

基础请求长啥样
HarmonyOS 推荐用 rcp(Remote Communication Kit)发请求,比老 http 模块更现代。
代码实现
typescript
import { rcp } from '@kit.RemoteCommunicationKit'
async function getUsers(): Promise<User[]> {
const session = rcp.createSession()
const resp = await session.get('https://api.example.com/users')
return resp.body as User[]
}
这是最原始写法,问题:没有 baseURL、没有 token、没有统一错误提示。下面我们一层层包。
第一层:统一 baseURL 和超时
typescript
const BASE_URL = 'https://api.example.com'
class Http {
private session: rcp.Session
constructor() {
this.session = rcp.createSession({
baseAddress: BASE_URL,
headers: { 'Content-Type': 'application/json' }
})
}
async get<T>(path: string): Promise<T> {
const resp = await this.session.get(path)
return resp.body as T
}
}
要点:
baseAddress设一次 baseURL,后面只传/users这种相对路径。- 用泛型
<T>让调用方拿到类型安全的返回,不用自己 cast。
第二层:请求拦截,自动带 token
真实接口基本都要登录态。与其每个请求手写 headers.token,不如在封装里统一加:
核心代码
typescript
async get<T>(path: string): Promise<T> {
const token = AppStorage.get<string>('token') ?? ''
const resp = await this.session.get(path, {
headers: { 'Authorization': 'Bearer ' + token } // 自动注入
})
return this.handle(resp) // 交给响应处理
}
AppStorage.get('token') 是全局存的登录 token,每次请求自动塞进 header。业务方完全不用管 token 从哪来。

第三层:响应拦截,统一错误处理
不同接口返回结构不同,但"成功/失败"的判断逻辑是共通的。集中处理:
完整示例
typescript
private handle(resp: rcp.Response): any {
const code = resp.statusCode
if (code >= 200 && code < 300) {
return resp.body // 成功,返回数据
}
if (code === 401) {
// token 过期,跳登录
throw new Error('登录已过期,请重新登录')
}
throw new Error('请求失败:' + code)
}
- 2xx 才视为成功,直接给业务数据。
- 401 统一跳登录,不用每个页面判断。
- 其他错误抛异常,调用方
try/catch即可。
业务里怎么用
typescript
const http = new Http()
async loadData() {
try {
this.users = await http.get<User[]>('/users')
} catch (e) {
promptAction.showToast({ message: (e as Error).message })
}
}
业务代码干净得只剩"取数据 + 出错提示"两件事。这就是封装的价值:重复的逻辑沉到底层,业务只关心业务。
常见误区(小白必踩)
| 误区 | 说明 |
|---|---|
| token 写死在业务代码里 | 每处手写 headers.token,改一次要改二十处。交给封装层统一注入。 |
| 不统一错误处理 | 每个页面自己 if (code===401),重复又易漏。集中到响应拦截里。 |
忘了 await |
请求是异步的,不 await 拿到的是 Promise 不是数据,bug 极难查。 |
下面这段代码可以直接复制到 DevEco Studio 里运行。建议你边读边敲,改一改文末「动手改一改」里的参数,亲眼看看效果。
完整可运行示例:HTTP 请求封装
把 http.createHttp() 包一层,统一处理 URL、错误、返回 JSON。
代码解析
typescript
// 一个最小可用的请求封装
import { http } from '@kit.NetworkKit'
async function getJSON<T>(url: string): Promise<T> {
const req = http.createHttp()
try {
const resp = await req.request(url, { method: http.RequestMethod.GET })
if (resp.responseCode !== 200) {
throw new Error('HTTP ' + resp.responseCode)
}
return JSON.parse(resp.result as string) as T
} finally {
req.destroy() // 用完释放,避免句柄泄漏
}
}
// 使用
@Entry
@Component
struct Page {
@State tip: string = '加载中...'
aboutToAppear() {
getJSON<{ title: string }>('https://example.com/api')
.then((d) => { this.tip = d.title })
.catch((e) => { this.tip = '出错:' + e.message })
}
build() { Column(){ Text(this.tip) }.padding(20) }
}
你会看到什么 :进入页面自动发请求,tip 从「加载中」变成接口返回的数据;若网络异常,tip 显示具体错误。
动手改一改:
- 给
getJSON增加第二个参数body,支持 POST。 - 在
finally前加console.info打印耗时,做基础性能观测。 - 把
tip初始值改成空,加一个if (this.tip)条件渲染骨架屏。
写在最后
网络封装这套"三层结构"------baseURL、请求拦截加 token、响应拦截统错------是我每个项目的标配。它前期多花半小时,后期省下的是成倍的维护成本。
你现在就可以把项目里散落的 httpRequest 收拢到一个 Http 类里。等哪天要换域名、加签名、统一埋点,你会感谢今天做了封装的自己。