鸿蒙面试真题:如何在鸿蒙中封装一套网络请求框架?如何处理Http请求?

174 阅读9分钟

一、封装http请求

官方文档里的http开发指南比较全面,下面我来讲解主要的步骤。

1、创建resultVO接口

前端发送请求后,后端返回的数据一般是如下格式:code--响应状态码;msg--响应信息;data--响应数据。响应数据data一般有多种类型,这里用泛型T接收,外部调用时,再指定T的类型。ArkTS是TypeScipt的超集,一般TS有的,ArkTS也有。所以ArkTS也支持泛型。

/*
  * resultVO接口
  * @code 响应码
  * @msg  响应信息
  * @data 响应数据
 */
export default interface resultVO<T>{
  code: number
  msg: string
  data: T
}

2、创建requestParams接口

此接口用于规范调用http请求时的参数。

import { http } from '@kit.NetworkKit'
 
/*
  * requestParams接口
  * @url          必填                     访问地址  (后端api访问地址)
  * @method       选填(默认get)           请求方法  (get、post...)
  * @extraData    选填(默认undefined )    请求参数
 */
export interface requestParams{
 
  url: string
  method?: http.RequestMethod
  extraData?: object
 
}

3、创建request文件,封装http请求

3.1 编写request方法

request方法需传入请求参数(requestParams接口),并返回一个Promise对象,该对象里面是resultVO类型的数据。

/*
  * request方法
  * @params requestParams接口类型
  * @@return Promise<resultVO<T>>
 */
export const request = async <T>(params: requestParams): Promise<resultVO<T>> => {
  
  /*
    * @resolve 请求成功
    * @reject  请求失败
   */
  return new Promise(async (resolve, reject) => {
    
 
  })
 
}

3.2 返回Promise对象前,先获取一下关键参数

如果是本地模拟器访问本地的SpringBoot服务器,基地址为你所使用的电脑,它的IPv4地址。移动端项目和Vue项目不同,Vue项目的基地址一般是127.0.0.1,这个需要注意。我们这里把基地址设置成一个常量。要修改是,去常量类里面修改,提高代码的维护性。

token可以使用首选项进行存储和调用。首选项的知识可以看我的另外一篇文章:封装用户首选项

请求头一般是json类型,如有其他需求,可以更改。这个一般是和后端协调决定。如果自己做课设或者毕设,这里传一个token进来就可以了。

  // 基地址。如果用本地模拟器访问,基地址一般为 “http://本机的IPv4地址:8080”
  const baseURL: string = CommonConstants.BASE_URL
 
  // 创建一个HttpRequest对象 -- 不可复用,一个httpRequest对象对应一个http请求
  let httpRequest = http.createHttp()
 
  // 获取token
  let token: preferences.ValueType = await PreferenceUtil.getPreferenceValue(
    CommonConstants.TOKEN, CommonConstants.NULL_TOKEN
  )
 
  // 完整请求地址
  let requestUrl = baseURL + params.url
 
  // 发起请求可选参数的类型和取值范围。
  let options: http.HttpRequestOptions = {
 
    // 请求类型,不传默认get
    method: params.method ?? http.RequestMethod.GET,
 
    // 请求头
    // 做课设、毕设之类的,'Content-Type'默认'application/json'就好
    // 请求头只用携带一个token,用于身份的校验
    header: {
      'Content-Type': 'application/json',
      token
    },
 
    // 请求参数,不传默认undefined
    extraData: params.extraData ?? undefined,
 
    // 必填,不知道是不是bug,不填object类型,就会拿不到后端数据,和官方文档不一样
    // 也可能是我后端接口的问题。----------这里存疑----------
    expectDataType: http.HttpDataType.OBJECT
  }

