HarmonyOS Hongmeng application development (7. Introduction and encapsulation use of HTTP network component axios)

In the development of the HarmonyOS application, the @ohos.net.http module that is officially provided can be used to access the network through HTTP. However, the official direct use is not very easy to use and needs to be packaged. It is recommended to use the popular axios network client library in front-end development. If it is the front-end developer, it will be easier to use axios.

Catalog

Introduction to axios

Can also use Axios? in HarmonyOS

Using the axios Network Request Library

Download Install

Activate permissions

Simple to use

Package and use of the axios module

Client encapsulation

Use after encapsulation

Official @ohos/net.http Introduction

Official Easy Package

Official http Module Package Use

Written at the end

Additional Resources

Introduction to axios

Axios is a well-known JavaScript-based open source library for sending HTTP requests in environments such as browsers and Node.js. It supports Promise API and can handle the complexity behind XMLHttpRequests and Fetch API, providing developers with a simple and easy-to-use way to implement Ajax (Asynchronous JavaScript and XML) requests.

The earliest browser page requests data from the server and returns the data of the whole page. The page will be refreshed forcibly, which is not very friendly to the user. We only want to modify some data of the page, but the whole page is returned from the server. So a new technology appeared, asynchronous network request Ajax, Asynchronous JavaScript and XML, it can exchange a small amount of data with the background server, so that the web page realizes asynchronous local update.

Because the native XMLHttpRequest API in the browser is more difficult to use, there are more javascript frameworks for implementing ajax, such as the familiar jQuery, Dojo, YUI, and so on. Today, a lightweight framework, called axios, is emerging as essentially a package for native XHR, except that it is an implementation version of Promise that meets the latest ESs specifications.

Key Features:

  1. Cross-platform support: The Axios can send requests through the XMLHttpRequests on the browser side, and use the http/https module in the Node.js.
  2. Promise API: All Axios network request methods return Promise objects, making asynchronous programming simpler and easier to handle.
  3. Intercept requests and responses: Provides global and instance-level interceptors for requests and responses, which can perform operations such as preprocessing, error processing, or data conversion before the request is sent or after the response is returned.
  4. Cancel Request: Supports active cancellation of an HTTP request that has been issued but not yet completed.
  5. Automatically convert JSON data: The Axios automatically converts the JSON data from the server into JavaScript objects, and automatically serializes the JSON data in the POST, PUT, and other request bodies into a string for sending.
  6. Configuration flexibility: The Axios allows you to customize various configuration items such as request headers, URLs, and timeout periods, which are applicable to different scenarios.
  7. Multiple request methods: Support all standard HTTP methods (GET, POST, PUT, DELETE, etc.) and good support for non-standard methods such as PATCH.
  8. Upload and download progress monitoring: The Axios also monitors the progress events of file uploading and downloading.

Can also use Axios? in HarmonyOS

In HarmonyOS, the @ohos/net.http module is officially available for network access. It is an official basic HTTP data request capability library. It directly supports the bottom layer of the HTTP protocol. Developers can send HTTP requests such as GET and POST through this module and process response results. Because it is a system-level API, its advantages are that performance and compatibility are guaranteed, and it is suitable for basic HTTP communication requirements.

Although @ohos/net.http modules are officially available for network access, the Axios library can be seen as a more powerful and easy-to-use package, and the interface usage is more consistent with the habits of front-end developers. The Axios library has become a popular client-side library in modern JavaScript applications due to its powerful functionality and ease of use.

To use the original axios library directly, the module name of the Axios library in the HarmonyOS is @ohos/axios.

The @ohos/axios third-party library is adapted based on the axios library, so that it can run in a network request library in the OpenHarmony, and this library follows the existing usage and features of the axios library, so that it is more suitable for the development of the Hongmeng project.

The @ohos/axios module may be understood as an encapsulation or extension of the official HTTP API. The @ohos/axios module provides a higher level of abstraction and convenience, and may include more functional features, such as automatic data format conversion, error processing, an interceptor mechanism, and good support for the Promise. This is to simplify a development process and improve development efficiency. When actually developing an application, if you need richer and more flexible network request management functions, it is usually recommended to use an encapsulation library such as @ohos/axios.

