• HarmonyOS Next 系列之HTTP请求封装和Token持久化存储(四)


    系列文章目录

    HarmonyOS Next 系列之省市区弹窗选择器实现(一)
    HarmonyOS Next 系列之验证码输入组件实现(二)
    HarmonyOS Next 系列之底部标签栏TabBar实现(三)
    HarmonyOS Next 系列之HTTP请求封装和Token持久化存储(四)



    前言

    HarmonyOS Next(基于API11)封装一个http请求工具类,自动拦截token失效跳转登录页,以及token持久化存取方案。


    一、实现设计

    • 对于接口请求我们最关心两个东西,一个是请求参数另一个是接收服务器返回的数据

    (1)请求参数最常设置的有:

    请求链接url、请求方式method(post,get等)、请求参数data、请求数据类型Content-Type、登录凭证token

    其中token可从本地持久化读取无需传入,剩下四个可设计为动态传参

    //接口入参数据类型
    interface RequestParams {
      url: string //请求链接
      method?: http.RequestMethod //请求方式
      data?: Object //请求额外数据
      headerContentType?: string //请求数据类型
    }
    

    (2)返回数据一般是个对象 常见固定字段有:

    code:状态码 ,message:接口响应说明 ,data:返回数据

    故接口返回数据类型可定义为:

    //接口请求返回数据类型
    interface ResponseResult {
      code: number //状态码
      message: string //处理信息
      data: Object | null //返回数据
    }
    

    ps:根据实际需要也可按需添加字段

    对于返回数据code状态码一般定义:
    1、200为请求成功
    2、401 token失效(无效或缺失)
    3、其他情况归为请求失败

    所以对于接口返回数据可根据code值分三种情况处理例如:

    if(code==200){//请求成功
       //返回数据
    }
    else if(code==401){//token失效
     //拦截跳转到登录页
     router.replaceUrl({
          url: "/pages/login"
      })
    }
    else{//请求失败
     
    }
    

    ps:当然状态码也可根据实际定义修改

    • Token持久化存储
      为了配合登录方案实现,方便在EntryAbility使用token,我们这里选择了Preferences作为存储方案。

    最后,熟悉web开发的同学都知道web项目中习惯把接口定义放置在api文件夹下统一管理,然后在页面引入使用,再此沿用该开发习惯,方便后期维护。

    二、代码实现

    目录结构:
    在这里插入图片描述

    1.http请求工具类request.ets

    封装一个http请求工具类文件,默认导出一个请求函数返回Promise(接口返回数据)

    utils/request.ets

    import http from '@ohos.net.http';
    import { BusinessError } from '@ohos.base';
    import promptAction from '@ohos.promptAction'
    import { getToken } from './index'
    import router from '@ohos.router';
    
    //baseURL接口域名
    const BASEURL: string = "https://xxxxxxx.com"
    //登录页路由
    const LOGINPAGEURL = 'pages/common/login'
    
    //接口入参数据接口
    interface RequestParams {
      url: string
      method?: http.RequestMethod
      data?: Object
      headerContentType?: string
    }
    
    
    //接口请求返回数据类
    class ResponseResult {
      code: number //状态码
      message: string //处理信息
      data: Object | null //返回数据
    
      constructor(code?: number, message?: string, data?: Object | null | undefined) {
        this.code = code ?? 0
        this.message = message ?? ''
        this.data = data ?? null
      }
    }
    
    /**
     *
     * @param params:接口请求参数(object类型)
     * {
     *  url :请求连接
     *  method :请求方法
     *  data :请求数据
     *  headerContentType :请求头发送的数据格式
     * }
     * @returns Promise
     */
    export default function request(params: RequestParams): Promise<ResponseResult> {
      return new Promise(async (resolve: (res: ResponseResult) => void, reject: (res: ResponseResult | string | BusinessError | http.HttpResponse) => void) => {
        //请求头contentType
        let contentType: string = params.headerContentType || 'application/json' //默认提交数据类型为application/json
        //请求数据data
        let requestData: Object | undefined = params.data;
        //application/x-www-form-urlencoded类型参数处理成key&value形式
        if (contentType === 'application/x-www-form-urlencoded') {
          if (typeof params.data === 'object') {
            requestData = Object.entries(requestData as object).reduce((prev: string, cur: Array<Object>) => {
              return (prev && `${prev}&`) + `${cur[0]}=${cur[1]}`
            }, '')
          }
        }
        //从本地存储获取token
        let token: string = await getToken()
        let httpRequest = http.createHttp();
        httpRequest.request(BASEURL + params.url, {
          method: params.method ?? http.RequestMethod.GET, //默认get方法
          header: {
            'Content-Type': contentType,
            token
          },
          extraData: requestData,
          readTimeout: 30000,
          connectTimeout: 30000
        }, (err: BusinessError, data: http.HttpResponse) => {
          if (!err) {
            //请求成功
            if (data.responseCode === 200) {
              let res: ResponseResult = JSON.parse(`${data.result}`);
              let response = new ResponseResult(res.code, res.message, res.data)
              //状态码code=200表示请求成功,状态码可根据实际接口文档修改
              if (res.code === 200) {
                resolve(response);
              }
              //状态码code=401表示token失效,状态码可根据实际接口文档修改
              else if (res.code === 401) { //跳转登录页
                router.clear() //清空历史页面
                //跳转到登录页
                router.replaceUrl({
                  url: LOGINPAGEURL
                })
              }
              //其他情况接口异常
              else {
                showToast(response.message)
                reject(response);
              }
            }
            //请求失败
            else {
              showToast()
              reject(data)
            }
    
          }
          //请求失败
          else {
            showToast(err.message)
            reject(err)
          }
          // 取消订阅HTTP响应头事件
          httpRequest.off('headersReceive');
          // 当该请求使用完毕时,主动销毁该JavaScript Object。
          httpRequest.destroy();
        })
      })
    }
    
    //弹窗提示
    const showToast = (message?: string) => {
      promptAction.showToast({
        message: message || '请求出错',
        duration: 2000
      })
    }
    
    

    说明:
    (1)定义了接口前缀(域名+端口号?+通用匹配符?) BASEURL:可根据实际修改
    (2)定义了登录页面路由 LOGINPAGEURL token失效跳转使用 :可根据实际修改
    (3)函数request入参是个对象,包含如下属性

    {
         url :请求连接
         method ?:请求方法
         data ?:请求数据
         headerContentType? :请求头发送的数据格式
      }
    

    method不传默认get方式,headerContentType 不传默认application/json
    当contetn-type为 “application/x-www-form-urlencoded” , data请求参数 自动处理成key&value形式

    (4)请求结果返回Promise

      {
      code: number //状态码
      message: string //处理信息
      data: Object | null //返回数据
    }
    

    当code=200,promise返回接口数据
    当code=401 token失效自动跳转登录页,
    当code其他值表示请求失败,showToast显示接口message字段文字

    (5)请求头默认添加token数据,从本地存储读取

    2.token持久化存取

    (1)entryability/EntryAbility.ets

    import dataPreferences from '@ohos.data.preferences';
    ....
    ....
    ....
     onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
      
        globalThis.getPreferences = () => {
          let preferences: Promise<dataPreferences.Preferences> = dataPreferences.getPreferences(this.context, "appStore")
          return preferences
        }
      }
    

    EntryAbility. onCreate周期函数内给全局变量globalThis添加getPreferences 属性方法,方便快速获取Preferences实例 ,添加到globalThis是为了后续页面开发或者工具类使用Preferences

    (2)utils/index.ets 工具类

    import dataPreferences from '@ohos.data.preferences';
    //获取token
    export const getToken: Function = async () => {
    
      try {
        let preferences: dataPreferences.Preferences = await globalThis.getPreferences()
        return preferences.getSync('token', '')
      }
      catch (e) {
      }
      return ''
    }
    
    //设置token并本地持久化存储
    export const setToken: Function = async (value: string) => {
      try {
        let preferences: dataPreferences.Preferences = await globalThis.getPreferences()
        preferences.putSync('token', encodeURIComponent(value))
        await preferences.flush()
      }
      catch (e) {
        console.log(JSON.stringify(e), 'e')
      }
    }
    

    在工具类index.ets封装2个方法(getToken,setToken),分别为获取token值和设置token值,其中setToken在登录成功获取到token值时候调用存入本地持久化

    3.页面使用

    在这里插入图片描述
    新建api文件夹、新建与页面同名的ets文件写入api定义
    api/home.ets

    
    import http from '@ohos.net.http'
    import request from '../utils/request'
    
    class params{
        storeId:string=''
    }
    //获取首页数据
    export function getHomeData(data:params){
        return request({
            url:"/api/store/home",
            method:http.RequestMethod.POST, //不传默认GET
            data,
            headerContentType:'application/x-www-form-urlencoded' //不传默认application/json
        })
    }
    
    
    
    //其他接口
    export function xxxxx(data:params){
        return request({
            url:"xxxxxxxx",
            data,
        })
    }
    ......
    ......
    ......
    
    

    页面引入
    pages/Home.ets

    import {getHomeData} from "../api/home"
    @Entry
    @Component
    struct Home{
         aboutToAppear(): void {
             getHomeData({
              storeId:'17815455885'
            }).then(res=>{
               console.log(JSON.stringify(res),'接口返回数据')
            }
         }
    }
    

    运行结果
    在这里插入图片描述

  • 相关阅读:
    麒麟信安携手河南IT联盟召开 《麒麟信安信创应用解决方案》线上分享会
    C++中的继承
    [附源码]SSM计算机毕业设计在线文献查阅系统JAVA
    29_ue4进阶末日生存游戏开发[准备道具]
    11.30排序
    Feign(黑马程序员)
    mysql给所有数据库表加字段
    负载均衡取消后的记录
    “穷人”才去大厂卷?
    好用又有趣的软件分享,走过路过不要错过
  • 原文地址:https://blog.csdn.net/sd1sd2/article/details/139536268