标签:生效 完整 zha 引用 pos 自己实现 请求方式 面向切面 情况
react-native-easy-app 是一款为React Native App快速开发提供基础服务的纯JS库(支持 IOS & Android),特别是在从0到1的项目搭建初期,至少可以为开发者减少30%的工作量。react-native-easy-app 主要做了这些工作:
1. 对AsyncStorage进行封装,开发者只需几行代码即可实现一个持久化数据管理器。
2. 对fetch进行封装,使得开发者只需关注当前App的前后台交互逻辑和协议,定义好参数设置及解析逻辑即可。
[3. 重新封装了RN的View、Text、Image、FlatList 使用得这些控件在适当的时候支持事件或支持icon与文本,能有效减少布局中的嵌套逻辑。]()
[4. 通过设置一个屏幕参考尺寸,重置XView、XText、XImage的尺寸,实现自动多屏适配]()
可能有人觉得,不同的App对Http请求的要求各异,第三方库怎么可能做到全面的封装,就算做到了,那也必定会 封装过度。
一千个人心中,有一千个哈姆雷特,也许我的思路能给你带来不一样的启发也未可知呢?
我们先来看下React native中文网给出的fetch使用示例:
fetch(‘https://facebook.github.io/react-native/movies.json‘)
.then((response) => response.json())
.then((responseJson) => {
return responseJson.movies;
})
.catch((error) => {
console.error(error);
});
同步请求(核心代码)
try {
// 注意这里的await语句,其所在的函数必须有async关键字声明
let response = await fetch(‘https://facebook.github.io/react-native/movies.json‘);
let responseJson = await response.json();
return responseJson.movies;
} catch (error) {
console.error(error);
}
RN平台的fetch请求很简洁,那我们再看看react-native-easy-app的请求 XHttp是不是也可以方便快捷的发送请求呢?
异步请求(核心代码) 示例 1
import { XHttp } from ‘react-native-easy-app‘;
XHttp().url(‘https://facebook.github.io/react-native/movies.json‘).execute(‘GET‘)
.then(({success, json, message, status}) => {
console.log(json.movies)
})
.catch(({message}) => {
showToast(message);
})
同步请求(核心代码)示例 2
import { XHttp } from ‘react-native-easy-app‘;
const response = await XHttp().url(‘https://facebook.github.io/react-native/movies.json‘).execute(‘GET‘);
const {success, json, message, status} = response;
console.log(json.movies)
异步请求2(核心代码)示例 3
import { XHttp } from ‘react-native-easy-app‘;
XHttp().url(‘https://facebook.github.io/react-native/movies.json‘).get((success, json, message, status)=>{
console.log(json.movies)
});
通过执行上面三段示例代码,发现输出了一致的结果(电影列表数组):
通过对比发现 XHttp 的使用与React Native平台提供的fetch很相似,其execute(‘get‘)方法返回的是一个promise对象,故也可以像fetch一样,发送同步或异步请求。另外还可以通过[method]+回调的形式发送请求。
相比原生fetch请求,XHttp 却返回了多个参数,我们打印一下示例2中的response看看里面都有啥?输出结果,格式化后如下:
通过上面的示例, react-native-easy-app 的 XHttp 可以像使用fetch一样方便快捷的发送Http请求,而且还包含请求码,错误信息,结果也被转化为了json对象,使用我们发送请求更加方便了。
但在实际的App开发中,我们Http请求框架的要求不只是能发送简单的Http请求就可以了,比如说,需要打印请求日志、设置header参数、统一处理解析逻辑,甚至可能处理返回的结构不是标准的json数据等各种需求。
我们来看看 react-native-easy-app 的 XHttp 能满足我们哪些需求:
注:上面三个示例的请求方式各有所长,下文发送请求示例的地方我都选择使用请求 示例 3 的方式举例。
XHttp().url(‘https://facebook.github.io/react-native/movies.json‘).request(‘HEAD‘, (success, json, message, status) => {
console.log(json.movies);
})
import { XHttpConfig } from ‘react-native-easy-app‘;
XHttpConfig().initTimeout(300000); //全局配置,设置所有Http请求的超时时间为30秒
XHttp().url(‘https://facebook.github.io/react-native/movies.json‘).timeout(15000) //设置当前请求超时间为15秒
.get((success, json, message, status) => {
})
XHttpConfig().initHttpLogOn(true);
XHttp().url(‘https://facebook.github.io/react-native/movies.json‘).get((success, json, message, status) => {
})
可以看出控制台打印出了详细的日志,是不是很方便?
3、现在的移动开发99%的情况下前后台交互都是使用的json格式数据,但很难保证一些特殊情况下,App不使用非标准json数据格式的Http请求。比如需要请求一些老网站或者使用一些第三方开放的老接口。这时候只需要指定返回纯文件数据即可,下面找一个返回xml格式的接口,请求看看结果:
let url = ‘http://www.webxml.com.cn/WebServices/MobileCodeWS.asmx/getDatabaseInfo‘
XHttp().url(url).pureText().get((success, text, message, status) => {
console.log(‘XML data‘, text)
})
控制台输出结果如下(通过XHttp的 pureText() 指定返回的数据以纯文本返回):
import { XHttpConfig, XHttp } from ‘react-native-easy-app‘;
XHttpConfig().initBaseUrl(‘http://www.webxml.com.cn/WebServices/‘);
XHttp().url(‘MobileCodeWS.asmx/getDatabaseInfo‘).get((success, text, message, status) => {
console.log(‘XML data‘, text)
})
import { XHttpConfig, XHttp } from ‘react-native-easy-app‘;
XHttpConfig().initHttpLogOn(true)
.initBaseUrl(‘https://facebook.github.io/‘)
.initContentType(‘multipart/form-data‘)
.initHeaderSetFunc((headers, request) => {
headers.headers_customerId = ‘headers_CustomerId001‘;
headers.headers_refreshToken = ‘headers_RefreshToken002‘;
})
.initParamSetFunc((params, request) => {
params.params_version = ‘params_version003‘;
params.params_channel_code = ‘params_channel_code004‘;
params.testChannel = ‘testChannel005‘;
});
XHttp().url(‘react-native/movies.json‘)
.header({‘Content-Type‘: ‘application/json‘, header_type: ‘header_type006‘})
.param({paramUserName: ‘paramUserName007‘, testChannel: ‘testChannel008‘})
.post((success, text, message, status) => {
})
从代码中可以看出通过XHttpConfig配置,我们设置了公共的heders、params,然后在通过XHttp发送请求时,又设置了特定的header和param的值,同时了修改了contentType的类型,并改为post请求,执行代码我们看看控制台日志内容:
通过控制台打印的日志,我们可以很清晰的看到,参数从001~008所有的参数(除了005)都能有效设置到请求当中。但为什么公共参数 params.testChannel = ‘testChannel005‘; 的设置没有生效呢,其实是因为,XHttp中的接口请求的私有参数中也设置了一个:testChannel: ‘testChannel008‘ 的参数,两者的Key相同,所以被接口私有参数给覆盖了(细心的同学也可以发现,日志中‘Content-Type‘: ‘application/json‘,contentType的类型也被覆盖了),这说明了接口的私有参数具有更高的优先级,这是合理的同时也使接口的请求更灵活方便。
返回的数据格式如下:
{
"ticker": {
"base": "BTC",
"target": "USD",
"price": "5301.78924881",
"volume": "179358.70555921",
"change": "-21.18183054"
},
"timestamp": 1584291183,
"success": true,
"error": ""
}
可以看出,接口返回的数据结构中,有三个主要字段:
以前面XHttp发送请求,接口的成功与否的判断依然是http的status来判断,显示达不到要求,请求cryptonator.com网站api数据统一解析的基本要求,那怎么自定义数据解析呢?我们试试看。
import { XHttpConfig, XHttp } from ‘react-native-easy-app‘;
XHttpConfig().initHttpLogOn(true)
.initBaseUrl(‘https://www.cryptonator.com/api/‘)
.initParseDataFunc((result, request, callback) => {
let {success, json, message, status} = result;
callback(success && json.success, json.ticker || {}, json.error || message, status);
});
XHttp().url(‘ticker/btc-usd‘).get((success, json, message, status) => {
console.log(‘success = ‘ + success);
console.log(‘json = ‘ + JSON.stringify(json));
console.log(‘message = ‘ + message);
console.log(‘status = ‘ + status);
});
我们再看下控制台输出的请求日志与Http请求打印的4个标准参数的内容:
发现没有,json对应的值就是返回的数据结构中:ticker对应的数据。其它字段并不能反映出来,因为数据刚好与默认判断条件吻合或为空。这是怎么实现的呢?
因为通过XHttpConfig的initParseDataFunc方法,我们重新定义了,接口请求返回的标准字段的值:
这样Http请求返回的参数自定义问题就解决了,这时候可能有人会说:我的app不只是请求一个后台或者还要请求第三方接口,不同的后台返回的数据结构也完全不一样,这种情况下么处理?不用担心,这种情况也是有解的:
FHttp().url(‘https://api.domainsdb.info/v1/domains/search‘)
.param({domain: ‘zhangsan‘, zone: ‘com‘})
.contentType(‘text/plain‘)
.rawData()
.get((success, json, message, status) => {
if (success) {
console.log(‘rawData‘, JSON.stringify(json))
} else {
console.log(message)
}
})
接口请求打印的日志为:
请求依然成功,各参数也没有问题,因为在发送Http请求的时候增加了一个标记rawData(),这个标记就是用于特殊处理的,标记当前Http请求需要返回原始的,不做任何解析的数据(设置此标记,会自动忽略用户自定义的数据解析方式)
假设当前App要请求三个平台:分别为SA,SB,SC,这三个平台要求不同的公共参数(包括header),且返回的数据结构也完全不一致,这时候我们可以这样处理,配置与请求都可以完全独立的实现:
import { XHttpConfig, XHttp } from ‘react-native-easy-app‘;
XHttpConfig(‘SA‘).initHttpLogOn(true) ...
XHttpConfig(‘SB‘).initHttpLogOn(true) ...
XHttpConfig(‘SC‘).initHttpLogOn(true) ...
const url = ‘https://facebook.github.io/react-native/movies.json‘;
XHttp(‘SA‘).url(url) .get((success, json, message, status) =>{
});
XHttp(‘SB‘).url(url) .get((success, json, message, status) =>{
});
XHttp(‘SC‘).url(url) .get((success, json, message, status) =>{
});
就是这么简单,配置与请求可以通过serverTag来区别,默认情况下使用同一个配置,但若指定了新的serverTag,发送Http请求时就可以通过serverTag来指定使用哪个Http请求的配置,这样同一个app里面,请求不同的服务器,以及处理不同服务器返回的数据也完全没有压力。
通过上面的例子,我们可以看出,XHttpConfig的三个公共配置方法:initHeaderSetFunc、initParamSetFunc、initParseDataFunc 是一个 面向切面的编程模式 ,这些方法还有一个共同的参数request(第二个参数)里面包含了请求的所有原始信息,因此可以有更多的想象空间,就等你去探索。
const url = ‘https://facebook.github.io/react-native/movies.json‘;
XHttp().url(url).get((success, json, message, status, response) => {
console.log(JSON.stringify(response.headers))
});
const {success, json, message, status, response} = await XHttp().url(url).execute(‘GET‘);
console.log(JSON.stringify(response.headers))
这种应用场景怎么处理呢?
XHttpConfig()
.initHttpLogOn(true)
.initBaseUrl(ApiCredit.baseUrl)
.initContentType(XHttpConst.CONTENT_TYPE_URLENCODED)
.initHeaderSetFunc((headers, request) => {
if (request.internal) {
Object.assign(headers, AuthToken.baseHeaders());//添加基础参数
headers.customerId = RNStorage.customerId;
if (RNStorage.refreshToken) {//若refreshToken不为空,则拼接
headers[‘access-token‘] = RNStorage.accessToken;
headers[‘refresh-token‘] = RNStorage.refreshToken;
}
}
})
.initParamSetFunc((params, request) => {
if (request.internal && RNStorage.customerId) {
params.CUSTOMER_ID = RNStorage.customerId;
}
}).initParseDataFunc((result, request, callback) => {
let {success, json, response, message, status} = result;
AuthToken.parseTokenRes(response);//解析token
if (status === 503) {//指定的Token过期标记
this.refreshToken(request, callback)
} else {
let {successful, msg, code} = json;
callback(success && successful === 1, selfOr(json.data, {}), selfOr(msg, message), code);
}
});
static 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;
});
}
};
在这里我就不做详细说明了直接贴代码,详细的请大家可以直接阅读源码或者参考 react-native-easy-app 库对应的 示例项目,至于原理是:在请求的时候,将初请求的方法引用保存到了request中,并命名为resendRequest,若获取到新的token之后,重新请求一遍resendRequest方法,传入原来的参数即可。
可能有同学觉得react-native-easy-app封装XHttp与XHttpConfig的方法与参数太多了,根本没办法记住,框架虽好却不便于使用,这个目前可能需要大家参考示例项目来写了(后面我会完善说明文档)。
当然大家有没有发现,在使用这些库方法的时候,代码有提示呢?那就对了。因为我为主要的方法增加了dts描述文档,所以在写代码过程中,如果不记得方法名参数直接通过代码自动提示来写就行了(自动提示在webStorm上的体验更好):
想进一步了解,请移步至 npm 或github查看 react-native-easy-app,有源码及使用示例,待大家一探究竟,欢迎朋友们 Star!
react-native-easy-app 详解与使用之(二) fetch
标签:生效 完整 zha 引用 pos 自己实现 请求方式 面向切面 情况
原文地址:https://blog.51cto.com/11339339/2501633