Through the check of the @ohos/axis source code, it is found that the ohos.net.http module is used to adapt the v1.3.4 version of the original library, so that it can be applied to the HarmonyOS and its existing usage and features are followed.

  • Http Request
  • Promise API
  • Request and response interceptors
  • Convert data data for request and response
  • Automatically convert JSON data data

Ohos/axios module source code:

OpenHarmony-SIG/ohos_axios

Using the axios Network Request Library

Interface List

|Interface| parameter | Function |
| — | — | — |
| axios(config) | config:Request configuration | send request |
| axios.create(config) | config:Request configuration | Create instance |
| axios.request(config) | config:Request configuration | send request |
| axios.get(url[, config]) | Url: Request Address
Config: Request Configuration Send get Request
| axios.delete(url[, config]) | Url: Request Address
Config: Request Configuration Send delete Request
| axios.post(url[, data[, config]]) | Url: Request Address
Data: Sending Request Volume Data
Config: Request Configuration Send post Request
| axios.put(url[, data[, config]]) | Url: Request Address
Data: Sending Request Volume Data
Config: Request Configuration Send put Request

Attribute List

|Properties| describe |
| — | — |
| axios.defaults[’xxx’] | Default settings. Value is the configuration item in the request configuration config
For example, the axios.defaults.headers obtains the header information.
| axios.interceptors | Interceptor。reference Interceptor usage of |

Download Install

  • Mode 1: In the terminal window, run the following command to install the three-party package. The DevEco Studio automatically adds the three-party package dependency in the oh-package.json5 of the project.
ohpm install @ohos/axis

If no ohpm command is prompted, the environment variable is not configured. Click the Configure ohpm command to view the detailed configuration.

  • Manner 2: Set the three-party package dependency in the oh-package.json5 of the project. The configuration example is as follows:
"dependencies": { "@ohos/axis": "^2.1.0"}

Activate permissions

Ohos.permission.INTERNET permissions need to be configured. Locate the module.json5 file in the project directory entry\src\main and configure the network request permissions.

{
  "module": {
    "name": "entry",
    "type": "entry",
    "description": "$string:module_desc",
    "mainElement": "EntryAbility",
    "deviceTypes": [
      "phone"
    ],
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ]
  }
}

Simple to use

import axios from '@ohos/axios'
//createaxiosExample of
const instance = axios.create({
  baseURL: "http://xx.xx.xx.xx", //base path,Want to seeAPIFeatures of the help document to determine the base path
  timeout: 5000, //Request timeout time
  headers: {
    "Content-Type": "application/json"
  }
})

//response interceptor,Further process the returned data through the response interceptor
instance.interceptors.response.use((response) => {
  //Only return results if the interface has data
  if (200 === response.status) {
    return response.data; //Data returned by the interface
  }
  return Promise.reject(response); //Indicates an error in the request,hand overcatchto handle the structure
}, err => {
  return Promise.reject(err)
})

/**
 * getask
 * @param params = {} query parameters
 * @returns
 */
export function httpGet(url:string, params = {}) {
  return instance.get<any>(url, {
    params
  })
}

/**
 * postask
 * @param data = {} Request body data
 * @returns
 */
export function httpPost(url:string, data = {}) {
  return instance.post<any>(url, {
   data 
  })
}

Package and use of the axios module

Axios module package:

//AxiosHttp.ets

import axios, {
  AxiosInstance,
  AxiosRequestConfig,
  AxiosRequestHeaders,
  AxiosResponse,
  InternalAxiosRequestConfig
} from "@ohos/axios";
import { LogUtils } from '../utils/LogUtils';


/**
 * Define interface response wrapper class
 */
export interface BaseResponse {
  //wanAndroid-APIresponse body
  errorCode: number
  errorMsg: string
  //expandxxx-APIresponse body
}

/**
 * Interface implementation class packaging,For example, other businesses can inherit and implement it againxxxResponse
 */
