DEV Community

HarmonyOS
HarmonyOS

Posted on

Practical Guide: Mastering HarmonyOS Network Request Development

Read the original article:Practical Guide: Mastering HarmonyOS Network Request Development

Contents

  • Preface

  • Feature Overview

  • Implementation of the Request Utility Class

    • File Location
    • Core Code
  • Data Type Definition

  • Practical Application

    • Using Network Requests in Pages
    • Implementing the Search Result Page
  • Feature Demonstration

    • Home
    • Search Result Page
  • Summary

Preface

Hello everyone, I am Ruocheng. This series aims to help developers quickly implement commonly used features in HarmonyOS apps, providing ready-to-use code examples. In this article, we will focus on encapsulating and applying network requests, enabling developers to easily make API calls. Basic concepts will not be elaborated in detail here. Please handle permission requests and related configurations as needed.

Feature Overview

This article provides a detailed explanation of the following:

  • Encapsulation of basic HTTP request methods
  • Practical design of an HTTP request utility class
  • Type definitions and API specifications
  • Actual use case examples

With these concepts, developers can quickly build their own network request layer and extend it to meet project requirements. Without further ado, let us get started.

Implementation of the Request Utility Class

File Location

Save the following code as Request.ets under the utils directory in your project:

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

Core Code

Below is the encapsulated Request.ets file — ready for direct use.

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

// Constant definitions
export const TOKEN_KEY: string = 'token'
export const BASE_URL: string = 'xxxxx' // Your API endpoint

/**
 * API response data structure
 */
export class ResponsesData<T> {
    code: number = 0
    msg: string = ""
    data: T | null = null
}

interface EmptyInterface {}


/**
 * Basic HTTP request method
 * @param url Request path
 * @param method Request method
 * @param data Request parameters
 * @returns Promise<T>
 */
async function requestHttp<T>(url: string = '', method: http.RequestMethod = http.RequestMethod.GET, data?: object): Promise<T> {
    // Create an HTTP request instance
    const httpRequest = http.createHttp()

    // Concatenate the complete URL
    let urlStr = BASE_URL + url

    // Process GET request parameters
    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
            }
        }
    }
    // Request configuration
    const config: http.HttpRequestOptions = {
        method,
        readTimeout: 10000,
        // No extraData required for GET requests
        extraData: method === http.RequestMethod.GET ? '' : data || {} as EmptyInterface,
        header: {
            'Content-Type': 'application/json',
            "Authorization": AppStorage.get(TOKEN_KEY) as string || ''
        }
    }

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

        // Process HTTP status codes
        switch (res.responseCode) {
            case 401:
                // Processing authentication failure
                AppStorage.set<string>(TOKEN_KEY, '')
                promptAction.showToast({ message: 'Token expired.' })
                router.replaceUrl({ url: 'pages/Login/LoginPage' })
                return Promise.reject(new Error(Token expired.'))

            case 404:
                promptAction.showToast({ message: 'Invalid request URL.' })
                return Promise.reject(new Error('Invalid request URL.'))

            default:
                // Parse the response data
                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 || 'Server error.' })
                    return Promise.reject(new Error(result.msg))
                }
        }
    } catch (error) {
        promptAction.showToast({ message: `Request failed: ${error}` })
        return Promise.reject(error)
    } finally {
        // Release resources
        httpRequest.destroy()
    }
}

/**
 * HTTP request utility class
 */
export class Request {
    /**
     * GET request
 * @param url Request path
 * @param data Request parameters
     */
    static get<T>(url: string, data?: object): Promise<T> {
        return requestHttp<T>(url, http.RequestMethod.GET, data)
    }

    /**
     * POST request
 * @param url Request path
     * @param data Request body
     */
    static post<T>(url: string, data?: object): Promise<T> {
        return requestHttp<T>(url, http.RequestMethod.POST, data)
    }

    /**
     * PUT request
 * @param url Request path
     * @param data Request body
     */
    static put<T>(url: string, data?: object): Promise<T> {
        return requestHttp<T>(url, http.RequestMethod.PUT, data)
    }

    /**
     * DELETE request
 * @param url Request path
 * @param data Request parameters
     */
    static delete<T>(url: string, data?: object): Promise<T> {
        return requestHttp<T>(url, http.RequestMethod.DELETE, data)
    }
}
Enter fullscreen mode Exit fullscreen mode

Data Type Definition

To better manage data structures, create a file named types.ets to define API types:

// Food category API
export interface Category {
  name: string;
  icon: Resource;
}

// Food data API
export interface FoodItem {
  name: string;
  img: string;
  calory: string;
}

// Common food data API
export interface CommonFood {
  category: string;
  name: string;
  img: string;
  calory: string;
}

// API response data
export interface ResponseData {
    keyword: string;
    Lists: FoodItem[]
}
Enter fullscreen mode Exit fullscreen mode

Practical Application

Using Network Requests in Pages

First, import the necessary modules:

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

Then, implement the search feature:

// Search feature
searchFood(keyword: string): void {
    Request.get<ResponseData>(`?key=xxxxxxx&food=${keyword}`).then(res => {
        console.log('Search result', JSON.stringify(res))
        this.searchResults = res.Lists
        router.pushUrl({
            url: "pages/SearchResult",
            params: res.Lists
        })
    })
}
Enter fullscreen mode Exit fullscreen mode

Note: For the home page implementation and related content, please refer to the previous article.

Implementing the Search Result Page

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("Result page", JSON.stringify(params))
        this.searchResults = params
    }

    build() {
        // Display search results
        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 })
    }
}
Enter fullscreen mode Exit fullscreen mode

Feature Demonstration

Home

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

Search Result Page

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

Summary

This article explained how to encapsulate an HTTP request utility class in a HarmonyOS app and demonstrated its use through a practical example. With this encapsulation approach, we can greatly simplify the implementation of network requests, improving code maintainability and scalability. Hope this article helps you in your HarmonyOS app development journey. Class dismissed.

Written by Full-stack Developer Ruocheng

Top comments (0)