二十分钟封装,一个App前后台Http交互的实现
在React Native开发过程中,几乎所有的app都需要使用到Http请求,所以fetch的封装必不可少,由于不同app的请求参数,解析规则,token机制等完全不一样,所以在大多数App开发中,前后台Http请求的实现都是开发者自己封装的。
封装一个前后台Http请求实现需要多久?
可能有人回答是1小时,也有3、5小时甚至更长时间的,或者也有说先这样封装个大概,等到需求不满足的时候再改。
花费1小时的时间不一定短,花费3、5的时间也不一定算长,具体要看前后台交互的复杂程度与开发者对交互实现的封装程度。
那这里我们就引出了一个问题了,我们通常说的app的Http请求【封装】,到底封装的是什么,我们需要做哪些工作,能使用得app的接口请求更简单,易用且有较高的灵活性?在我看来这个“封装”主要分两个部分:
数据交换 层面的封装,即:
-
- 实现前后台的互通,支持服务器要求的数据交换类型、格式等
- 调用者可以自由设置请求的header、params等参数,程序根据不同的设置也能保证请求能正确的发送给服务端并返回相应的结果
- 支持超时、日志打印等一些基本功能
业务逻辑 层面的封装,即:
- 入参:公共部分header、params的参数处理,避免在具体接口请求是传入不必要与接口无关的参数
- 出参:对后台返回的数据按约定好的规则做一层基础解析处理,避免在具体接口数据解析的时候做一些无意义的操作
从投入的时间上来看:
第一部分基本上要花掉开发者80%以上的时间来封装
第二部分需要消耗的时间可能不足20% 【以此推算,按上面1个小时的封装时间,用在逻辑封装部分的时间也就12分钟左右😝】
我们再回头看一下,第一部分的【数据交换】封装是否涉及到具体业务逻辑呢?答案是:没有。
既然没有我们为什么不把第一部分的封装交给第三方框架呢,我们只需要做第二部分的封装多省事,有这样第三方框架么?
答案是:有的,react-native-easy-app 就可以实现【前后台数据交换】层面的封装,通过这个开源库,我们就只需要实现涉及【App业务逻辑】层面的封装即可。
为验证 react-native-easy-app 的实用性,在这里我们先来构想一个业务逻辑层面封装的需求:
-
请求接口的公共headers参数有:
- version、channelCode、model、platform (所有接口)
- accessToken、refreshToken、customerId (登录后额外增加)
-
请求接口的公共params参数有:
- customerId (登录后额外增加)
- 后台返回的数据结构示例如下:
{ data: {}, successful:1, msg: 'request msg', code: 'xxx'} -
请求状态码为503的时候表示accessToken过期,accessToken过期的情况下,需要重新获取新的accessToken并刷新因accessToken过期导致请求失败的接口
- accessToken、refreshToken在登录成功后的response的headers中返回。
对于以上业务逻辑层面的需求,看看通过 react-native-easy-app 我们可以怎么做。
XHttpConfig().initHttpLogOn(true) .initHeaderSetFunc((headers) => { headers['model'] = 'xiao mi'; headers['version'] = '1.0.0'; headers['platform'] = Platform.OS; headers['channelCode'] = 'channelOfficial'; if (isLogin()) { headers['customerId'] = RNStorage.customerId; headers['accessToken'] = RNStorage.accessToken; headers['refreshToken'] = RNStorage.refreshToken; } }) .initParamSetFunc(params => { if (isLogin()) { params['customerId'] = RNStorage.customerId; } }) .initParseDataFunc((result, request, callback) => { let {success, json, message, status, response} = result; if (status === 503) {// accessToken过期标记 this.refreshToken(request, callback); } else { let {data, successful, msg, code} = json; callback(success && successful === 1, data || {}, msg || message, code, response); } });
accessToken重新请求的实现及对失败接口的刷新:
refreshToken = (request, callback) => { if (global.hasQueryToken) { global.tokenExpiredList.push({request, callback}); } else { global.hasQueryToken = true; global.tokenExpiredList = [{request, callback}]; const refreshUrl = `${RNStorage.baseUrl}api/refreshToken?refreshToken=${RNStorage.refreshToken}`; fetch(refreshUrl).then(resp => { resp.json().then(({successful, data: {accessToken}}) => { if (successful === 1) {// 获取到新的accessToken RNStorage.accessToken = accessToken; global.tokenExpiredList.map(({request, callback}) => { request.resendRequest(request, callback); }); global.tokenExpiredList = []; } else { console.log('Token 过期,退出登录'); } }); }).catch(err => { console.log('Token 过期,退出登录'); }).finally(() => { global.hasQueryToken = false; }); } };
就这样对当前构想的app的逻辑层面的封装就实现了(实现上面的代码约70行,也许要超过20分钟 😆😝,但相较于以前从零开的封装,是不是节约了大量的时间呢?)是不是清晰明了。当然,这只是代码片段,没有实际操作,就没办法证明上面的代码实现是实际有效的。
为了演示,先用 react native init HttpTestDemo
创建一个RN项目:示例项目:HttpTestDemo 修改并删除不必要的布局或资源,结果如下:
假定有三个接口,分别为 api/login、api/userInfo 、api/refreshToken (为了省事,接口都以json文件替代)
- api/login 有两个必传参数:[userName、userPass];请求内容类型为:application/x-www-form-urlencoded;post请求
- api/userInfo 无参数;请求内容类型为:application/json;get请求
- api/refreshToken 必须参数refreshToken;请求内容类型为:application/json;get请求
https://react-native-fast-app.oss-cn-beijing.aliyuncs.com/api/login
https://react-native-fast-app.oss-cn-beijing.aliyuncs.com/api/userInfo
https://react-native-fast-app.oss-cn-beijing.aliyuncs.com/api/refreshToken
- 按 react-native-fast-app 的说明文档,安装库:
npm install react-native-fast-app --save
- 定义一个持久化对象,用于保存accessToken,customerId等参数:
export const RNStorage = {// 持久化数据列表 customerId: undefined,//客户ID accessToken: undefined,//OAuth2.0 accessToken refreshToken: undefined,//OAuth2.0 refreshToken baseUrl: undefined, userInfo: undefined, hasLogin: false, };
3.在页面的构造方法时调用 RNStorage的初始化操作;初始化完成之后,调用Http请求XHttpConfig的【业务逻辑】层初始化方法,这样就完成了,现在就可以调用接口了。
- 调用登录接口:(由于使用json文件的形式只能使用get请求)
import { XHttp } from 'react-native-easy-app'; login = () => { let params = {userName: 'zhangsan', userPass: '123456a'}; XHttp().url('api/login').param(params).formEncoded().get((success, json, message, status, resonse) => { if (success) { if (resonse.headers && resonse.headers.map) { RNStorage.accessToken = resonse.headers.map['x-oss-meta-accesstoken']; RNStorage.refreshToken = resonse.headers.map['x-oss-meta-refreshtoken']; } RNStorage.customerId = json.customerId; RNStorage.hasLogin = true; this.setState({data: JSON.stringify(json)}); } else { console.log('失败', message); } }); };
调用接口,通过框架自带的日志功能,可以看到,该拼的参数都拼接了,从header中也获取到了token
- 调用获取用户个人信息接口:
import { XHttp } from 'react-native-easy-app'; queryUserInfo = () => { XHttp().url('api/userInfo').formJson().get((success, json, message) => { if (success) { RNStorage.userInfo = json; this.setState({data: JSON.stringify(json)}); } else { console.log('失败', message); } }); };
调用接口,通过框架自带的日志功能,可以看到accessToken、refreshToken也正确的拼接了。
由于没有合适的服务器,token过期的情况就不演示了,只要请求refreshToken的接口正常请求就不会有问题。
至此一个完整的App 【业务逻辑】层面的封装就完全实现了,从Http请求的配置到,refreshToken的重新请求到刷新失败接口,一共大概只用了70行代码左右,是不是相较于之前从零开始的fetch封装简单容易多了,节约了大量的封装时间呢?
担心框架的灵活性?请参考 react-native-easy-app 详解与使用之(二) fetch 并且react-native-easy-app 开源库并不只有Http请求的封装,还有更多功能,有兴趣的同学可以查看此栏目的其它文章,你肯定会有更多收获。
当前示例项目链接:HttpTestDemo
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
一分钟实现,一个RN持久数据管理器
在React Native开发过程中,总避免不了需要存储一些数据在本地。对于大多数应用只需要存储一些结构简单的数据,如标记位,用户信息等。这时候我们首选择的存储方式就是AsyncStorage,那我们先来看下AsyncStorage给我提供了哪些基本方法: 可以看出AsyncStorage已经包含set、get、remove、clear等一系列静态方法,基本上已经满足了我们对数据增、删、改、查(CURD 下文中我都统一使用简称)的需求。对于AsyncStorage的使用,官网建议我们再封装一层,而不是直接使用AsyncStorage。 那我们先在网上搜索一下看看大家是怎么封装AsyncStorage的。通过在网上大量的搜索与对比,我把大家的封装方式分为三类: 重复造轮式这种封装方式,基本上是新建一个可导出的类,加入几个静态的CURD方法,然后在相应的静态方法里面直接调用AsyncStorage的CURD方法,其它的不做任何处理。【这种‘‘简陋’’的封装,还不如不封装】 类型转换式这种封装方式,相较于重复造轮式,增加了类型转换和异常捕获,使得AsyncStorage的数据存的类型不再局限...
- 下一篇
Android 冷门知识点汇总:你知道哪些Android中的冷门知识?
四大组件相关: 1.启动一个Activity,在应用进程至少需要两个Binder线程。 2.启动一个launchMode为singleTask的Activity,它并不一定会运行在新的Activity栈中。 3.两个不同应用的Activity,可以运行在同一个Activity栈中。 4.同一个应用进程中的所有Activity,共享一个WindowSession。 5.弹出一个AlertDialog,不一定需要Activity级别的Context,而且任何地方都有办法弹出一个AlertDialog,只要是在Application的attachBaseContext之后。 下面是一个简单的demo演示: 首先看DemoApplication,然后看Alert类: 在Application中初始化: import android.app.Application; public class DemoApplication extends Application { @Override public void onCreate() { Alert.alertAnyWhere(); super.o...
相关文章
文章评论
共有0条评论来说两句吧...