export interface ApiResponse<T = any> extends BaseResponse {
  //wanAndroid-APIresponse body
  data: T | any;
  //expandxxx-APIresponse body
}

/**
 * After encapsulation,Incoming interceptors are not supported
 * You need to define your own interface inheritance AxiosRequestConfigtype
 * thus supporting incoming interceptors,But the interceptor option should be an optional attribute
 * Then request the instance passed inoptionsfor inheritedAxiosRequestConfigcustom type
 */
interface InterceptorHooks {
  requestInterceptor?: (config: HttpRequestConfig) => Promise<HttpRequestConfig>;
  requestInterceptorCatch?: (error: any) => any;
  responseInterceptor?: (response: AxiosResponse) => AxiosResponse | Promise<AxiosResponse>;
  responseInterceptorCatch?: (error: any) => any;
}

// @ts-ignore
interface HttpRequestConfig extends InternalAxiosRequestConfig {
  showLoading?: boolean; //Whether to display the requestloading
  checkResultCode?: boolean; //Whether to check the response result code
  checkLoginState?: boolean //Verify user login status
  needJumpToLogin?: boolean //Do you need to jump to the login page?
  interceptorHooks?: InterceptorHooks;
  headers?: AxiosRequestHeaders
}


/**
 * Network request structure
 * based onaxiosFramework implementation
 */
class AxiosHttpRequest {
  config: HttpRequestConfig;
  interceptorHooks?: InterceptorHooks;
  instance: AxiosInstance;

  constructor(options: HttpRequestConfig) {
    this.config = options;
    this.interceptorHooks = options.interceptorHooks;
    this.instance = axios.create(options);
    this.setupInterceptor()
  }

  setupInterceptor(): void {
    this.instance.interceptors.request.use(
      //Here are mainly high versionsaxiosWhen setting up the interceptor inConfigThe attribute must beInternalAxiosRequestConfig,butInternalAxiosRequestConfiginsideheadersIt is a must pass,So in the implemented subclass, if I set it to be non-required, an error will be reported.,Added an ignore annotation
      // @ts-ignore
      this.interceptorHooks?.requestInterceptor,
      this.interceptorHooks?.requestInterceptorCatch,
    );
    this.instance.interceptors.response.use(
      this.interceptorHooks?.responseInterceptor,
      this.interceptorHooks?.responseInterceptorCatch,
    );
  }

  // The role of type parameters,TDecideAxiosResponseIn the exampledatatype
  request<T = any>(config: HttpRequestConfig): Promise<T> {
    return new Promise<T>((resolve, reject) => {
      this.instance
        .request<any, T>(config)
        .then(res => {
          resolve(res);
        })
        .catch((err) => {
          LogUtils.error("network requestRequestabnormal:", err.message)
          errorHandler(err)
          if (err) {
            reject(err);
          }
        });
    });
  }

  get<T = any>(config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: 'GET' });
  }

  post<T = any>(config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: 'POST' });
  }

  delete<T = any>(config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: 'DELETE' });
  }

  patch<T = any>(config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: 'PATCH' });
  }
}

export function errorHandler(error: any) {
  if (error instanceof AxiosError) {
    showToast(error.message)
  } else if (error != undefined && error.response != undefined && error.response.status) {
    switch (error.response.status) {
    // 401: Not logged in
    // If you are not logged in, you will be redirected to the login page.,And carry the path of the current page
    // Return to the current page after successful login,This step needs to be done on the login page。
      case 401:

        break;
    // 403 tokenExpired
    // Prompt user when login expires
    // clear localtokenand clearvuexmiddletokenobject
    // Jump to login page
      case 403:
        showToast("Login expired,please login again")
      // Cleartoken
      // localStorage.removeItem('token');
        break;
    // 404Request does not exist
      case 404:
        showToast("The network request does not exist")
        break;

    // Other errors,Throw an error message directly
      default:
        showToast(error.response.data.message)
    }

  }
}

export default AxiosHttpRequest 

Client encapsulation