3.3 发送request请求

    获取所需的参数之后,我们就可以发起request请求了,具体如下。请求成功后,记得销毁httpRequest对象,释放内存。
  /*
    * @resolve 请求成功
    * @reject  请求失败
   */
  return new Promise(async (resolve, reject) => {
 
    /*
      * 发送http请求
      * @url          后端api完整地址
      * @options      发起请求的可选参数
      * @callback     回调函数
     */
    httpRequest.request(
 
      requestUrl,
 
      options,
 
      (err, data) => {
 
        if (!err) {
 
          // 处理请求成功的逻辑
 
          // 获得后端返回过来的resultVO
          const res = data.result as resultVO<T>
 
        } else {
 
          // 处理请求失败的逻辑
 
          promptAction.showToast({message: CommonConstants.REQUEST_ERROR})
 
          console.error('error:' + JSON.stringify(err))
          reject(err)
 
        }
 
        // 销毁httpRequest对象
        httpRequest.destroy()
      }
 
    )
 
  })

3.4 根据业务需要处理请求后的逻辑

例1:拦截token过期

          // token过期
          if(res.code == CommonConstants.TOKEN_INVALIDATION){
 
            promptAction.showToast({message: CommonConstants.TOKEN_INVALIDATION_MSG})
 
            //清空历史页面
            router.clear()
            //跳转到登录页
            router.replaceUrl({
              url: CommonConstants.LOGIN_PAGE_URL
            })
 
            return
          }

例2:处理正确状态码

          // 判断响应码
          if(res.code == CommonConstants.CODE_SUCCESS){
 
            // 返回想要的的结果
            resolve(res)
 
          }else {
 
            promptAction.showToast({message: res.msg})
            return
          }

DD一下:欢迎大家关注工粽号<程序猿百晓生>,可以了解到以下知识点。

`欢迎大家关注工粽号<程序猿百晓生>,可以了解到以下知识点。`
1.OpenHarmony开发基础
2.OpenHarmony北向开发环境搭建
3.鸿蒙南向开发环境的搭建
4.鸿蒙生态应用开发白皮书V2.0 & V3.0
5.鸿蒙开发面试真题(含参考答案) 
6.TypeScript入门学习手册
7.OpenHarmony 经典面试题(含参考答案)
8.OpenHarmony设备开发入门【最新版】
9.沉浸式剖析OpenHarmony源代码
10.系统定制指南
11.【OpenHarmony】Uboot 驱动加载流程
12.OpenHarmony构建系统--GN与子系统、部件、模块详解
13.ohos开机init启动流程
14.鸿蒙版性能优化指南
.......

二、完整文件代码

文件1:常量类

export class CommonConstants {
  
  static readonly BASE_URL: string = 'http://192.168.31.160:8080'
 
  // global data key
  static readonly G_STORE: string = 'GreenTreasureStore';
 
  static readonly TOKEN: string = 'TOKEN'
 
  static readonly NULL_TOKEN: string = null
 
  static readonly TOKEN_INVALIDATION : number = 401
 
  static readonly CODE_SUCCESS : number = 1
 
  static readonly TOKEN_INVALIDATION_MSG : string = 'token过期,请重新登录'
 
  static readonly REQUEST_ERROR : string = '请求失败'
 
  static readonly LOGIN_PAGE_URL : string = '/pages/Login/Index'
 
}

文件2:resultVO接口

/*
  * resultVO接口
  * @code 响应码
  * @msg  响应信息
  * @data 响应数据
 */
export default interface resultVO<T>{
  code: number
  msg: string
  data: T
}

文件3:requestParams接口

import { http } from '@kit.NetworkKit'
 
/*
  * requestParams接口
  * @url          必填                     访问地址  (后端api访问地址)
  * @method       选填(默认get)           请求方法  (get、post...)
  * @extraData    选填(默认undefined )    请求参数
 */
export interface requestParams{
 
  url: string
  method?: http.RequestMethod
  extraData?: object
 
}

文件4:request文件

import { http } from '@kit.NetworkKit'
import { preferences } from '@kit.ArkData'
import PreferenceUtil from '../utils/PreferencesUtil'
import resultVO from '../common/impl/resultVO'
import { CommonConstants } from '../constants/CommonConstants'
import { requestParams } from '../common/impl/requestParams'
import { promptAction, router } from '@kit.ArkUI'
 
/*
  * request方法
  * @params requestParams接口类型
  * @@return Promise<resultVO<T>>
 */
