网络请求
HTTP请求与数据获取
概述
网络请求是大多数应用的核心能力。HarmonyOS 提供了 @ohos.net.http 模块用于发起 HTTP 请求,支持 GET、POST、PUT、DELETE 等常见方法。此外还有 @ohos.net.webSocket 用于 WebSocket 通信。理解网络请求的用法,是构建与服务器交互的应用的基础。
为什么需要网络请求?现代应用几乎都需要从服务器获取数据或提交数据。新闻应用需要拉取文章列表,电商应用需要获取商品信息,社交应用需要发送消息。掌握网络请求的用法,是应用开发的基本功。
基础概念
HTTP 请求:基于 HTTP 协议的请求-响应模型。客户端发送请求,服务端返回响应。常用的请求方法有 GET(获取数据)、POST(提交数据)、PUT(更新数据)、DELETE(删除数据)。
HttpRequest:HarmonyOS 的 HTTP 请求对象,通过 http.createHttp() 创建。每个请求对象只能使用一次,使用后需要调用 destroy() 销毁。
请求头:附加在请求中的元数据,如 Content-Type、Authorization 等,用于告诉服务器请求的格式和身份信息。
响应:服务器返回的数据,包含状态码、响应头和响应体。
快速上手
最简单的 GET 请求:
import http from '@ohos.net.http'
@Entry
@Component
struct HttpDemo {
@State result: string = '点击按钮发起请求'
async fetchData() {
// 创建 HTTP 请求对象
const httpRequest = http.createHttp()
try {
// 发起 GET 请求
const response = await httpRequest.request('https://api.example.com/data', {
method: http.RequestMethod.GET,
header: { 'Content-Type': 'application/json' }
})
// 处理响应
if (response.responseCode === 200) {
this.result = response.result as string
} else {
this.result = `请求失败: ${response.responseCode}`
}
} catch (error) {
this.result = `请求异常: ${error}`
} finally {
// 销毁请求对象
httpRequest.destroy()
}
}
build() {
Column() {
Text(this.result).fontSize(16)
Button('获取数据').onClick(() => this.fetchData())
}
.padding(16)
}
}
详细用法
POST 请求
import http from '@ohos.net.http';
async function postRequest() {
const httpRequest = http.createHttp();
try {
const response = await httpRequest.request('https://api.example.com/users', {
method: http.RequestMethod.POST,
header: {
'Content-Type': 'application/json', // 指定请求体格式
},
extraData: {
// 请求体数据
name: '张三',
email: 'zhang@example.com',
age: 25,
},
});
console.info(`响应码: ${response.responseCode}`);
console.info(`响应体: ${response.result}`);
} finally {
httpRequest.destroy();
}
}
带认证的请求
async function authenticatedRequest(token: string) {
const httpRequest = http.createHttp();
try {
const response = await httpRequest.request('https://api.example.com/profile', {
method: http.RequestMethod.GET,
header: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`, // 携带认证令牌
},
});
if (response.responseCode === 401) {
console.warn('令牌已过期,需要重新登录');
}
return response;
} finally {
httpRequest.destroy();
}
}
上传文件
async function uploadFile(filePath: string) {
const httpRequest = http.createHttp();
try {
const response = await httpRequest.request('https://api.example.com/upload', {
method: http.RequestMethod.POST,
header: {
'Content-Type': 'multipart/form-data',
},
extraData: {
// 使用文件路径上传
file: filePath,
},
});
console.info(`上传结果: ${response.responseCode}`);
} finally {
httpRequest.destroy();
}
}
请求超时和重试
async function requestWithRetry(url: string, maxRetries: number = 3) {
let lastError: Error | null = null;
for (let i = 0; i < maxRetries; i++) {
const httpRequest = http.createHttp();
try {
const response = await httpRequest.request(url, {
method: http.RequestMethod.GET,
header: { 'Content-Type': 'application/json' },
connectTimeout: 10000, // 连接超时: 10秒
readTimeout: 30000, // 读取超时: 30秒
});
if (response.responseCode === 200) {
return response.result;
}
// 服务器错误,可以重试
if (response.responseCode >= 500) {
lastError = new Error(`服务器错误: ${response.responseCode}`);
continue;
}
// 客户端错误,不重试
throw new Error(`请求失败: ${response.responseCode}`);
} catch (error) {
lastError = error as Error;
} finally {
httpRequest.destroy();
}
// 等待一段时间后重试
if (i < maxRetries - 1) {
await new Promise((resolve) => setTimeout(resolve, 1000 * (i + 1)));
}
}
throw lastError;
}
封装网络请求工具
import http from '@ohos.net.http';
// 请求配置
interface RequestConfig {
url: string;
method?: http.RequestMethod;
data?: object;
header?: object;
timeout?: number;
}
// 响应结果
interface ApiResponse<T> {
code: number;
message: string;
data: T;
}
// 封装请求方法
class HttpClient {
private baseUrl: string = 'https://api.example.com';
private token: string = '';
// 设置认证令牌
setToken(token: string) {
this.token = token;
}
// 通用请求方法
async request<T>(config: RequestConfig): Promise<ApiResponse<T>> {
const httpRequest = http.createHttp();
try {
const header: Record<string, string> = {
'Content-Type': 'application/json',
...(config.header as Record<string, string>),
};
// 如果有令牌,添加到请求头
if (this.token) {
header['Authorization'] = `Bearer ${this.token}`;
}
const response = await httpRequest.request(`${this.baseUrl}${config.url}`, {
method: config.method || http.RequestMethod.GET,
header: header,
extraData: config.data,
connectTimeout: config.timeout || 10000,
readTimeout: config.timeout || 30000,
});
if (response.responseCode === 200) {
return JSON.parse(response.result as string) as ApiResponse<T>;
} else if (response.responseCode === 401) {
throw new Error('未授权,请重新登录');
} else {
throw new Error(`请求失败: ${response.responseCode}`);
}
} catch (error) {
console.error(`请求异常: ${error}`);
throw error;
} finally {
httpRequest.destroy();
}
}
// 便捷方法
async get<T>(url: string) {
return this.request<T>({ url, method: http.RequestMethod.GET });
}
async post<T>(url: string, data?: object) {
return this.request<T>({ url, method: http.RequestMethod.POST, data });
}
async put<T>(url: string, data?: object) {
return this.request<T>({ url, method: http.RequestMethod.PUT, data });
}
async delete<T>(url: string) {
return this.request<T>({ url, method: http.RequestMethod.DELETE });
}
}
// 使用
const client = new HttpClient();
// GET 请求
const users = await client.get<User[]>('/users');
// POST 请求
const newUser = await client.post<User>('/users', {
name: '张三',
email: 'zhang@example.com',
});
常见场景
列表数据加载
interface Article {
id: number
title: string
content: string
}
@Entry
@Component
struct ArticleList {
@State articles: Article[] = []
@State isLoading: boolean = false
@State errorMsg: string = ''
async loadArticles() {
this.isLoading = true
this.errorMsg = ''
try {
const httpRequest = http.createHttp()
const response = await httpRequest.request('https://api.example.com/articles', {
method: http.RequestMethod.GET,
header: { 'Content-Type': 'application/json' }
})
if (response.responseCode === 200) {
const result = JSON.parse(response.result as string)
this.articles = result.data as Article[]
}
httpRequest.destroy()
} catch (error) {
this.errorMsg = '加载失败,请重试'
} finally {
this.isLoading = false
}
}
build() {
Column() {
if (this.isLoading) {
LoadingProgress().width(48).height(48)
} else if (this.errorMsg) {
Text(this.errorMsg).fontColor(Color.Red)
Button('重试').onClick(() => this.loadArticles())
} else {
List() {
ForEach(this.articles, (article: Article) => {
ListItem() {
Column() {
Text(article.title).fontSize(18).fontWeight(FontWeight.Bold)
Text(article.content).fontSize(14).maxLines(2)
}
.padding(16)
}
})
}
}
}
.onAppear(() => this.loadArticles())
}
}
注意事项
网络权限:在 module.json5 中需要声明网络权限:"requestPermissions": [{ "name": "ohos.permission.INTERNET" }]
主线程安全:HTTP 请求是异步操作,不会阻塞主线程。但不要在回调中执行耗时操作,应使用 TaskPool 处理。
HTTPS:生产环境应使用 HTTPS,避免明文传输敏感数据。
请求对象复用:每个 HttpRequest 对象只能使用一次,使用后必须调用 destroy() 销毁。不要复用请求对象发起新请求。
内存泄漏:在组件销毁时(aboutToDisappear),确保取消未完成的请求。
进阶用法
WebSocket 通信
import webSocket from '@ohos.net.webSocket'
@Component
struct WebSocketDemo {
@State messages: string[] = []
private ws: webSocket.WebSocket | null = null
async connectWebSocket() {
this.ws = webSocket.createWebSocket()
// 连接打开
this.ws.on('open', () => {
console.info('WebSocket 连接已建立')
})
// 接收消息
this.ws.on('message', (error, data) => {
if (!error) {
this.messages.push(data as string)
}
})
// 连接关闭
this.ws.on('close', () => {
console.info('WebSocket 连接已关闭')
})
// 连接错误
this.ws.on('error', (error) => {
console.error(`WebSocket 错误: ${error}`)
})
// 发起连接
await this.ws.connect('wss://api.example.com/ws')
}
sendMessage(msg: string) {
this.ws?.send(msg)
}
aboutToDisappear() {
this.ws?.close()
}
build() {
Column() {
List() {
ForEach(this.messages, (msg: string) => {
ListItem() { Text(msg).fontSize(14) }
})
}
Button('连接').onClick(() => this.connectWebSocket())
Button('发送').onClick(() => this.sendMessage('你好'))
}
}
}