//AxiosRequest.ets
import {AxiosHttpRequest,errorHandler} from './AxiosHttp'
import { AxiosError, AxiosRequestHeaders } from '@ohos/axios';
import { LogUtils } from '../utils/LogUtils';
import showToast from '../utils/ToastUtils';
import { hideLoadingDialog, showLoadingDialog } from '../utils/DialogUtils';
import { StorageUtils } from '../utils/StorageUtils';
import { StorageKeys } from '../constants/StorageKeys';
import { JsonUtils } from '../utils/JsonUtils';
import { Router } from '../route/Router';
import { RoutePath } from '../route/RoutePath';

/**
 * axiosRequest client to create
 */
const axiosClient = new AxiosHttpRequest({
  baseURL: "/api",
  timeout: 10 * 1000,
  checkResultCode: false,
  headers: {
    'Content-Type': 'application/json'
  } as AxiosRequestHeaders,
  interceptorHooks: {
    requestInterceptor: async (config) => {
      // Do some processing before sending the request,For example, print request information
      LogUtils.debug('network requestRequest Request method:', `{config.method}`);
      LogUtils.debug('network requestRequest request link:', `{config.url}`);
      LogUtils.debug('network requestRequest Params:', `\n{JsonUtils.stringify(config.params)}`);
      LogUtils.debug('network requestRequest Data:', `{JsonUtils.stringify(config.data)}`);
      axiosClient.config.showLoading = config.showLoading
      if (config.showLoading) {
        showLoadingDialog("loading...")
      }
      if (config.checkLoginState) {
        let hasLogin = await StorageUtils.get(StorageKeys.USER_LOGIN, false)
        LogUtils.debug('network requestRequest Login status verification>>>', `{hasLogin.toString()}`);
        if (hasLogin) {
          return config
        } else {
          if (config.needJumpToLogin) {
            Router.push(RoutePath.TestPage)
          }
          throw new AxiosError("please sign in")
        }
      }
      return config;
    },
    requestInterceptorCatch: (err) => {
      LogUtils.error("network requestRequestError", err.toString())
      if (axiosClient.config.showLoading) {
        hideLoadingDialog()
      }
      return err;
    },
    responseInterceptor: (response) => {
      //Prioritize executing your own request response interceptor,Executing a general requestrequestof
      if (axiosClient.config.showLoading) {
        hideLoadingDialog()
      }
      LogUtils.debug('Network request responseResponse:', `\n{JsonUtils.stringify(response.data)}`);
      if (response.status === 200) {
        // @ts-ignore
        const checkResultCode = response.config.checkResultCode
        if (checkResultCode && response.data.errorCode != 0) {
          showToast(response.data.errorMsg)
          return Promise.reject(response)
        }
        return Promise.resolve(response.data);
      } else {
        return Promise.reject(response);
      }
    },
    responseInterceptorCatch: (error) => {
      if (axiosClient.config.showLoading) {
        hideLoadingDialog()
      }
      LogUtils.error("Network request response exception", error.toString())
      errorHandler(error);
      return Promise.reject(error);
    },
  }
});

export default axiosClient; 

Use after encapsulation

When packaged, use becomes simple. Examples are as follows:

 import axiosClient from './AxiosRequest'

let baseUrl = "https://www.wanandroid.com/"

//Return data structure definition
interface HomeModelIssueList {
  releaseTime: number;
  type: string;
  date: number;
  publishTime: number;
  count: number;
}

interface HomeModel {
  issueList: HomeModelIssueList[];
  itemList: HomeModelIssueList[];
  nextPageUrl: string;
  nextPublishTime: number;
  newestIssueType: string;
}

interface BannerDataModelData {
  desc: string;
  id: number;
  imagePath: string;
  isVisible: number;
  order: number;
  title: string;
  type: number;
  url: string;
}

/**
 * Request home page data-axiosclient request
 * @param date
 * @returns
 */
export function getHomeListAxios(date: string = "") {
  return axiosClient.get<HomeModel>({
    url: baseUrl + "api/v2/feed",
    params: { "date": date },
    showLoading: true
    // headers: { "Accept": "application/json" } as AxiosRequestHeaders
  })
}

/**
 * Get category details interface
 * @param id
 * @param start
 */
