第10篇:网络请求——获取远程数据 鸿蒙中文编程
文章摘要 本文是一篇关于网络请求和数据获取的教程,主要包含以下内容: HTTP请求基础:介绍了HTTP协议的基本概念,包括GET/POST请求方法和常见状态码(200、404、500等)。 实践操作:详细讲解了如何使用HTTP模块发送GET和POST请求,包括创建请求对象、处理响应数据和错误处理。 异步编程:解释了async/await和Promise的使用方法,说明如何处理网络请求的异步特性。
第10篇:网络请求——获取远程数据
本课目标:掌握HTTP网络请求,能从服务器获取数据
**作者:**中文编程倡导者—— 李金雨
预计课时:3课时(135分钟)
难度等级:⭐⭐⭐⭐(高级)
一、开篇引入
1.1 为什么需要网络请求?
前面的课程,我们的数据都是写死在代码里的:
@State 商品列表: object[] = [
{ 名称: "手机", 价格: 2999 },
{ 名称: "电脑", 价格: 5999 }
]
但真实的应用需要从服务器获取数据:
- 🌤️ 天气应用需要获取实时天气
- 📰 新闻应用需要获取最新新闻
- 🛒 电商应用需要获取商品信息
- 💬 聊天应用需要获取消息记录
1.2 什么是HTTP请求?
HTTP(超文本传输协议)是浏览器和服务器之间通信的"语言"。
就像你去餐厅点菜:
你(客户端) 服务员(服务器)
│ │
│ "我要一份炒饭"(请求) │
│ ─────────────────────────> │
│ │
│ "好的,这是您的炒饭"(响应) │
│ <───────────────────────── │
1.3 本课目标
今天我们要学习:
- 什么是HTTP请求
- 怎么发送GET请求
- 怎么发送POST请求
- 怎么处理异步操作
- 实战:天气查询、新闻阅读
1.4 预期成果
完成本课后,你能做出这样的应用:
天气查询: 新闻阅读:
┌─────────────┐ ┌─────────────┐
│ 🌤️ 天气查询 │ │ 📰 今日新闻 │
├─────────────┤ ├─────────────┤
│ │ │ │
│ 北京 │ │ 🔥 新闻标题1 │
│ ⛅ 多云 │ │ 新闻摘要... │
│ 25°C │ │ │
│ │ │ 📰 新闻标题2 │
│ 湿度: 60% │ │ 新闻摘要... │
│ 风速: 3级 │ │ │
│ │ │ 📰 新闻标题3 │
│ [刷新数据] │ │ 新闻摘要... │
│ │ │ │
└─────────────┘ └─────────────┘
二、概念讲解
2.1 HTTP基础
请求方法
| 方法 | 用途 | 例子 |
|---|---|---|
| GET | 获取数据 | 获取天气、获取新闻列表 |
| POST | 提交数据 | 登录、提交表单、发表评论 |
| PUT | 更新数据 | 修改用户信息 |
| DELETE | 删除数据 | 删除文章 |
状态码
服务器返回的"结果代码":
| 状态码 | 含义 | 说明 |
|---|---|---|
| 200 | 成功 | 请求成功,返回了数据 |
| 404 | 未找到 | 请求的资源不存在 |
| 500 | 服务器错误 | 服务器出问题了 |
| 403 | 禁止访问 | 没有权限 |
2.2 导入http模块
import http from '@ohos.net.http'
2.3 发送GET请求
基本流程
import http from '@ohos.net.http'
async 获取数据() {
// 1. 创建HTTP请求对象
let http请求 = http.createHttp()
// 2. 发送请求
let 响应 = await http请求.request(
"https://api.example.com/data", // 请求地址
{ method: http.RequestMethod.GET } // 请求方法
)
// 3. 处理响应
if (响应.responseCode == 200) {
let 数据 = JSON.parse(响应.result.toString())
console.log("获取成功:" + JSON.stringify(数据))
} else {
console.log("请求失败:" + 响应.responseCode)
}
}
完整例子
// 完整可运行代码,复制到 Index.ets 即可运行
import http from '@ohos.net.http'
@Entry
@Component
struct Index {
@State 数据: string = ""
@State 加载中: boolean = false
build() {
Column({ space: 20 }) {
Button("获取数据")
.onClick(() => this.获取数据())
if (this.加载中) {
Text("加载中...")
} else {
Text(this.数据 || "点击按钮获取数据")
}
}
}
async 获取数据() {
this.加载中 = true
try {
let http请求 = http.createHttp()
let 响应 = await http请求.request(
"https://api.example.com/data",
{ method: http.RequestMethod.GET }
)
if (响应.responseCode == 200) {
this.数据 = 响应.result.toString()
} else {
this.数据 = "请求失败:" + 响应.responseCode
}
} catch (错误) {
this.数据 = "网络错误:" + 错误.message
} finally {
this.加载中 = false
}
}
}
2.4 发送POST请求
提交表单数据
async 提交数据() {
let http请求 = http.createHttp()
let 响应 = await http请求.request(
"https://api.example.com/login",
{
method: http.RequestMethod.POST,
header: {
'Content-Type': 'application/json'
},
extraData: JSON.stringify({
用户名: "张三",
密码: "123456"
})
}
)
if (响应.responseCode == 200) {
let 结果 = JSON.parse(响应.result.toString())
console.log("登录成功")
}
}
2.5 异步操作
什么是异步?
网络请求需要时间(几秒甚至更久),如果等待时界面卡住,用户体验很差。
异步就是:
- 发送请求后,不等结果,继续执行其他代码
- 结果回来后,再处理
async/await
// async 标记这是一个异步函数
async 获取数据() {
// await 等待请求完成
let 响应 = await http请求.request(...)
// 请求完成后才执行这里
console.log("请求完成")
}
Promise
// 不用async/await的写法
获取数据() {
let http请求 = http.createHttp()
http请求.request("https://api.example.com/data")
.then((响应) => {
// 成功时执行
console.log("成功:" + 响应.result)
})
.catch((错误) => {
// 失败时执行
console.log("失败:" + 错误.message)
})
}
2.6 错误处理
async 获取数据() {
try {
// 可能出错的代码
let 响应 = await http请求.request(...)
if (响应.responseCode == 200) {
// 处理成功
} else {
// 处理HTTP错误
throw new Error("HTTP错误:" + 响应.responseCode)
}
} catch (错误) {
// 捕获所有错误
console.error("出错了:" + 错误.message)
// 给用户友好的提示
this.错误信息 = "网络连接失败,请检查网络"
} finally {
// 无论成功失败都执行
this.加载中 = false
}
}
三、动手实践
3.1 基础练习:天气查询应用
由于无法访问真实API,我们用模拟数据演示:
// 完整可运行代码,复制到 Index.ets 即可运行
import http from '@ohos.net.http'
@Entry
@Component
struct Index {
@State 城市: string = "北京"
@State 天气数据: object = null
@State 加载中: boolean = false
@State 错误信息: string = ""
// 模拟天气数据(实际应用中使用真实API)
模拟数据: object = {
城市: "北京",
天气: "多云",
温度: 25,
湿度: "60%",
风速: "3级",
图标: "⛅"
}
build() {
Column({ space: 20 }) {
// 标题
Text("🌤️ 天气查询")
.fontSize(28)
.fontWeight(FontWeight.Bold)
.margin(20)
// 城市选择
Row({ space: 10 }) {
Text("选择城市:")
.fontSize(16)
Select([
{ value: "北京" },
{ value: "上海" },
{ value: "广州" },
{ value: "深圳" }
])
.selected(this.城市)
.onSelect((索引: number, 值: string) => {
this.城市 = 值
})
}
// 查询按钮
Button("查询天气", { type: ButtonType.Capsule })
.width('80%')
.height(50)
.backgroundColor('#2196F3')
.enabled(!this.加载中)
.onClick(() => {
this.查询天气()
})
// 加载中
if (this.加载中) {
Column({ space: 10 }) {
LoadingProgress()
.width(50)
.height(50)
.color('#2196F3')
Text("正在查询...")
.fontSize(14)
.fontColor('#999999')
}
.margin(30)
}
// 错误信息
if (this.错误信息 != "") {
Text(this.错误信息)
.fontSize(14)
.fontColor('#F44336')
.margin(20)
}
// 天气信息
if (this.天气数据 != null && !this.加载中) {
this.天气卡片()
}
}
.width('100%')
.height('100%')
.padding(20)
.backgroundColor('#F5F5F5')
}
@Builder
天气卡片() {
Column({ space: 15 }) {
Text(this.天气数据?.['图标'])
.fontSize(80)
Text(this.天气数据?.['城市'])
.fontSize(24)
.fontWeight(FontWeight.Bold)
Text(`${this.天气数据?.['温度']}°C`)
.fontSize(48)
.fontWeight(FontWeight.Bold)
.fontColor('#2196F3')
Text(this.天气数据?.['天气'])
.fontSize(20)
.fontColor('#666666')
Row({ space: 30 }) {
Column({ space: 5 }) {
Text("湿度")
.fontSize(12)
.fontColor('#999999')
Text(this.天气数据?.['湿度'])
.fontSize(16)
}
Column({ space: 5 }) {
Text("风速")
.fontSize(12)
.fontColor('#999999')
Text(this.天气数据?.['风速'])
.fontSize(16)
}
}
.margin({ top: 10 })
}
.width('100%')
.padding(30)
.backgroundColor('#FFFFFF')
.borderRadius(16)
.shadow({ radius: 10, color: '#10000000' })
.margin({ top: 20 })
}
async 查询天气() {
this.加载中 = true
this.错误信息 = ""
this.天气数据 = null
try {
// 模拟网络请求延迟
await this.延迟(1000)
// 实际应用中使用真实API:
// let http请求 = http.createHttp()
// let 响应 = await http请求.request(
// `https://api.weather.com/v1/current?city=${this.城市}`,
// { method: http.RequestMethod.GET }
// )
// 模拟数据(替换为真实API响应)
this.模拟数据.城市 = this.城市
this.天气数据 = this.模拟数据
} catch (错误) {
this.错误信息 = "查询失败:" + 错误.message
} finally {
this.加载中 = false
}
}
延迟(毫秒: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, 毫秒))
}
}
3.2 进阶练习:新闻列表应用
import http from '@ohos.net.http'
interface 新闻数据 {
id: string
标题: string
摘要: string
时间: string
图片?: string
热度?: number
}
// 完整可运行代码,复制到 Index.ets 即可运行
@Entry
@Component
struct Index {
@State 新闻列表: 新闻数据[] = []
@State 加载中: boolean = false
@State 刷新中: boolean = false
@State 错误信息: string = ""
@State 当前分类: string = "推荐"
build() {
Column() {
// 标题栏
Row() {
Text('📰 今日新闻')
.fontSize(22)
.fontWeight(FontWeight.Bold)
Button(this.加载中 ? '加载中' : '刷新')
.fontSize(14)
.enabled(!this.加载中)
.backgroundColor('transparent')
.fontColor('#2196F3')
.onClick(() => this.加载新闻())
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.padding(20)
.backgroundColor('#FFFFFF')
// 分类标签
Scroll() {
Row({ space: 10 }) {
ForEach(['推荐', '国内', '国际', '科技', '体育', '娱乐'], (分类: string) => {
Text(分类)
.fontSize(14)
.fontColor(this.当前分类 == 分类 ? '#FFFFFF' : '#666666')
.padding({ left: 15, right: 15, top: 6, bottom: 6 })
.backgroundColor(this.当前分类 == 分类 ? '#2196F3' : '#F5F5F5')
.borderRadius(15)
.onClick(() => {
this.当前分类 = 分类
this.加载新闻()
})
})
}
.padding(15)
}
.scrollBar(BarState.Off)
.scrollable(ScrollDirection.Horizontal)
// 内容区域
Stack() {
if (this.加载中 && this.新闻列表.length == 0) {
// 首次加载
Column({ space: 10 }) {
LoadingProgress()
.width(50)
.height(50)
Text('加载中...')
.fontColor('#999999')
}
} else if (this.错误信息 != '') {
// 错误状态
Column({ space: 10 }) {
Text('❌')
.fontSize(50)
Text(this.错误信息)
.fontColor('#F44336')
Button('重试')
.onClick(() => this.加载新闻())
}
} else if (this.新闻列表.length == 0) {
// 空数据
Column({ space: 10 }) {
Text('📭')
.fontSize(60)
Text('暂无新闻')
.fontColor('#999999')
}
} else {
// 新闻列表
this.新闻列表组件()
}
}
.width('100%')
.layoutWeight(1)
}
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
@Builder
新闻列表组件() {
List({ space: 10 }) {
ForEach(this.新闻列表, (新闻: 新闻数据) => {
ListItem() {
Column({ space: 10 }) {
Row({ space: 10 }) {
// 标题和摘要
Column({ space: 8 }) {
Text(新闻.标题)
.fontSize(17)
.fontWeight(FontWeight.Medium)
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text(新闻.摘要)
.fontSize(14)
.fontColor('#666666')
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Row({ space: 10 }) {
if (新闻.热度 && 新闻.热度 > 1000) {
Text('🔥 热门')
.fontSize(11)
.fontColor('#FFFFFF')
.padding({ left: 5, right: 5, top: 2, bottom: 2 })
.backgroundColor('#FF5722')
.borderRadius(3)
}
Text(新闻.时间)
.fontSize(12)
.fontColor('#999999')
}
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
// 图片
if (新闻.图片) {
Text(新闻.图片)
.fontSize(50)
.width(80)
.height(80)
.backgroundColor('#E3F2FD')
.textAlign(TextAlign.Center)
}
}
.width('100%')
}
.width('100%')
.padding(15)
.backgroundColor('#FFFFFF')
.borderRadius(10)
}
})
}
.padding(15)
.refreshing(this.刷新中)
.onRefresh(() => {
this.刷新中 = true
this.加载新闻()
})
}
aboutToAppear() {
this.加载新闻()
}
async 加载新闻() {
if (!this.刷新中) {
this.加载中 = true
}
this.错误信息 = ''
try {
// 模拟网络延迟
await this.延迟(800)
// 模拟数据(实际应用替换为真实API)
this.新闻列表 = this.生成模拟新闻()
} catch (错误) {
this.错误信息 = '加载失败,请检查网络'
} finally {
this.加载中 = false
this.刷新中 = false
}
}
生成模拟新闻(): 新闻数据[] {
let 基础数据 = [
{ 标题: '科技创新推动经济发展', 摘要: '最新报告显示,科技产业已成为经济增长的主要动力...', 热度: 5000 },
{ 标题: '教育改革新政策发布', 摘要: '教育部发布最新政策,将全面推进素质教育...', 热度: 3000 },
{ 标题: '环保行动取得显著成效', 摘要: '经过持续努力,城市空气质量明显改善...', 热度: 2000 },
{ 标题: '体育赛事精彩回顾', 摘要: '昨晚的比赛中,国家队表现出色,赢得关键胜利...', 热度: 4500 },
{ 标题: '新电影即将上映', 摘要: '备受期待的大片将于下月登陆各大影院...', 热度: 3500 }
]
return 基础数据.map((新闻, 索引) => ({
id: 索引.toString(),
标题: `[${this.当前分类}] ${新闻.标题}`,
摘要: 新闻.摘要,
时间: '2小时前',
图片: 索引 % 2 == 0 ? '📷' : undefined,
热度: 新闻.热度
}))
}
延迟(毫秒: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, 毫秒))
}
}
3.3 进阶练习:封装网络请求工具
创建一个可复用的网络请求工具:
// 网络工具.ts
import http from '@ohos.net.http'
export class 网络工具 {
// 基础URL
static 基础URL: string = "https://api.example.com"
// GET请求
static async GET(路径: string, 参数?: object): Promise<object> {
let url = this.基础URL + 路径
// 添加查询参数
if (参数) {
let 查询字符串 = Object.keys(参数)
.map(键 => `${键}=${encodeURIComponent(参数[键])}`)
.join('&')
url += '?' + 查询字符串
}
return this.发送请求(url, http.RequestMethod.GET)
}
// POST请求
static async POST(路径: string, 数据: object): Promise<object> {
let url = this.基础URL + 路径
return this.发送请求(url, http.RequestMethod.POST, 数据)
}
// 发送请求
private static async 发送请求(
url: string,
方法: http.RequestMethod,
数据?: object
): Promise<object> {
let http请求 = http.createHttp()
let 选项: http.HttpRequestOptions = {
method: 方法,
header: {
'Content-Type': 'application/json'
}
}
if (数据) {
选项.extraData = JSON.stringify(数据)
}
try {
let 响应 = await http请求.request(url, 选项)
if (响应.responseCode == 200) {
return JSON.parse(响应.result.toString())
} else {
throw new Error(`HTTP ${响应.responseCode}`)
}
} catch (错误) {
console.error('请求失败:', 错误)
throw 错误
}
}
}
// 使用示例
import { 网络工具 } from './网络工具'
async 获取用户列表() {
try {
let 数据 = await 网络工具.GET('/users')
console.log('用户列表:', 数据)
} catch (错误) {
console.error('获取失败:', 错误)
}
}
async 登录(用户名: string, 密码: string) {
try {
let 结果 = await 网络工具.POST('/login', {
username: 用户名,
password: 密码
})
console.log('登录成功:', 结果)
} catch (错误) {
console.error('登录失败:', 错误)
}
}
四、知识总结
4.1 核心概念回顾
- HTTP请求:客户端和服务器通信的方式
- GET:获取数据
- POST:提交数据
- 异步操作:不阻塞界面的请求方式
- async/await:处理异步的语法糖
4.2 网络请求速查
import http from '@ohos.net.http'
// GET请求
async GET请求() {
let http请求 = http.createHttp()
let 响应 = await http请求.request(
"https://api.example.com/data",
{ method: http.RequestMethod.GET }
)
return JSON.parse(响应.result.toString())
}
// POST请求
async POST请求() {
let http请求 = http.createHttp()
let 响应 = await http请求.request(
"https://api.example.com/submit",
{
method: http.RequestMethod.POST,
header: { 'Content-Type': 'application/json' },
extraData: JSON.stringify({ 键: "值" })
}
)
return JSON.parse(响应.result.toString())
}
4.3 状态码速查
| 状态码 | 含义 | 处理建议 |
|---|---|---|
| 200 | 成功 | 正常处理数据 |
| 400 | 请求错误 | 检查请求参数 |
| 401 | 未授权 | 需要登录 |
| 404 | 未找到 | 检查URL |
| 500 | 服务器错误 | 稍后重试 |
4.4 常见错误提醒
| 错误现象 | 原因 | 解决方法 |
|---|---|---|
| 请求失败 | 网络不通 | 检查网络连接 |
| 解析错误 | 返回的不是JSON | 检查API返回格式 |
| 超时 | 请求时间太长 | 增加超时时间或优化请求 |
| 权限错误 | 没有访问权限 | 检查是否需要登录 |
五、课后作业
5.1 巩固练习(必做)
练习1:用户登录
实现登录功能:
- 输入用户名和密码
- 发送POST请求验证
- 显示登录结果
- 保存登录状态
练习2:数据列表
实现一个数据列表:
- 从服务器获取列表数据
- 支持下拉刷新
- 支持上拉加载更多
- 显示加载状态
练习3:图片加载
实现图片展示:
- 加载网络图片
- 显示加载占位图
- 处理加载失败
- 支持点击查看大图
5.2 创意编程(选做)
创意1:翻译应用
- 输入文字
- 选择目标语言
- 调用翻译API
- 显示翻译结果
创意2:股票查询
- 输入股票代码
- 获取实时股价
- 显示走势图
- 添加到自选
创意3:音乐搜索
- 搜索歌曲
- 显示搜索结果
- 播放预览
- 查看歌词
5.3 下篇预习
下一篇,我们将学习数据持久化,把数据保存到本地。预习问题:
- 怎么把数据保存到手机?
- 怎么读取保存的数据?
- 什么时候需要本地存储?
恭喜你完成了第10篇的学习! 🎉
现在你已经掌握了网络请求,可以从服务器获取数据了。记住:网络请求是应用的血液,数据是应用的生命!
下节课,我们将学习如何把数据保存到本地!
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)