告别冗长代码:鸿蒙项目中的HTTP封装秘籍

406 阅读3分钟

在开发一个鸿蒙项目的时候,我们经常需要编写大量冗长的 HTTP 请求和处理代码,这些代码不仅容易出错,而且难以维护。为了解决这一问题,封装 HTTP 就是一个好的办法。

这里我总结一下我最近做的一个IT类相关新闻资讯的一个 App,关于这个 App 的 HTTP 模块统一封装。

登录模块

首先登录模块是肯定需要进行登录接口的post请求

登录逻辑处理我大致画了一个流程图:

image.png

未封装前的部分登录代码贴图如下(考虑安全关系,我这里是把接口隐藏了一部分,各位友友可以换成你们自己的接口地址)

image.png

部分登录代码如下:(可自行拷贝)

try {
    let res = await http.createHttp().request(接口地址, {
      method: http.RequestMethod.POST,
      header: { "Content-Type": "application/json" },
      extraData: {
        username: this.username,
        password: this.password
      },
      expectDataType: http.HttpDataType.OBJECT
    })

    // AlertDialog.show({ message: JSON.stringify(res.result, null, 2) })
    //   处理响应式数据
    let resDate = res.result as iResponseModel
    if (resDate.code !== 10000) {
      promptAction.showToast({ message: resDate.message })
      return
    }
    AppStorage.setOrCreate('user', resDate.data)
    router.pushUrl({
      url: "pages/Index"
    })
  }
  catch (err) {
    this.isAgree = false
  }
}

打卡组件功能实现

打卡组件功能处理逻辑我也大致画了一个流程图,如下:

image.png

获取当年当月的连续打卡数据,代码贴图如下:

image.png

完成在HdClockIn.ets中get请求,获取打卡数据并完成判断显示最终结果部分代码如下:

async getClockData() {
  try {
    let res = await http.createHttp().request("接口地址", {
      header: { "Authorization": `Bearer ${this.currentUser.token}` },
      expectDataType: http.HttpDataType.OBJECT
    })
    // 拿到结果
    // AlertDialog.show({ message: JSON.stringify(res.result, null, 2) })
    //   处理结果
    let resData = res.result as iResModel
    // AlertDialog.show({ message: resData.data.clockinNumbers.toString() })
    // AlertDialog.show({ message: JSON.stringify(resData, null, 2) })
    this.clockCount = resData.data.clockinNumbers
    // AlertDialog.show({ message: JSON.stringify(resData.data.clockins, null, 2) })
  }
  catch (err) {

  }
}

未封装前的部分登录代码贴图如下:

image.png

部分登录代码如下:(可自行拷贝)

async ClockIn() {
  try {
    let res = await http.createHttp().request('接口地址', {
      method: http.RequestMethod.POST,
      header: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${this.currentUser.token}`
      },
      expectDataType: http.HttpDataType.OBJECT
    })
    //   服务器返回post请求数据
    let resData = res.result as iResModel
    this.clockCount = resData.data.clockinNumbers

  }
  catch (err) {
    console.log('mylog', JSON.stringify(err))
  }
}

总结

由上面三个post请求和get请求可以看出,它们有很多相同的地方可以提炼出来,所以我利用http模块结合泛型方法进行统一的post请求封装和利用http模块结合泛型方法进行统一的get请求封装。

其次,利用封装好的post泛型方法和get泛型方法改造登录请求逻辑和打卡请求逻辑。并且get和post泛型方法中,增加401判断,如果code===401则跳转到登录页面。

http模块统一封装代码如下

我是把封装提炼到一个工具文件,最后导出给其他文件使用:

image.png

在request.ets文件中,利用静态的泛型方法对http模块统一封装完整代码:

import { http } from '@kit.NetworkKit'
import { iLoginUserModel, iResponseModel } from '../../../ets/models/AccountModel'
import { promptAction, router } from '@kit.ArkUI'

const Basurl: string = '接口地址'

export class HdHttp {
  static async post<T>(url: string, extraData?: object) {
    try {
      let options: http.HttpRequestOptions = {
        method: http.RequestMethod.POST,
        header: {
          "Content-Type": "application/json",
        },
        expectDataType: http.HttpDataType.OBJECT
      }
      let user = AppStorage.get('user') as iLoginUserModel
      let token = user?.token
      if (token && options.header) {
        options.header['Authorization'] = `Bearer ${token}`
      }
      if (extraData) {
        options.extraData = extraData
      }
      url = Basurl + url
      let res = await http.createHttp().request(url, options)
      let resData = res.result as iResponseModel<T>
      if (resData.code !== 10000) {
        if (resData.code === 401) {
          //   token失效重新登录
          promptAction.showToast({ message: "请重新登录" })
          router.pushUrl({ url: "pages/LoginPage" })
        }
        else {
          promptAction.showToast({ message: resData.message })
          return Promise.reject(resData.message)
        }
      }
      return resData
    }
    catch (err) {
      promptAction.showToast({ message: "网络请求错误" })
      return Promise.reject(err)
    }

  }

  // get不需要extraData,因为参数可以拼接到url上面
  static async get<T>(url: string) {
    try {
      let options: http.HttpRequestOptions = {
        header: {
          "Content-Type": "application/json"
        },
        expectDataType: http.HttpDataType.OBJECT
      }

      let user = AppStorage.get("user") as iLoginUserModel
      let token = user.token
      if (token && options.header) {
        options.header["Authorization"] = `Bearer ${token}`
      }
      url = Basurl + url
      let res = await http.createHttp().request(url, options)
      let resData = res.result as iResponseModel<T>
      if (resData.code !== 10000) {
        if (resData.code === 401) {
          //   token失效重新登录
          promptAction.showToast({ message: "请重新登录" })
          router.pushUrl({ url: "pages/LoginPage" })
        }
        else {
          promptAction.showToast({ message: resData.message })
          return Promise.reject(resData.message)
        }
      }
      return resData
    }
    catch (err) {
      promptAction.showToast({ message: "网络请求错误" })
      return Promise.reject(err)
    }
  }
}

我这里也把url提炼出来,是为了以后项目如果要修改url地址,比如,如果写了几十个url地址,我只需要修改一个地方即可。

最后利用封装好的http模块改造上面三个post请求和get请求,

如下: 首先导入封装好的http模块:

import { HdHttp } from '../utils/request';

最后使用:

let resData = await HdHttp.post<iData>('接口地址')
this.clockCount = resData.data.clockinNumbers

可以很直观看出,通过封装减少重复工作、提升开发效率。

注意:是接口返回的数据的类型,要自己定义数据类型。


本文正在参加华为鸿蒙有奖征文征文活动