干货分享:一文掌握HarmonyOS Next网络请求开发 原创 精华

全栈若城
发布于 2025-8-19 15:08
浏览
2收藏

@toc

前言

大家好,我是若城。本系列旨在帮助开发者快速实现HarmonyOS Next应用中的常用功能,提供即拿即用的代码示例。本文将重点介绍网络请求的封装与应用,让开发者能够轻松实现API接口调用。本系列不会过多讲解基础知识,需要申请权限的部分请开发者自行处理。

功能概述

本文将详细介绍:

  • HTTP请求基础方法的封装
  • 实用的HTTP请求工具类设计
  • 类型定义与接口规范
  • 实际应用场景演示

通过这些内容,开发者可以快速构建自己的网络请求层,并进行二次开发以满足项目需求, 好了,废话不多说,我们开始上课.

Request 工具类实现

文件位置

将以下代码保存为Request.ets,放置在项目的utils目录下:

 干货分享:一文掌握HarmonyOS Next网络请求开发-鸿蒙开发者社区

核心代码

下面是封装好的Request.ets代码可以直接拿走哦~~~

import { http } from '@kit.NetworkKit'
import { promptAction, router } from '@kit.ArkUI'

// 常量定义
export const TOKEN_KEY: string = 'token'
export const BASE_URL: string = 'xxxxxx'  // 你的接口地址

/**
 * API响应数据结构
 */
export class ResponsesData<T> {
    code: number = 0
    msg: string = ""
    data: T | null = null
}

interface EmptyInterface {}


/**
 * HTTP请求基础方法
 * @param url 请求路径
 * @param method 请求方法
 * @param data 请求参数
 * @returns Promise<T>
 */
