HarmonyOS网络请求实战:使用@ohos.net.http进行HTTP通信
·
网络请求是现代移动应用的基础能力,HarmonyOS通过@ohos.net.http模块提供了强大的HTTP客户端功能。本文将全面讲解如何在HarmonyOS应用中进行网络通信,涵盖从基础请求到高级特性的完整实战内容。
一、网络请求基础概念
1.1 HTTP协议概述
HTTP(HyperText Transfer Protocol)是互联网上应用最为广泛的应用层协议,理解其基本概念对网络编程至关重要。
HTTP请求方法:
- GET:获取资源(幂等操作)
- POST:创建资源或提交数据
- PUT:更新资源
- DELETE:删除资源
- PATCH:部分更新资源
HTTP状态码分类:
- 1xx:信息响应
- 2xx:成功响应(200 OK,201 Created)
- 3xx:重定向响应
- 4xx:客户端错误(404 Not Found)
- 5xx:服务器错误
1.2 HarmonyOS网络权限配置
在进行网络请求前,需要在module.json5文件中配置网络权限:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "需要网络权限进行数据请求"
}
]
}
}
二、基础HTTP请求实战
2.1 创建HTTP请求实例
import http from '@ohos.net.http'
@Component
struct BasicHttpExample {
@State responseData: string = ''
@State isLoading: boolean = false
// 创建HTTP请求实例
private httpRequest: http.HttpRequest = http.createHttp()
build() {
Column({ space: 20 }) {
Text('基础HTTP请求演示')
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text('响应数据:')
.fontSize(16)
.fontWeight(FontWeight.Medium)
Scroll() {
Text(this.responseData)
.fontSize(14)
.fontColor('#666')
.textAlign(TextAlign.Start)
}
.height(200)
.backgroundColor('#F5F5F5')
.padding(10)
Row({ space: 15 }) {
Button('GET请求')
.onClick(() => this.sendGetRequest())
.enabled(!this.isLoading)
Button('POST请求')
.onClick(() => this.sendPostRequest())
.enabled(!this.isLoading)
}
if (this.isLoading) {
Progress({ value: 50, total: 100 })
.width(200)
Text('请求中...')
.fontSize(14)
.fontColor('#666')
}
}
.width('100%')
.padding(20)
.onDetach(() => {
// 组件销毁时释放资源
this.httpRequest.destroy()
})
}
2.2 GET请求实现
// GET请求示例
async sendGetRequest() {
this.isLoading = true
this.responseData = '请求中...'
try {
// 配置请求选项
let httpRequestOptions: http.HttpRequestOptions = {
method: http.RequestMethod.GET,
header: {
'Content-Type': 'application/json'
},
expectDataType: http.HttpDataType.OBJECT, // 期望返回对象类型
usingCache: true, // 启用缓存
priority: 1 // 请求优先级
}
// 发送请求(使用公共测试API)
let response = await this.httpRequest.request(
'https://jsonplaceholder.typicode.com/posts/1',
httpRequestOptions
)
// 处理响应
if (response.responseCode === http.ResponseCode.OK) {
this.responseData = JSON.stringify(response.result, null, 2)
console.log('GET请求成功:', response.result)
} else {
this.responseData = `请求失败,状态码: ${response.responseCode}`
}
} catch (error) {
console.error('GET请求异常:', error)
this.responseData = `请求异常: ${error.message}`
} finally {
this.isLoading = false
}
}
2.3 POST请求实现
// POST请求示例
async sendPostRequest() {
this.isLoading = true
this.responseData = '请求中...'
try {
// 准备请求数据
const postData = {
title: 'HarmonyOS网络请求测试',
body: '这是一个测试POST请求的内容',
userId: 1
}
let httpRequestOptions: http.HttpRequestOptions = {
method: http.RequestMethod.POST,
header: {
'Content-Type': 'application/json'
},
extraData: JSON.stringify(postData), // 请求体数据
expectDataType: http.HttpDataType.OBJECT
}
let response = await this.httpRequest.request(
'https://jsonplaceholder.typicode.com/posts',
httpRequestOptions
)
if (response.responseCode === http.ResponseCode.CREATED) {
this.responseData = JSON.stringify(response.result, null, 2)
console.log('POST请求成功:', response.result)
} else {
this.responseData = `请求失败,状态码: ${response.responseCode}`
}
} catch (error) {
console.error('POST请求异常:', error)
this.responseData = `请求异常: ${error.message}`
} finally {
this.isLoading = false
}
}
三、高级网络请求特性
3.1 请求配置与管理
class HttpService {
private httpRequest: http.HttpRequest
constructor() {
this.httpRequest = http.createHttp()
// 配置全局请求参数
this.configureHttpRequest()
}
private configureHttpRequest() {
// 设置请求超时(毫秒)
this.httpRequest.setTimeout(10000)
// 设置自定义HTTP头
this.httpRequest.setExtraHeaders({
'User-Agent': 'HarmonyOS-App/1.0',
'Accept-Language': 'zh-CN,zh;q=0.9'
})
}
// 支持查询参数的GET请求
async getWithParams(url: string, params?: Record<string, any>) {
let fullUrl = url
if (params) {
const queryString = Object.keys(params)
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
.join('&')
fullUrl += `?${queryString}`
}
const options: http.HttpRequestOptions = {
method: http.RequestMethod.GET,
header: { 'Content-Type': 'application/json' },
usingCache: true,
priority: 1
}
return await this.httpRequest.request(fullUrl, options)
}
}
3.2 文件上传实现
// 文件上传功能
async uploadFile(fileUri: string, uploadUrl: string) {
try {
// 创建FormData格式数据
const formData = new FormData()
// 获取文件信息
const fileInfo = await this.getFileInfo(fileUri)
// 添加文件到FormData
formData.append('file', {
uri: fileUri,
type: fileInfo.type,
name: fileInfo.name
} as any)
// 添加其他字段
formData.append('description', 'HarmonyOS文件上传测试')
const options: http.HttpRequestOptions = {
method: http.RequestMethod.POST,
header: {
'Content-Type': 'multipart/form-data'
},
extraData: formData
}
const response = await this.httpRequest.request(uploadUrl, options)
if (response.responseCode === http.ResponseCode.OK) {
console.log('文件上传成功')
return response.result
} else {
throw new Error(`上传失败: ${response.responseCode}`)
}
} catch (error) {
console.error('文件上传异常:', error)
throw error
}
}
3.3 网络状态监控
import network from '@ohos.net.network'
@Component
struct NetworkStatusMonitor {
@State networkType: string = '未知'
@State isConnected: boolean = false
private netHandle: network.NetHandle | null = null
aboutToAppear() {
this.startNetworkMonitoring()
}
aboutToDisappear() {
this.stopNetworkMonitoring()
}
// 开始网络状态监控
async startNetworkMonitoring() {
try {
// 获取当前网络连接
this.netHandle = network.getDefaultNet()
// 获取网络类型
this.updateNetworkType()
// 监听网络状态变化
network.on('netAvailable', (data: network.NetCapabilityInfo) => {
this.isConnected = true
this.updateNetworkType()
console.log('网络已连接')
})
network.on('netCapabilitiesChange', (data: network.NetCapabilityInfo) => {
this.updateNetworkType()
console.log('网络能力发生变化')
})
network.on('netConnectionPropertiesChange', (data: network.ConnectionProperties) => {
console.log('网络连接属性发生变化')
})
network.on('netBlockStatusChange', (data: network.NetBlockStatusInfo) => {
this.isConnected = !data.blocked
console.log('网络阻塞状态变化:', data.blocked)
})
} catch (error) {
console.error('网络监控启动失败:', error)
}
}
// 更新网络类型信息
async updateNetworkType() {
if (!this.netHandle) return
try {
const netCapabilities = await this.netHandle.getNetCapabilities()
if (netCapabilities.hasNetCap(network.NetCap.NET_CAPABILITY_WIFI)) {
this.networkType = 'WiFi'
} else if (netCapabilities.hasNetCap(network.NetCap.NET_CAPABILITY_CELLULAR)) {
this.networkType = '移动网络'
} else if (netCapabilities.hasNetCap(network.NetCap.NET_CAPABILITY_ETHERNET)) {
this.networkType = '有线网络'
} else {
this.networkType = '其他网络'
}
} catch (error) {
console.error('获取网络类型失败:', error)
}
}
stopNetworkMonitoring() {
network.off('netAvailable')
network.off('netCapabilitiesChange')
network.off('netConnectionPropertiesChange')
network.off('netBlockStatusChange')
}
build() {
Column({ space: 15 }) {
Text('网络状态监控')
.fontSize(18)
.fontWeight(FontWeight.Bold)
Row({ space: 10 }) {
Text('连接状态:')
.fontSize(14)
Text(this.isConnected ? '已连接' : '未连接')
.fontSize(14)
.fontColor(this.isConnected ? '#34C759' : '#FF3B30')
}
Row({ space: 10 }) {
Text('网络类型:')
.fontSize(14)
Text(this.networkType)
.fontSize(14)
.fontColor('#007DFF')
}
}
.padding(20)
}
}
四、网络请求封装与最佳实践
4.1 统一的HTTP客户端封装
class HttpClient {
private static instance: HttpClient
private httpRequest: http.HttpRequest
private baseURL: string
private interceptors: RequestInterceptor[] = []
private constructor(baseURL: string) {
this.baseURL = baseURL
this.httpRequest = http.createHttp()
this.setupInterceptors()
}
static getInstance(baseURL?: string): HttpClient {
if (!HttpClient.instance) {
if (!baseURL) {
throw new Error('首次调用需要提供baseURL')
}
HttpClient.instance = new HttpClient(baseURL)
}
return HttpClient.instance
}
// 设置请求拦截器
private setupInterceptors() {
// 请求拦截器示例
this.interceptors.push({
onRequest: (config) => {
// 添加认证token
const token = this.getAuthToken()
if (token) {
config.header = config.header || {}
config.header['Authorization'] = `Bearer ${token}`
}
return config
},
onResponse: (response) => {
// 统一处理响应
if (response.responseCode >= 400) {
throw this.handleError(response)
}
return response
},
onError: (error) => {
// 统一错误处理
console.error('请求错误:', error)
throw error
}
})
}
// 统一的GET方法
async get<T>(endpoint: string, params?: Record<string, any>): Promise<T> {
const url = this.buildURL(endpoint, params)
const config: http.HttpRequestOptions = {
method: http.RequestMethod.GET,
header: { 'Content-Type': 'application/json' },
usingCache: true
}
// 执行拦截器
const finalConfig = this.runRequestInterceptors(config)
const response = await this.httpRequest.request(url, finalConfig)
const processedResponse = this.runResponseInterceptors(response)
return processedResponse.result as T
}
// 统一的POST方法
async post<T>(endpoint: string, data?: any): Promise<T> {
const url = `${this.baseURL}${endpoint}`
const config: http.HttpRequestOptions = {
method: http.RequestMethod.POST,
header: { 'Content-Type': 'application/json' },
extraData: data ? JSON.stringify(data) : undefined
}
const finalConfig = this.runRequestInterceptors(config)
const response = await this.httpRequest.request(url, finalConfig)
const processedResponse = this.runResponseInterceptors(response)
return processedResponse.result as T
}
// 构建完整URL
private buildURL(endpoint: string, params?: Record<string, any>): string {
let url = `${this.baseURL}${endpoint}`
if (params) {
const queryParams = new URLSearchParams(params).toString()
url += `?${queryParams}`
}
return url
}
// 错误处理
private handleError(response: http.HttpResponse): Error {
switch (response.responseCode) {
case http.ResponseCode.BAD_REQUEST:
return new Error('请求参数错误')
case http.ResponseCode.UNAUTHORIZED:
return new Error('未授权访问')
case http.ResponseCode.FORBIDDEN:
return new Error('访问被禁止')
case http.ResponseCode.NOT_FOUND:
return new Error('资源不存在')
case http.ResponseCode.INTERNAL_ERROR:
return new Error('服务器内部错误')
default:
return new Error(`请求失败: ${response.responseCode}`)
}
}
private runRequestInterceptors(config: http.HttpRequestOptions): http.HttpRequestOptions {
return this.interceptors.reduce((acc, interceptor) => {
return interceptor.onRequest ? interceptor.onRequest(acc) : acc
}, config)
}
private runResponseInterceptors(response: http.HttpResponse): http.HttpResponse {
return this.interceptors.reduce((acc, interceptor) => {
return interceptor.onResponse ? interceptor.onResponse(acc) : acc
}, response)
}
private getAuthToken(): string | null {
// 从安全存储中获取token
return localStorage.getItem('auth_token')
}
}
interface RequestInterceptor {
onRequest?(config: http.HttpRequestOptions): http.HttpRequestOptions
onResponse?(response: http.HttpResponse): http.HttpResponse
onError?(error: Error): Error
}
4.2 API服务层封装
// 具体的API服务
class UserApiService {
private httpClient: HttpClient
constructor() {
this.httpClient = HttpClient.getInstance('https://api.example.com')
}
// 用户登录
async login(credentials: LoginCredentials): Promise<AuthResponse> {
return await this.httpClient.post<AuthResponse>('/auth/login', credentials)
}
// 获取用户信息
async getUserProfile(userId: number): Promise<UserProfile> {
return await this.httpClient.get<UserProfile>(`/users/${userId}`)
}
// 更新用户信息
async updateUserProfile(userId: number, profile: Partial<UserProfile>): Promise<UserProfile> {
return await this.httpClient.post<UserProfile>(`/users/${userId}`, profile)
}
// 分页获取用户列表
async getUsers(page: number = 1, limit: number = 10): Promise<UserListResponse> {
return await this.httpClient.get<UserListResponse>('/users', {
page,
limit
})
}
}
// 数据模型
interface LoginCredentials {
username: string
password: string
}
interface AuthResponse {
token: string
expiresIn: number
user: UserProfile
}
interface UserProfile {
id: number
username: string
email: string
avatar?: string
createdAt: string
}
interface UserListResponse {
users: UserProfile[]
total: number
page: number
limit: number
}
五、实战应用:新闻客户端
5.1 新闻数据获取
@Component
struct NewsClient {
@State newsList: NewsItem[] = []
@State isLoading: boolean = false
@State errorMessage: string = ''
private newsService = new NewsApiService()
build() {
Column({ space: 20 }) {
Text('新闻客户端')
.fontSize(20)
.fontWeight(FontWeight.Bold)
if (this.isLoading) {
this.buildLoadingState()
} else if (this.errorMessage) {
this.buildErrorState()
} else {
this.buildNewsList()
}
Button('刷新新闻')
.onClick(() => this.loadNews())
.enabled(!this.isLoading)
}
.width('100%')
.padding(20)
.onAppear(() => {
this.loadNews()
})
}
@Builder
buildLoadingState() {
Column({ space: 10 }) {
Progress({ value: 50, total: 100 })
.width(200)
Text('加载中...')
.fontSize(14)
.fontColor('#666')
}
}
@Builder
buildErrorState() {
Column({ space: 10 }) {
Image($r('app.media.error'))
.width(60)
.height(60)
Text(this.errorMessage)
.fontSize(14)
.fontColor('#FF3B30')
.textAlign(TextAlign.Center)
}
}
@Builder
buildNewsList() {
List({ space: 10 }) {
ForEach(this.newsList, (news: NewsItem) => {
ListItem() {
NewsItemComponent({ news })
}
})
}
.layoutWeight(1)
}
async loadNews() {
this.isLoading = true
this.errorMessage = ''
try {
const response = await this.newsService.getNews()
this.newsList = response.articles
} catch (error) {
this.errorMessage = error.message
console.error('加载新闻失败:', error)
} finally {
this.isLoading = false
}
}
}
class NewsApiService {
private httpClient: HttpClient
constructor() {
this.httpClient = HttpClient.getInstance('https://newsapi.org/v2')
}
async getNews(category: string = 'technology'): Promise<NewsResponse> {
return await this.httpClient.get<NewsResponse>('/top-headlines', {
country: 'us',
category: category,
apiKey: 'your-api-key-here' // 实际使用时替换为真实API密钥
})
}
}
interface NewsItem {
title: string
description: string
url: string
urlToImage: string
publishedAt: string
author: string
}
interface NewsResponse {
status: string
totalResults: number
articles: NewsItem[]
}
六、错误处理与性能优化
6.1 全面的错误处理策略
class ErrorHandler {
static handleNetworkError(error: any): string {
if (error.code === http.ResponseCode.TIMEOUT) {
return '请求超时,请检查网络连接'
} else if (error.code === http.ResponseCode.UNAVAILABLE) {
return '网络不可用,请检查网络设置'
} else if (error.code >= 500) {
return '服务器繁忙,请稍后重试'
} else if (error.code >= 400) {
return '请求错误,请检查输入参数'
} else {
return '网络请求失败,请重试'
}
}
static isNetworkError(error: any): boolean {
return error && (
error.code === http.ResponseCode.UNAVAILABLE ||
error.code === http.ResponseCode.TIMEOUT ||
!navigator.onLine
)
}
static shouldRetry(error: any): boolean {
// 可重试的错误类型
return this.isNetworkError(error) || error.code >= 500
}
}
6.2 请求重试机制
async requestWithRetry(
url: string,
options: http.HttpRequestOptions,
maxRetries: number = 3
): Promise<http.HttpResponse> {
let lastError: Error
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await this.httpRequest.request(url, options)
return response
} catch (error) {
lastError = error
if (!ErrorHandler.shouldRetry(error) || attempt === maxRetries) {
break
}
// 指数退避策略
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000)
await this.sleep(delay)
console.log(`请求失败,第${attempt}次重试...`)
}
}
throw lastError
}
private sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms))
}
总结
通过本文的深入学习,您应该掌握了HarmonyOS网络请求的完整知识体系:
- 基础请求:GET、POST等HTTP方法的实现
- 高级特性:文件上传、网络状态监控等复杂场景
- 架构设计:统一的HTTP客户端封装和API服务层设计
- 实战应用:新闻客户端的完整数据流实现
- 错误处理:全面的异常处理和重试机制
网络请求是应用与外界交互的桥梁,良好的网络层设计能显著提升应用的用户体验和稳定性。
需要参加鸿蒙认证的请点击 鸿蒙认证链接
更多推荐

所有评论(0)