export const request = async <T>(params: requestParams): Promise<resultVO<T>> => {
 
  // 基地址。如果用本地模拟器访问,基地址一般为 “http://本机的IPv4地址:8080”
  const baseURL: string = CommonConstants.BASE_URL
 
  // 创建一个HttpRequest对象 -- 不可复用,一个httpRequest对象对应一个http请求
  let httpRequest = http.createHttp()
 
  // 获取token
  let token: preferences.ValueType = await PreferenceUtil.getPreferenceValue(
    CommonConstants.TOKEN, CommonConstants.NULL_TOKEN
  )
 
  // 完整请求地址
  let requestUrl = baseURL + params.url
 
  // 发起请求可选参数的类型和取值范围。
  let options: http.HttpRequestOptions = {
 
    // 请求类型,不传默认get
    method: params.method ?? http.RequestMethod.GET,
 
    // 请求头
    // 做课设、毕设之类的,'Content-Type'默认'application/json'就好
    // 请求头只用携带一个token,用于身份的校验
    header: {
      'Content-Type': 'application/json',
      token
    },
 
    // 请求参数,不传默认undefined
    extraData: params.extraData ?? undefined,
 
    // 必填,不知道是不是bug,不填object类型,就会拿不到后端数据,和官方文档不一样
    // 也可能是我后端接口的问题。----------这里存疑----------
    expectDataType: http.HttpDataType.OBJECT
  }
 
  /*
    * @resolve 请求成功
    * @reject  请求失败
   */
  return new Promise(async (resolve, reject) => {
 
    /*
      * 发送http请求
      * @url          后端api完整地址
      * @options      发起请求的可选参数
      * @callback     回调函数
     */
    httpRequest.request(
 
      requestUrl,
      options,
      (err, data) => {
 
        if (!err) {
 
          // 处理请求成功的逻辑
 
          console.info('httpRequest','code:' + JSON.stringify(data.result['code']))
          console.info('httpRequest','msg:' + JSON.stringify(data.result['msg']))
          console.info('httpRequest','data:' + JSON.stringify(data.result['data']))
 
          // 获得后端返回过来的resultVO
          const res = data.result as resultVO<T>
 
          // token过期
          if(res.code == CommonConstants.TOKEN_INVALIDATION){
 
            promptAction.showToast({message: CommonConstants.TOKEN_INVALIDATION_MSG})
 
            //清空历史页面
            router.clear()
            //跳转到登录页
            router.replaceUrl({
              url: CommonConstants.LOGIN_PAGE_URL
            })
 
            return
          }
 
          // 判断响应码
          if(res.code == CommonConstants.CODE_SUCCESS){
 
            // 返回想要的的结果
            resolve(res)
 
          }else {
            
            promptAction.showToast({message: res.msg})
            return
          }
 
        } else {
 
          // 处理请求失败的逻辑
 
          promptAction.showToast({message: CommonConstants.REQUEST_ERROR})
 
          console.error('error:' + JSON.stringify(err))
          reject(err)
 
        }
 
        // 销毁httpRequest对象
        httpRequest.destroy()
 
      }
    )
  })
}

三、发送request请求

1、开启网络权限

想要发送http请求,必须开启网络权限。网络权限开启步骤如下:

1.1 找到文件entry/src/main/module.json5

1.2 进入文件module.json5,在【module】的下一级,加上:

    "requestPermissions":[
      {
        "name" : "ohos.permission.GET_NETWORK_INFO",
      },
      {
        "name" : "ohos.permission.INTERNET",
      }
    ],

2、测试登录请求

    可以自己找一些后端接口进行测试。这里是笔者自己写的后端接口。后端接口的编写本文暂时不讲。读者可以自己试着写写看。

2.1 创建LoginDTO接口

该接口规范登录请求的参数

export interface  LoginDTO{
 
  userPhone: string
  password: string
 
}

2.2 测试

import { LoginDTO } from '../common/dto/LoginDTO'
import { request } from '../utils/request';
import { http } from '@kit.NetworkKit';
 
const login = (loginDto: LoginDTO) => {
  return request<string>({
    url: `/user/api/login?userPhone=${loginDto.userPhone}&password=${loginDto.password}`,
    method: http.RequestMethod.POST,
  })
}
 
