鸿蒙 韶非 UI 系列:HTTP 数据请求 @ohos.net.http,告别前端 fetch,鸿蒙网络栈入门第一篇
写在前面
如果你写过 ArkUI 之外的任何鸿蒙应用能力,大概率第一个想做的就是「调接口」:
后端给你一个 RESTful 接口
https://api.example.com/users,前端 React 用fetch(url)一行搞定。鸿蒙 ArkTS 里你写fetch——编译报错「找不到 fetch」。你查文档发现鸿蒙不是浏览器运行环境,没有fetch/XHR/axios,要调接口得用专门的网络栈@ohos.net.http。
你点进去发现http.createHttp()+HttpRequest.request()+HttpRequestOptions一堆配置项 +HttpResponse字段一堆 + 异步两种姿势 + 错误码和状态码要分清——比fetch复杂十倍,一脸懵。
这是「前端思维」和「鸿蒙原生网络栈」的分水岭。鸿蒙给的网络栈答案是@ohos.net.http——createHttp()创建请求器、request()发 GET/POST、HttpRequestOptions配头/超时/协议/数据类型、HttpResponse拿响应码+头+体。
本文就用一个真机可跑的「调 httpbin.org 公开测试接口」demo,把@ohos.net.http从「听名字一脸懵」讲到「下个项目直接抄」。代码托管在 AtomGit,文末有链接,真机实拍截图作证。这是非 UI 系列第一篇,ArkUI 十篇凑齐后转网络栈入门。
适合人群:写过 ArkUI、第一次调鸿蒙网络接口的同学。
不适合人群:还在学@State的同学——出门左转看我的入门篇。
一、先讲清楚:@ohos.net.http到底是啥
一句话:@ohos.net.http是鸿蒙原生 HTTP 网络栈,管「发请求、收响应、控超时、销毁」全流程。
你之前写前端用fetch/axios是浏览器宿主 API——鸿蒙不是浏览器环境,没有这些。@ohos.net.http是鸿蒙专门给应用调网络接口的原生模块,能力对标fetch但更精细可控。
核心 API 一览:
| API | 作用 | 一句话理解 |
|---|---|---|
http.createHttp() | 创建请求器 | 「造一个 HttpRequest 实例,后面用它发请求」 |
httpReq.request(url, options) | 发请求 | 「GET/POST/PUT/DELETE 都通过它」 |
HttpRequestOptions | 配置项 | 「method/header/超时/协议/数据类型」 |
HttpResponse | 响应 | 「responseCode + header + result」 |
httpReq.destroy() | 销毁请求器 | 「不用了销毁,防内存漏」 |
记住这五个,往下看。
二、动手:一个调 httpbin.org 的 GET/POST demo
2.1 import + 创建请求器
importhttpfrom'@ohos.net.http'@Entry@Componentstruct Index{privatehttpReq:http.HttpRequest=http.createHttp()@StatestatusText:string='尚未请求'@StaterespBody:string=''@StateisLoading:boolean=false// ...}三个细节:
import http from '@ohos.net.http'——http是 namespace,所有 API 都挂在它下面(http.createHttp()/http.RequestMethod.GET/http.HttpDataType.STRING等)http.HttpRequest是 interface(不是 class),实例由http.createHttp()造,不能new http.HttpRequest()- 请求器是有状态资源,不用了要
destroy()——比fetch多一步销毁管理
2.2 GET 请求:Promise 异步姿势
asyncdoGet():Promise<void>{this.isLoading=truethis.statusText='GET 请求中...'try{constresp:http.HttpResponse=awaitthis.httpReq.request('https://httpbin.org/get',{method:http.RequestMethod.GET,header:{'X-Custom-Header':'鸿蒙ArkTS-HTTP'},connectTimeout:8000,readTimeout:8000,usingProtocol:http.HttpProtocol.HTTP1_1,expectDataType:http.HttpDataType.STRING,})this.statusText=`GET 完成,状态码${resp.responseCode}`this.respBody=`URL: https://httpbin.org/get\n\n${JSON.stringify(JSON.parse(resp.resultasstring),null,2)}`}catch(e){this.statusText=`GET 失败:${e.message}`this.respBody=''}finally{this.isLoading=false}}httpReq.request(url, options)返回Promise<HttpResponse>,用await等响应。HttpRequestOptions常用字段:
| 字段 | 作用 | 默认 |
|---|---|---|
method | RequestMethod 枚举 | GET |
header | 请求头对象 | {} |
extraData | 请求体(POST 用) | - |
connectTimeout | 连接超时(ms) | 60000 |
readTimeout | 读取超时(ms) | 60000 |
usingProtocol | HTTP 协议 | HTTP1_1 |
expectDataType | 期望响应数据类型 | STRING |
2.3 POST 请求:传 JSON 体
asyncdoPost():Promise<void>{this.isLoading=truethis.statusText='POST 请求中...'try{constbody:Record<string,string>={'user':'鸿蒙开发者','action':'login','ts':`${Date.now()}`}constresp:http.HttpResponse=awaitthis.httpReq.request('https://httpbin.org/post',{method:http.RequestMethod.POST,header:{'Content-Type':'application/json','X-Request-From':'ArkTS'},extraData:JSON.stringify(body),connectTimeout:8000,readTimeout:8000,usingProtocol:http.HttpProtocol.HTTP1_1,expectDataType:http.HttpDataType.STRING,})this.statusText=`POST 完成,状态码${resp.responseCode}`this.respBody=`POST 体 →\n${JSON.stringify(body,null,2)}\n\n响应 →\n${JSON.stringify(JSON.parse(resp.resultasstring),null,2)}`}catch(e){this.statusText=`POST 失败:${e.message}`this.respBody=''}finally{this.isLoading=false}}POST 比 GET 多两步:
header加Content-Type: application/json——告诉服务端体是 JSONextraData: JSON.stringify(body)——把对象序列化成 JSON 字符串传
ArkTS 强约束:
body不能是裸对象字面量(arkts-no-untyped-obj-literals),要先声明Record<string, string>再赋值。
2.4 响应处理:HttpResponse三字段
resp.responseCode// 服务端状态码(200/404/500)resp.header// 响应头对象resp.result// 响应体(string/Object/ArrayBuffer,取决于 expectDataType)resp.resultType// 实际响应类型枚举新手最容易混淆的是result:它的类型由expectDataType决定(STRING→ string,ARRAY_BUFFER→ 二进制,默认Object→ 已 deprecated)。我设了STRING,所以resp.result as string断言成字符串用。
2.5 销毁请求器:生命周期收尾
aboutToDisappear():void{// 销毁 HttpRequest 释放资源(不收易内存漏)this.httpReq.destroy()}http.createHttp()造的请求器是有状态资源——底层持有 socket、缓冲区、连接池。组件销毁时不调destroy(),这些资源不会自动释放,长时间运行易内存漏。这是fetch没有的鸿蒙专属坑。
2.6 UI 反馈区
build(){Column({space:14}){Text('HTTP 数据请求 Demo(@ohos.net.http)').fontSize(22).fontWeight(FontWeight.Bold).margin({top:16})// 状态区:isLoading 显示加载中,完成后显示状态码Column({space:8}){Text('状态').fontSize(14).fontColor('#007DFF')Text(this.statusText).fontSize(16).fontWeight(FontWeight.Bold).fontColor('#222').padding(10).backgroundColor('#F0F0F0').borderRadius(6).width('100%')}.width('100%').padding(14).backgroundColor('#fff').borderRadius(10)// 按钮区:GET / POST / 清空Row({space:10}){Button('GET 请求').backgroundColor('#007DFF').fontColor('#fff').height(40).layoutWeight(1).enabled(!this.isLoading).onClick(()=>{this.doGet()})Button('POST 请求').backgroundColor('#FF4D4F').fontColor('#fff').height(40).layoutWeight(1).enabled(!this.isLoading).onClick(()=>{this.doPost()})Button('清空').backgroundColor('#eee').fontColor('#333').height(40).onClick(()=>{this.statusText='尚未请求';this.respBody=''})}.width('100%')// 响应体显示区:滚得动(长 JSON 体可滚)Column({space:8}){Text('响应体').fontSize(14).fontColor('#007DFF')Scroll(){Text(this.respBody||'(点按钮发请求后,这里显示响应体)').fontSize(12).fontColor('#555').fontFamily('sans-serif').padding(10).backgroundColor('#FAFAFA').borderRadius(6)}.height('58%').width('100%').scrollBar(BarState.Auto)}.width('100%').padding(14).backgroundColor('#fff').borderRadius(10).layoutWeight(1)}.padding(16).backgroundColor('#F5F6F8').height('100%').width('100%')}三、真机实拍:调 httpbin.org 真发出去并真有响应
我把这个 demo 装到真机上跑(鸿蒙 6.1.1.125, API 24),调 httpbin.org 公开测试接口,下面两张都是真机实拍,没有任何 P 图。
初始态:状态「尚未请求」+ GET/POST/清空三按钮 + 响应体区提示:
点 GET 按钮发请求后的响应态:状态「GET 完成,状态码 200」+ 响应体区显示 httpbin.org 返回的 JSON:
重点看第二张:状态显示「GET 完成,状态码 200」——真发出去并真有响应。响应体区显示 httpbin.org 返回的 JSON,里面能看到我设的
X-Custom-Header: 鸿蒙ArkTS-HTTP被服务端 echo 回来——这就是header字段生效的证明。POST 同理,体被 echo 回来证明extraData生效。
四、@ohos.net.httpvs 前端fetch:啥差异
新手最容易纠结的问题:既然前端fetch那么简洁,鸿蒙为啥要造一套独立的?
| 维度 | 前端fetch | @ohos.net.http |
|---|---|---|
| 运行环境 | 浏览器宿主 | 鸿蒙原生运行环境 |
| 创建 | 全局fetch()调即用 | http.createHttp()造实例 |
| 销毁 | 浏览器自管 | 必须destroy()防漏 |
| 超时配置 | AbortController+ signal | connectTimeout/readTimeout字段 |
| 请求头 | headers字段 | header字段(单数) |
| 响应类型 | response.text()/json()/arrayBuffer()链式 | expectDataType预设 +resp.result直取 |
| 错误姿势 | Promise+try/catch | 同Promise+try/catch |
一句话决策:鸿蒙应用调接口必须用@ohos.net.http,不能用fetch(不存在)。鸿蒙不是浏览器,这套原生栈更精细可控,代价是销毁管理多一步。
五、常见坑(都是血泪)
| 坑 | 症状 | 解法 |
|---|---|---|
用fetch/axios | 编译报错「找不到 fetch」 | 鸿蒙用@ohos.net.http,没有浏览器宿主 API |
HttpRequest用new | 编译报错 | HttpRequest是 interface,用http.createHttp()造实例 |
HttpDataType.STRING_TYPE | 编译报错「Property 不存在」 | 真常量名是STRING(不带_TYPE后缀) |
不调destroy() | 长跑内存漏 | 组件销毁aboutToDisappear调httpReq.destroy() |
裸对象字面量传extraData | 编译报错arkts-no-untyped-obj-literals | 先声明Record<string, X>变量再JSON.stringify |
resp.result类型不明 | 运行时报错 | 设expectDataType: STRING+as string断言 |
| 忘 INTERNET 权限 | 请求发不出去 | module.json5 配ohos.permission.INTERNET,装机首次授权 |
| 错误码和状态码混 | 业务判断错 | e.message是错误码(网络层),resp.responseCode是状态码(应用层) |
六、INTERNET 权限:鸿蒙应用调网的硬门槛
鸿蒙应用调网络接口,必须先在module.json5声明ohos.permission.INTERNET权限,否则请求发不出去(编译会 WARN 提示)。
{"module":{"abilities":[{"name":"EntryAbility","srcEntry":"./ets/entryability/EntryAbility.ets","requestPermissions":[{"name":"ohos.permission.INTERNET"}]}]}}装机首次跑时,鸿蒙会弹权限询问框,用户同意后才能调网。这是鸿蒙安全模型的硬门槛——比浏览器fetch多一层权限管理。
七、完整代码仓库
本文所有代码都已托管到AtomGit,欢迎 clone、提 issue、点 star:
🔗仓库地址:https://atomgit.com/JaneConan/arkui-http-request
仓库包含:
- 完整的「调 httpbin.org GET/POST」demo 工程
Index.ets主页面(http.createHttp+request+ UI 反馈 + 销毁)doGet/doPost两个异步姿势示范HttpRequestOptions常用字段配置 +HttpResponse三字段处理- 可直接用 DevEco Studio 打开运行(需真机有网 + INTERNET 权限授权)
八、下一步该学什么?
跑通这个 demo 之后,你的鸿蒙网络栈就入门了。这是非 UI 系列第一篇,后续按这个顺序往下:
- 文件 IO
@ohos.file.fs(下一篇):沙箱读写 + 元信息查,告别裸 PersistentStorage 存大对象 - 能力调用
@ohos.ability:调起相机/相册/定位等系统能力,应用集成系统服务 - 后台任务
@ohos.backgroundTask:长时后台跑、保活、调度,真机部署必学 - 数据持久化
@ohos.data.relationalStore:鸿蒙 SQLite 封装,结构化数据存取 - WebSocket
@ohos.net.webSocket:长连接、推送、实时通讯,聊天应用必学
写在最后
@ohos.net.http的本质,是**「鸿蒙原生网络栈」**——不是浏览器宿主 API,是鸿蒙专门给应用调接口的原生模块,能力对标fetch但更精细可控。代价是销毁管理多一步、权限管理多一层。
一旦你开始用鸿蒙原生思维写网络调,你会发现大部分「调接口收响应」的需求,都是createHttp+request+destroy三步的自然结果。代码量比fetch多三行,可控性高九成。
代码已经给你了,仓库链接在上面。现在关掉这篇文章,打开 DevEco Studio,把 demo 跑起来,亲手点 GET 感受下真发请求真收响应。
跑通了,回来评论区打个「1」,我看看有多少人真的动手了。🚀
作者:JaneConan
仓库:https://atomgit.com/JaneConan/arkui-http-request
协议:Apache-2.0,随便用,别告我