HarmonyOS Next 系列之HTTP请求封装和Token持久化存储(四)
系列文章目录
HarmonyOS Next 系列之省市区弹窗选择器实现(一)
HarmonyOS Next 系列之验证码输入组件实现(二)
HarmonyOS Next 系列之底部标签栏TabBar实现(三)
HarmonyOS Next 系列之HTTP请求封装和Token持久化存储(四)
HarmonyOS Next 系列之从手机选择图片或拍照上传功能实现(五)
HarmonyOS Next 系列之可移动悬浮按钮实现(六)
HarmonyOS Next 系列之沉浸式状态实现的多种方式(七)
HarmonyOS Next系列之Echarts图表组件(折线图、柱状图、饼图等)实现(八)
HarmonyOS Next系列之地图组件(Map Kit)使用(九)
HarmonyOS Next系列之半圆环进度条实现(十)
HarmonyOS Next 系列之列表下拉刷新和触底加载更多数据实现(十一)
HarmonyOS Next系列之实现一个左右露出中间大两边小带缩放动画的轮播图(十二)
文章目录
- 系列文章目录
- 前言
- 一、实现设计
- 二、代码实现
- 1.http请求工具类request.ets
- 2.token持久化存取
- 3.页面使用
前言
HarmonyOS Next(基于API11)封装一个http请求工具类,自动拦截token失效跳转登录页,以及token持久化存取方案。
一、实现设计
- 对于接口请求我们最关心两个东西,一个是请求参数另一个是接收服务器返回的数据
(1)请求参数最常设置的有:
请求链接url、请求方式method(post,get等)、请求参数data、请求数据类型Content-Type、登录凭证token
其中token可从本地持久化读取无需传入,剩下四个可设计为动态传参
//接口入参数据类型interfaceRequestParams{url:string//请求链接method?:http.RequestMethod//请求方式data?:Object//请求额外数据headerContentType?:string//请求数据类型}(2)返回数据一般是个对象 常见固定字段有:
code:状态码 ,message:接口响应说明 ,data:返回数据
故接口返回数据类型可定义为:
//接口请求返回数据类型interfaceResponseResult{code:number//状态码message:string//处理信息data:Object|null//返回数据}ps:根据实际需要也可按需添加字段
对于返回数据code状态码一般定义:
1、200为请求成功
2、401 token失效(无效或缺失)
3、其他情况归为请求失败
所以对于接口返回数据可根据code值分三种情况处理例如:
if(code==200){//请求成功//返回数据}elseif(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
importhttpfrom'@ohos.net.http';import{BusinessError}from'@ohos.base';importpromptActionfrom'@ohos.promptAction'import{getToken}from'./index'importrouterfrom'@ohos.router';//baseURL接口域名constBASEURL:string="https://xxxxxxx.com"//登录页路由constLOGINPAGEURL='pages/common/login'//接口入参数据接口interfaceRequestParams{url:string method?:http.RequestMethod data?:Object headerContentType?:string}//接口请求返回数据类classResponseResult{code:number//状态码message:string//处理信息data:Object|null//返回数据constructor(code?:number,message?:string,data?:Object|null|undefined){this.code=code??0this.message=message??''this.data=data??null}}/** * * @param params:接口请求参数(object类型) * { * url :请求连接 * method :请求方法 * data :请求数据 * headerContentType :请求头发送的数据格式 * } * @returns Promise<ResponseResult> */exportdefaultfunctionrequest(params:RequestParams):Promise<ResponseResult>{returnnewPromise(async(resolve:(res:ResponseResult)=>void,reject:(res:ResponseResult|string|BusinessError|http.HttpResponse)=>void)=>{//请求头contentTypeletcontentType:string=params.headerContentType||'application/json'//默认提交数据类型为application/json//请求数据dataletrequestData:Object|undefined=params.data;//application/x-www-form-urlencoded类型参数处理成key&value形式if(contentType==='application/x-www-form-urlencoded'){if(typeofparams.data==='object'){requestData=Object.entries(requestDataasobject).reduce((prev:string,cur:Array<Object>)=>{return(prev&&`${prev}&`)+`${cur[0]}=${cur[1]}`},'')}}//从本地存储获取tokenlettoken:string=awaitgetToken()lethttpRequest=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){letres:ResponseResult=JSON.parse(`${data.result}`);letresponse=newResponseResult(res.code,res.message,res.data)//状态码code=200表示请求成功,状态码可根据实际接口文档修改if(res.code===200){resolve(response);}//状态码code=401表示token失效,状态码可根据实际接口文档修改elseif(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();})})}//弹窗提示constshowToast=(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
importdataPreferencesfrom'@ohos.data.preferences';............onCreate(want:Want,launchParam:AbilityConstant.LaunchParam):void{globalThis.getPreferences=()=>{letpreferences:Promise<dataPreferences.Preferences>=dataPreferences.getPreferences(this.context,"appStore")returnpreferences}}EntryAbility. onCreate周期函数内给全局变量globalThis添加getPreferences 属性方法,方便快速获取Preferences实例 ,添加到globalThis是为了后续页面开发或者工具类使用Preferences
(2)utils/index.ets 工具类
importdataPreferencesfrom'@ohos.data.preferences';//获取tokenexportconstgetToken:Function=async()=>{try{letpreferences:dataPreferences.Preferences=awaitglobalThis.getPreferences()returnpreferences.getSync('token','')}catch(e){}return''}//设置token并本地持久化存储exportconstsetToken:Function=async(value:string)=>{try{letpreferences:dataPreferences.Preferences=awaitglobalThis.getPreferences()preferences.putSync('token',encodeURIComponent(value))awaitpreferences.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
importhttpfrom'@ohos.net.http'importrequestfrom'../utils/request'classparams{storeId:string=''}//获取首页数据exportfunctiongetHomeData(data:params){returnrequest({url:"/api/store/home",method:http.RequestMethod.POST,//不传默认GETdata,headerContentType:'application/x-www-form-urlencoded'//不传默认application/json})}//其他接口exportfunctionxxxxx(data:params){returnrequest({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),'接口返回数据')}}}运行结果