export function getCategoryDetailList(id: number, start: number) {
  return axiosClient.get<HomeModel>(
    {
      url: baseUrl + "api/v4/categories/videoList",
      params: {
        "id": id,
        "start": start,
        "udid": CommonConstants.UUID,
        deviceModel: CommonConstants.DEVICE_NUM
      }
    }
  )
}

/**
 * ObtainwanAndroidfront pageBannerdata,Test verificationAPI data:TGeneric data
 * @returns
 */
export function getWanAndroidBanner() {
  return axiosClient.get<ApiResponse<BannerDataModelData[]>>(
    {
      url: wanAndroidUrl + "/banner/json",
      checkResultCode: true,
      showLoading: true
    }
  )
}

Official @ohos/net.http Introduction

In HarmonyOS (OpenHarmony), @ohos/net.http is an officially provided basic module for performing HTTP communication. Developers can use this module to send and receive HTTP requests and responses to implement data interaction between applications and servers.

Document Center –HTTP Data Request

Official Easy Package

The @ohos/net.http module is not easy to use. The simple package is as follows:

//http.ets
/**
 * Define interface response wrapper class
 */
import http from '@ohos.net.http';

export interface BaseResponse {
  //wanAndroid-APIresponse body
  errorCode: number
  errorMsg: string
  //expandxxx-APIresponse body
}

/**
 * Interface implementation class packaging,For example, other businesses can inherit and implement it againxxxResponse
 */
export interface ApiResponse<T = any> extends BaseResponse {
  //wanAndroid-APIresponse body
  data: T | any;
  //expandxxx-APIresponse body
}

interface HttpRequestConfig extends http.HttpRequestOptions {
  showLoading?: boolean; //Whether to display the requestloading
  checkResultCode?: boolean; //Whether to check the response result code
  checkLoginState?: boolean //Verify user login status
  needJumpToLogin?: boolean //Do you need to jump to the login page?
  url?: string, //Request web link
}


/**
 * Network request constructor
 * Based on Hongmeng defaulthttpFramework implementation
 */
class HttpBuilder {
  httpClient: http.HttpRequest
  config: HttpRequestConfig

  constructor(options: HttpRequestConfig) {
    this.httpClient = http.createHttp()
    this.config = options
    this.setupInterceptor()
  }
  /**
   * Configure property interceptor
   */
  setupInterceptor() {

  }

  request<T = any>(config: HttpRequestConfig): Promise<T> {
    return new Promise<T>((resolve, reject) => {
      this.httpClient.request(
        config.url,
        config,
        (error, data) => {
          if (!error) {
            resolve(data.result as T);
          } else {
            reject(error)
          }
          // When the request is finished using,transferdestroyMethod actively destroyedx
          this.httpClient.destroy()
        }
      )
    })
  }

  get<T = any>(config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: http.RequestMethod.GET })
  }

  post<T = any>(config: HttpRequestConfig): Promise<T> {
    return this.request({ ...config, method: http.RequestMethod.POST })
  }
}

export default HttpBuilder

Official http Module Package Use

import http from '@ohos.net.http';
import showToast from '../utils/ToastUtils';
import HttpBuilder from './http';

//Interface send timeout
const READ_TIMEOUT = 100000
//Interface read timeout
const CONNECT_TIMEOUT = 100000

let baseUrl = "https://www.wanandroid.com/"

const httpClient = new HttpBuilder({
  readTimeout: READ_TIMEOUT,
  connectTimeout: CONNECT_TIMEOUT
})

//Return data structure definition
interface HomeModelIssueList {
  releaseTime: number;
  type: string;
  date: number;
  publishTime: number;
  count: number;
}

interface HomeModel {
  issueList: HomeModelIssueList[];
  itemList: HomeModelIssueList[];
  nextPageUrl: string;
  nextPublishTime: number;
  newestIssueType: string;
}

/**
 * Request data--systemhttpask
 * @param date
 * @returns
 */
export function getHomeList(date: string = "") {
  return httpClient.get<HomeModel>({
    url: baseUrl + "api/v2/feed",
    extraData: { "date": date }
  })
}