@Entry
@Component
struct Index {
 
  async aboutToAppear() {
 
    const res = await login({userPhone: '13652576888',password:'123456'})
  }
 
  build() {
  }
}

2.3 结果

在鸿蒙HarmonyOS应用开发中,网络请求是不可或缺的一部分。HarmonyOS提供了http模块来处理HTTP请求,并且支持使用Promise和异步回调来处理请求结果,使得网络请求的处理更加简洁和高效。

DD一下:欢迎大家关注工粽号<程序猿百晓生>,可以了解到以下知识点。

`欢迎大家关注工粽号<程序猿百晓生>,可以了解到以下知识点。`
1.OpenHarmony开发基础
2.OpenHarmony北向开发环境搭建
3.鸿蒙南向开发环境的搭建
4.鸿蒙生态应用开发白皮书V2.0 & V3.0
5.鸿蒙开发面试真题(含参考答案) 
6.TypeScript入门学习手册
7.OpenHarmony 经典面试题(含参考答案)
8.OpenHarmony设备开发入门【最新版】
9.沉浸式剖析OpenHarmony源代码
10.系统定制指南
11.【OpenHarmony】Uboot 驱动加载流程
12.OpenHarmony构建系统--GN与子系统、部件、模块详解
13.ohos开机init启动流程
14.鸿蒙版性能优化指南
.......

Promise与异步回调

Promise是JavaScript中用于处理异步操作的一种对象,它可以代表一个异步操作的最终完成或失败,并可以链式调用,使得异步代码的编写更加清晰。在HarmonyOS中,我们可以使用Promise来处理http模块的异步回调。

使用Promise处理Http请求

下面是一个使用Promise处理HTTP请求的示例代码:

async send(): Promise<http.HttpResponse> {
  return new Promise<http.HttpResponse>((resolve, reject) => {
    this.httpRequest.request("http://www.baidu.com",
      {
        method: http.RequestMethod.GET,
        header: {
          'Content-Type': 'application/json'
        },
        expectDataType: http.HttpDataType.STRING,
        usingCache: true,
        priority: 1,
        connectTimeout: 60000,
        readTimeout: 60000,
        usingProtocol: http.HttpProtocol.HTTP1_1
      }, (err, data) => {
      if (!err) {
        this.httpRequest.off('headersReceive');
        this.httpRequest.destroy();
        resolve(data);
      } else {
        this.httpRequest.off('headersReceive');
        this.httpRequest.destroy();
        reject(resolve(data));
      }
    });
  });
}

在这个示例中,我们定义了一个send方法,它返回一个Promise对象。在Promise的构造函数中,我们发起了HTTP请求,并在回调函数中处理请求结果。如果请求成功,我们取消订阅HTTP响应头事件,销毁请求对象,并调用resolve方法传递数据;如果请求失败,我们同样取消订阅事件和销毁对象,并调用reject方法传递错误信息。

使用then和catch处理结果

一旦我们有了返回Promise的异步操作,我们就可以使用then和catch方法来处理结果。下面是如何使用then和catch来处理send方法的结果:

http.send().then((data: http.HttpResponse) => {
  console.log("Received data:", data.result.toString());
}).catch((error: string) => {
  console.error("Error occurred:", error);
});

在这个代码片段中,我们调用了send方法,并使用then方法来处理成功的结果,使用catch方法来处理失败的情况。这样,我们就可以优雅地处理网络请求的各种情况,而不需要嵌套的回调函数。

注意事项

  • 在使用Promise时,确保正确处理异步操作的成功和失败,避免未处理的Promise导致的问题。
  • 在请求完成后,记得取消订阅相关事件并销毁请求对象,以释放资源。
  • 根据实际业务需求,合理设置请求的超时时间和其他参数。

结语

通过使用Promise和异步回调,我们可以更加优雅地处理HarmonyOS中的HTTP请求。这种方式不仅提高了代码的可读性,还使得错误处理更加直观。在实际开发中,合理运用Promise可以大大提升开发效率和代码质量。