async function requestHttp<T>(url: string = '', method: http.RequestMethod = http.RequestMethod.GET, data?: object): Promise<T> {
    // 创建HTTP请求实例
    const httpRequest = http.createHttp()

    // 拼接完整URL
    let urlStr = BASE_URL + url

    // GET请求参数处理
    if (method === http.RequestMethod.GET) {
        if (data && Object.keys(data).length) {
            const queryParams = Object.keys(data)
                .filter(key => data[key] !== undefined && data[key] !== null)
                .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`)
                .join('&')

            if (queryParams) {
                urlStr += "?" + queryParams
            }
        }
    }
    // 请求配置
    const config: http.HttpRequestOptions = {
        method,
        readTimeout: 10000,
        // GET请求不需要传递extraData
        extraData: method === http.RequestMethod.GET ? '' : data || {} as EmptyInterface,
        header: {
            'Content-Type': 'application/json',
            "Authorization": AppStorage.get(TOKEN_KEY) as string || ''
        }
    }

    try {
        // 发送请求
        const res = await httpRequest.request(urlStr, config)
        console.info(`[HTTP ${method}] ${urlStr}`)

        // 处理HTTP状态码
        switch (res.responseCode) {
            case 401:
                // 处理认证失败
                AppStorage.set<string>(TOKEN_KEY, '')
                promptAction.showToast({ message: 'token超时!' })
                router.replaceUrl({ url: 'pages/Login/LoginPage' })
                return Promise.reject(new Error('token超时!'))

            case 404:
                promptAction.showToast({ message: '请求地址不正确!' })
                return Promise.reject(new Error('请求地址不正确!'))

            default:
                // 解析响应数据
                const result = JSON.parse(res.result as string) as ResponsesData<T>

                if (result.code === 200) {
                    return result.data as T
                } else {
                    promptAction.showToast({ message: result.msg || '服务器异常!' })
                    return Promise.reject(new Error(result.msg))
                }
        }
    } catch (error) {
        promptAction.showToast({ message: `请求失败: ${error}` })
        return Promise.reject(error)
    } finally {
        // 释放资源
        httpRequest.destroy()
    }
}

/**
 * HTTP请求工具类
 */
export class Request {
    /**
     * GET请求
     * @param url 请求路径
     * @param data 请求参数
     */
    static get<T>(url: string, data?: object): Promise<T> {
        return requestHttp<T>(url, http.RequestMethod.GET, data)
    }

    /**
     * POST请求
     * @param url 请求路径
     * @param data 请求体
     */
    static post<T>(url: string, data?: object): Promise<T> {
        return requestHttp<T>(url, http.RequestMethod.POST, data)
    }

    /**
     * PUT请求
     * @param url 请求路径
     * @param data 请求体
     */
    static put<T>(url: string, data?: object): Promise<T> {
        return requestHttp<T>(url, http.RequestMethod.PUT, data)
    }

    /**
     * DELETE请求
     * @param url 请求路径
     * @param data 请求参数
     */
    static delete<T>(url: string, data?: object): Promise<T> {
        return requestHttp<T>(url, http.RequestMethod.DELETE, data)
    }
}

数据类型定义

为了更好地管理数据结构,我们创建一个types.ets文件来定义接口类型:

// 食物分类接口
export interface Category {
  name: string;
  icon: Resource;
}

// 食物数据接口
export interface FoodItem {
  name: string;
  img: string;
  calory: string;
}

// 常用食物数据接口
export interface CommonFood {
  category: string;
  name: string;
  img: string;
  calory: string;
}

// 接口返回数据
export interface ResponseData {
    keyword: string;
    Lists: FoodItem[]
}

实战应用

在页面中使用网络请求

首先,导入必要的模块:

import { FoodItem, ResponseData } from '../utils/types';
import { Request } from "../utils/request"

然后,实现搜索功能:

// 搜索功能
searchFood(keyword: string): void {
    Request.get<ResponseData>(`?key=xxxxxxx&food=${keyword}`).then(res => {
        console.log('搜索结果', JSON.stringify(res))
        this.searchResults = res.Lists
        router.pushUrl({
            url: "pages/SearchResult",
            params: res.Lists
        })
    })
}

注意: 首页的页面代码以及相关内容可以参考上一节的文章哦,这里就不做展示啦~~

搜索结果页面实现

import { FoodItem, ResponseData } from '../utils/types';
import { router, Router } from '@kit.ArkUI';

@Entry
@Component
struct SearchResult {
    @State message: string = 'Hello World';
    @State searchResults: FoodItem[] = []
    
    aboutToAppear(): void {
        const params = router.getParams() as FoodItem[]
        console.log("结果页", JSON.stringify(params))
        this.searchResults = params
    }

    build() {
        // 展示搜索结果
        List() {
            ForEach(this.searchResults, (item: FoodItem) => {
                ListItem() {
                    this.FoodItem(item)
                }
            })
        }
        .width('100%')
        .layoutWeight(1)
        .listDirection(Axis.Vertical)
        .edgeEffect(EdgeEffect.Spring)
        .padding({ top: 8, bottom: 16 })
        .backgroundColor('#F8F8F8')
    }

    @Builder
    FoodItem(item: FoodItem) {
        Column() {
            Row() {
                Image(item.img)
                    .width(80)
                    .height(80)
                    .borderRadius(12)
                    .backgroundColor('#F0F0F0')
                    .margin({ right: 16 })
                    .objectFit(ImageFit.Cover)
                    .alt($r('app.media.food'))

                Column() {
                    Text(item.name)
                        .fontSize(18)
                        .fontWeight(FontWeight.Medium)
                        .margin({ bottom: 8 })
                        .maxLines(1)
                        .textOverflow({ overflow: TextOverflow.Ellipsis })
                }
                .alignItems(HorizontalAlign.Start)
                .layoutWeight(1)
                .justifyContent(FlexAlign.Center)
            }
            .width('100%')
            .padding(16)
        }
        .backgroundColor(Color.White)
        .borderRadius(16)
        .shadow({ radius: 8, color: 'rgba(0, 0, 0, 0.08)', offsetY: 2 })
        .margin({ bottom: 12, left: 16, right: 16 })
    }
}

效果展示

首页

 干货分享:一文掌握HarmonyOS Next网络请求开发-鸿蒙开发者社区

搜索结果页

 干货分享:一文掌握HarmonyOS Next网络请求开发-鸿蒙开发者社区

总结

本文介绍了如何在HarmonyOS Next应用中封装HTTP请求工具类,并通过实际案例展示了其使用方法。通过这种封装,我们可以大大简化网络请求的实现,提高代码的可维护性和可扩展性。希望这篇文章对你的HarmonyOS Next应用开发有所帮助!下课~~

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
3
收藏 2
回复
举报
5条回复
按时间正序
/
按时间倒序
柚子的柚
柚子的柚

代码逻辑清晰明了,可以直接拿来用了,解决了新项目封装的问题,太棒啦!

1
回复
2025-8-19 15:15:33
全栈若城
全栈若城 回复了 柚子的柚
代码逻辑清晰明了,可以直接拿来用了,解决了新项目封装的问题,太棒啦!

感谢d l z c

回复
2025-8-19 15:19:52
wangKirk
wangKirk

大佬威武

1
回复
2025-8-19 15:22:48
全栈若城
全栈若城 回复了 wangKirk
大佬威武

感谢大佬支持

回复
2025-8-19 15:33:51
前端森之鸟
前端森之鸟

感谢大佬分享,可以直接使用!

回复
2025-8-20 15:12:20
回复
    相关推荐