├── .gitignore ├── index.js ├── test.js ├── package.json ├── model.js ├── iointer.js ├── ioconfig.js ├── io.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import Io from './io'; 2 | import IoConfig from './ioconfig'; 3 | 4 | exports.Io = Io; 5 | exports.IoConfig = IoConfig; 6 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | const Model from './model'; 2 | 3 | Model.listdata({ 4 | data: { 5 | username: 'zmr', 6 | sex: '女' 7 | }, 8 | success: function(list){ 9 | console.log(list); 10 | }, 11 | complete: function(){ 12 | console.log('io complete'); 13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-io-fetch", 3 | "version": "1.0.4", 4 | "description": "react native fetch io api", 5 | "main": "index.js", 6 | "scripts": { 7 | "interstart": "node iointer.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/zmrdlb/react-native-io-fetch.git" 12 | }, 13 | "keywords": ["fetch","react native"], 14 | "author": { 15 | "name": "zmrdlb", 16 | "email": "592044573@qq.com" 17 | }, 18 | "license": "ISC", 19 | "bugs": { 20 | "url": "https://github.com/zmrdlb/react-native-io-fetch/issues" 21 | }, 22 | "homepage": "https://github.com/zmrdlb/react-native-io-fetch#readme", 23 | "dependencies": { 24 | "extend": "^3.0.0", 25 | "multiparty": "^4.1.2", 26 | "querystring": "^0.2.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /model.js: -------------------------------------------------------------------------------- 1 | const {IoConfig,Io} = require('react-native-io-fetch'); 2 | const extend = require('extend'); 3 | 4 | /** 5 | * 设置自己的配置 6 | */ 7 | 8 | /** 9 | * 业务错误条件配置 10 | * @param {[type]} result [description] 11 | * @return {[type]} [description] 12 | */ 13 | IoConfig.fail.filter = function(result){ 14 | if(result.code != 'A0001'){ 15 | return true; //说明发生了业务错误 16 | }else{ 17 | return false; 18 | } 19 | } 20 | 21 | /** 22 | * io请求发送前执行 23 | * @return {[type]} [description] 24 | */ 25 | IoConfig.ioparams.beforeSend = function(){ 26 | console.log('请求开始'); 27 | } 28 | 29 | /** 30 | * io请求结束后 31 | */ 32 | IoConfig.ioparams.complete = function(){ 33 | console.log('请求结束') 34 | } 35 | 36 | /** 37 | * 网络错误或者系统错误 38 | * @param {[type]} error [description] 39 | * @return {[type]} [description] 40 | */ 41 | IoConfig.ioparams.error = function(error){ 42 | //error或有或无 error.message 43 | console.log(error.message || '亲,忙不过来了'); 44 | } 45 | 46 | /** 47 | * 业务错误处理 48 | * @param {[type]} result [description] 49 | * @param {[type]} response [description] 50 | * @return {[type]} [description] 51 | */ 52 | IoConfig.ioparams.fail = function(result,response){ 53 | if(result.code == 'A0002'){ 54 | console.log('未登录'); 55 | }else{ 56 | console.log(result.errmsg || '亲,忙不过来了'); 57 | } 58 | } 59 | 60 | /** 61 | * 调用以下方法的时候,opt如ioparams。但是一般只传以下参数就可以了: 62 | * data success 63 | * 以下方法已经统一处理了,如果想覆盖自行传入 64 | * beforeSend error fail complete 65 | */ 66 | module.exports = { 67 | //listdata接口 68 | listdata(opt){ 69 | Io.request(extend(true,{ 70 | request: { 71 | method: 'POST' 72 | }, 73 | url: 'http://127.0.0.1:8000/listdata' 74 | },opt)); 75 | } 76 | }; 77 | -------------------------------------------------------------------------------- /iointer.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const url = require('url'); 3 | const querystring = require('querystring'); 4 | const multiparty = require('multiparty'); 5 | 6 | var requestCount = 0; 7 | 8 | function writeHead(res){ 9 | res.setHeader('Charset','utf-8'); 10 | res.setHeader('Access-Control-Allow-Origin','*'); 11 | res.setHeader('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE'); 12 | res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); 13 | } 14 | 15 | function writeempty(res) { 16 | writeHead(res); 17 | res.setHeader('Content-Type','text/plain'); 18 | res.writeHead(404,'not found'); 19 | res.end(''); 20 | } 21 | 22 | function writeres(res){ 23 | var dataBlob = [], rowIndex = 0; 24 | for(var i = 0; i < 10; i++){ 25 | dataBlob.push({ 26 | txt: 'row data '+ rowIndex, 27 | imgsrc: 'https://facebook.github.io/react/img/logo_og.png' 28 | }); 29 | rowIndex++; 30 | } 31 | writeHead(res); 32 | res.setHeader('Content-Type','application/json'); 33 | res.writeHead(200, 'ok'); 34 | res.end(JSON.stringify({ 35 | code: 'A0001', 36 | data: dataBlob, 37 | errmsg: '接口返回的错误信息' 38 | }),'utf8'); 39 | } 40 | 41 | http.createServer((req,res) => { 42 | var urlobj = url.parse(req.url); 43 | var pathname = urlobj.pathname; 44 | // 45 | // console.log(urlobj.query); 46 | 47 | switch(pathname){ 48 | case '/listdata': 49 | if(req.method.toUpperCase() == 'POST'){ 50 | requestCount++; 51 | console.log(`第${requestCount}次请求`); 52 | 53 | var contentType = req.headers['content-type']; 54 | console.log(req.headers); 55 | if(/multipart\/form-data/.test(contentType)){ //说明是FormData 56 | console.log('multipart/form-data'); 57 | var form = new multiparty.Form(); 58 | form.parse(req, (err,fields,files) => { 59 | console.log(fields); 60 | }); 61 | writeres(res); 62 | }else if(/application\/x-www-form-urlencoded/.test(contentType)){ //application/x-www-form-urlencoded 63 | console.log('application/x-www-form-urlencoded'); 64 | var postdata = ''; 65 | req.on('data',(data) => { 66 | postdata += data; 67 | }); 68 | req.on('end',() => { 69 | var argsobj = querystring.parse(postdata); 70 | console.log(argsobj); 71 | writeres(res); 72 | }); 73 | }else{ 74 | writeempty(res); 75 | } 76 | 77 | }else{ 78 | writeempty(res); 79 | } 80 | break; 81 | default: 82 | writeempty(res); 83 | break; 84 | 85 | } 86 | }).listen(8000); 87 | -------------------------------------------------------------------------------- /ioconfig.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview io请求的一些公共配置 3 | */ 4 | const that = { 5 | /** 6 | * 对于接口返回的业务错误进行统一处理 7 | * @type {Object} 8 | */ 9 | fail: { 10 | funname: 'fail', //当发生业务错误的时候,调用的方法名 11 | filter: function(result) { 12 | // if(result.code != 'A0001'){ 13 | // return true; //说明发生了业务错误 14 | // }else{ 15 | // return false; 16 | // } 17 | return false; 18 | } 19 | }, 20 | 21 | /** 22 | * 请求头部配置 23 | * @type {Object} 24 | */ 25 | headers: { 26 | //如果Content-Type设置为false,则不传Content-Type 27 | 'Content-Type': 'application/x-www-form-urlencoded' 28 | }, 29 | /** 30 | * 请求对象参数配置 31 | * @type {Object} 32 | */ 33 | request: { 34 | method: 'GET', //GET|POST 35 | mode: 'cors', //cors|no-cors|same-origin|navigate 36 | //其他参数 37 | //body: credentials: cache: redirect: referrer: integrity 38 | /** 39 | * same-origin: 同ajax一样,同域发送cookie 40 | * include: 跨域发送cookie 41 | * @type {String} 42 | */ 43 | credentials: 'same-origin' 44 | } 45 | }; 46 | 47 | /** 48 | * 调用io组件,传入的参数格式 49 | * @type {Object} 50 | */ 51 | that.ioparams = { 52 | headers: that.headers, //同headers 53 | request: that.request, //同request 54 | /** 55 | * 请求参数。可以是以下几种类型: 56 | * Bolb 57 | * BufferSource 58 | * FormData 59 | * URLSearchParams 60 | * USVString 61 | * String 62 | * JSON: 如果是json, 则做特殊处理,请见下面isformdata的说明 63 | */ 64 | // data: {}, 65 | /** 66 | * 如果data是json: 67 | * 1. request.method不是GET或HEAD, 且isformdata为true, 那么将data转换成FormData格式 68 | * 2. 如果不符合第1种,将data转换成querystring 69 | * @type {Boolean} 70 | */ 71 | isformdata: false, 72 | url: '', //请求url地址 73 | /** 74 | * 请求的数据类型,默认为json. 数据类型和reponse对象返回获取结果的方法对应关系如下 75 | * arrayBuffer: response.arrayBuffer 76 | * blob: response.blob 77 | * formData: response.formData, 78 | * json: response.json, 79 | * text: response.text 80 | * @type {String} 81 | */ 82 | type: 'json', 83 | timeout: 6000, 84 | /** 85 | * io请求前,统一的处理 86 | * @return {[type]} [description] 87 | */ 88 | beforeSend: function(){ 89 | 90 | }, 91 | /** 92 | * 对于接口返回错误,一般因为网络原因,进行的统一处理 93 | */ 94 | error: function(error){ 95 | //error或有或无 error.message 96 | //Alert.alert('系统消息',error.message || '亲,忙不过来了'); 97 | }, 98 | /** 99 | * 如果fail配置了funname为fail,则调用此方法. 此时fail.filter返回true 100 | * @param {Object|Other} result 接口返回数据 101 | * @param {Response} response 返回的response对象 102 | * @return {[type]} [description] 103 | */ 104 | fail: function(result,response){ 105 | //Alert.alert('系统消息',result.errmsg || '亲,忙不过来了'); 106 | }, 107 | /** 108 | * 成功调用方法。调用的情况有如下几种: 109 | * 1. dealfail为true, 则fail.filter返回false时,调用success 110 | * 此时如果dealdata为true, 则result为dealdatafun返回的数据 111 | * 2. dealfail为false时,则接口返回后直接调用此方法(不发生error的情况下) 112 | * 113 | * @param {Object|Other} result 接口返回数据 114 | * @param {Response} response 返回的response对象 115 | */ 116 | success: function(result,response){}, 117 | /** 118 | * 接口请求完毕调用。无论success,fail,error 119 | * @return {[type]} [description] 120 | */ 121 | complete: function(){}, 122 | /** 123 | * 如果dealdata为true, 则success的result为此方法返回的数据 124 | * @param {Object|Other} result 接口返回数据 125 | * @return {[type]} [description] 126 | */ 127 | dealdatafun: function(result){return result.data}, 128 | /** 129 | * 是否统一处理业务错误 130 | * @type {Boolean} 131 | */ 132 | dealfail: true, //是否统一处理业务错误 133 | /** 134 | * 当业务成功时,调用success前,是否统一格式化数据 135 | * 如果dealfail为true,并且fail.filter返回为false时,如果此项设置为true,则调用dealdatafun方法,返回处理后的数据 136 | * @type {Boolean} 137 | */ 138 | dealdata: true 139 | }; 140 | 141 | export default that; 142 | -------------------------------------------------------------------------------- /io.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview io请求总a 3 | */ 4 | import IoConfig from './ioconfig'; 5 | const extend = require('extend'); 6 | const querystring = require('querystring'); 7 | 8 | /** 9 | * 将data格式化成FormData 10 | * @param {JSON} data [description] 11 | * @return {FormData} [description] 12 | */ 13 | function formatFormData(data){ 14 | var _formdata = new FormData(); 15 | data = Object.entries(data); 16 | for(var pair of data){ 17 | var [key, val] = pair; 18 | if(val == undefined){ 19 | continue; 20 | }else if(val.constructor == Array){ 21 | val.forEach((v,i) => { 22 | _formdata.append(key,v); 23 | }); 24 | continue; 25 | }else{ 26 | _formdata.append(key,val); 27 | } 28 | } 29 | return _formdata; 30 | } 31 | 32 | export default { 33 | /** 34 | * 发起io请求 35 | * @param {JSON} ioparams 同ioconfig.ioparams 36 | * @return {[type]} [description] 37 | */ 38 | request: function(ioparams) { 39 | if(ioparams.url == ''){ 40 | throw new Error('io参数url不能为空'); 41 | return; 42 | } 43 | var conf = {}; 44 | 45 | extend(true,conf,IoConfig.ioparams,ioparams); 46 | 47 | conf.request.method = conf.request.method.toUpperCase(); 48 | 49 | //检测ioparams里的data 50 | var body = conf.data, _method = conf.request.method; 51 | 52 | if(body && body.constructor === Object){ //说明data是json 53 | if(_method != 'GET' && _method != 'HEAD' && conf.isformdata){ 54 | body = formatFormData(body); 55 | delete conf.headers['Content-Type']; 56 | }else{ 57 | body = querystring.stringify(body); 58 | } 59 | } 60 | 61 | if(conf.headers['Content-Type'] === false){ 62 | delete conf.headers['Content-Type']; 63 | } 64 | 65 | //赋值request.body 66 | if(body){ 67 | switch(_method){ 68 | case 'GET': 69 | if(typeof body == 'string'){ 70 | conf.url += '?'+body.toString(); 71 | } 72 | break; 73 | case 'HEAD': 74 | break; 75 | default: 76 | conf.request.body = body; 77 | break; 78 | } 79 | } 80 | 81 | //发起请求 82 | conf.request.headers = conf.headers; 83 | var myrequest = new Request(conf.url,conf.request); 84 | 85 | //请求发起前统一处理 86 | conf.beforeSend(); 87 | 88 | var race = Promise.race([ 89 | fetch(myrequest), 90 | new Promise(function(resolve,reject){ 91 | setTimeout(reject,conf.timeout,new Error('请求超时')); 92 | }) 93 | ]); 94 | race.then(function(response){ 95 | if(response.ok) { //response.status [200,299] 96 | response[conf.type]().then(function(result){ 97 | if(conf.dealfail){ //处理业务错误 98 | if(IoConfig.fail.filter(result)){ //有业务错误发生 99 | conf[IoConfig.fail.funname](result,response); 100 | }else{ //无业务错误发生 101 | if(conf.dealdata){ 102 | conf.success(conf.dealdatafun(result),response); 103 | }else{ 104 | conf.success(result,response); 105 | } 106 | } 107 | }else{ 108 | conf.success(result,response); 109 | } 110 | },function(error){ 111 | throw error; 112 | }); 113 | }else{ 114 | var error = new Error(response.statusText || '网络错误') 115 | throw error; 116 | } 117 | conf.complete(); 118 | }).catch(function(error){ 119 | //捕获任何错误,即发生语法错误也会捕获 120 | conf.error(error); 121 | conf.complete(); 122 | }); 123 | } 124 | }; 125 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-io-fetch 2 | 学习react-native的时候,知道了[fetch api](https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API),新的资源获取语法,比XmlHttpRequest具有更强大的功能:易读性、抽象性、简洁性、支持各种类型资源请求等。 3 | 现在对于fetch的使用,封装了一层,提取了便于开发者配置和使用的api. 4 | 5 | [using fetch](https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch) 6 | 7 | # 注意 8 | 此包是针对react-native开发的,而且node现在还不支持某些新的web api, 如Request对象,所以请下载到react native项目中使用测试。 9 | 10 | # 安装 11 | npm install react-native-io-fetch --save 12 | 13 | # 使用 14 | 15 | - 将react-native-io-fetch中的model.js拷贝在react-native项目里,此处举例存放路径为:common/model.js 16 | 17 | - 将model.js里面的const {IoConfig,Io} = require('./index');改成const {IoConfig,Io} = require('react-native-io-fetch'); 18 | 19 | - 将test.js里面的代码,放在react-native中的某处引用,注意修改里面model.js的引用路径 20 | 21 | - 切换到目录react-native-io-fetch下,运行npm run interstart, 开启node接口模拟 22 | 23 | - 运行react-native项目,执行里test.js中拷贝的代码,查看结果。 24 | 25 | 26 | # API说明 27 | 28 | ## IoConfig 29 | 30 | io接口请求配置:此处封装了io请求默认配置 31 | 32 | - Ioconfig.ioparams: 'io请求参数'. 默认: 33 | 34 | ``` 35 | { 36 | headers: Ioconfig.headers 37 | request: Ioconfig.request 38 | /** 39 | * 请求参数。可以是以下几种类型: 40 | * Bolb 41 | * BufferSource 42 | * FormData 43 | * URLSearchParams 44 | * USVString 45 | * String 46 | * JSON: 如果是json, 则做特殊处理,请见下面isformdata的说明 47 | */ 48 | // data: {}, 49 | /** 50 | * 如果data是json: 51 | * 1. request.method不是GET或HEAD, 且isformdata为true, 那么将data转换成FormData格式 52 | * 2. 如果不符合第1种,将data转换成querystring 53 | * @type {Boolean} 54 | */ 55 | isformdata: false, 56 | url: '', //请求url地址 57 | /** 58 | * 请求的数据类型,默认为json. 支持的数据类型有如下几种 59 | * arrayBuffer 60 | * blob 61 | * formData 62 | * json 63 | * text 64 | */ 65 | type: 'json', 66 | timeout: 6000, //接口超时时间 67 | /** 68 | * io请求前,统一的处理 69 | * @return {[type]} [description] 70 | */ 71 | beforeSend: function(){ 72 | 73 | }, 74 | /** 75 | * 对于接口返回错误,一般因为网络原因,进行的统一处理 76 | */ 77 | error: function(error){ 78 | //error或有或无 error.message 79 | //Alert.alert('系统消息',error.message || '亲,忙不过来了'); 80 | }, 81 | /** 82 | * 如果fail配置了funname为fail,则调用此方法. 此时fail.filter返回true 83 | * @param {Object|Other} result 接口返回数据 84 | * @param {Response} response 返回的response对象 85 | * @return {[type]} [description] 86 | */ 87 | fail: function(result,response){ 88 | //Alert.alert('系统消息',result.errmsg || '亲,忙不过来了'); 89 | }, 90 | /** 91 | * 成功调用方法。调用的情况有如下几种: 92 | * 1. dealfail为true, 则fail.filter返回false时,调用success 93 | * 此时如果dealdata为true, 则result为dealdatafun返回的数据 94 | * 2. dealfail为false时,则接口返回后直接调用此方法(不发生error的情况下) 95 | * 96 | * @param {Object|Other} result 接口返回数据 97 | * @param {Response} response 返回的response对象 98 | */ 99 | success: function(result,response){}, 100 | /** 101 | * 接口请求完毕调用。无论success,fail,error 102 | * @return {[type]} [description] 103 | */ 104 | complete: function(){}, 105 | /** 106 | * 如果dealdata为true, 则success的result为此方法返回的数据 107 | * @param {Object|Other} result 接口返回数据 108 | * @return {[type]} [description] 109 | */ 110 | dealdatafun: function(result){return result.data}, 111 | /** 112 | * 是否统一处理业务错误 113 | * @type {Boolean} 114 | */ 115 | dealfail: true, //是否统一处理业务错误 116 | /** 117 | * 当业务成功时,调用success前,是否统一格式化数据 118 | * 如果dealfail为true,并且fail.filter返回为false时,如果此项设置为true,则调用dealdatafun方法,返回处理后的数据 119 | * @type {Boolean} 120 | */ 121 | dealdata: true 122 | } 123 | ``` 124 | 125 | - IoConfig.fail: 统一处理接口返回的业务错误。如接口返回格式为: 126 | 127 | ```` 128 | { 129 | code: 'A0001', //A0001: 业务处理成功,A0002: 未登录,A0003: 其他业务处理错误 130 | } 131 | ```` 132 | ```` 133 | IoConfig.fail = { 134 | funname: 'fail', //当发生业务错误的时候,调用的IoConfig.ioparams里配置的方法名 135 | filter: function(result) { 136 | if(result.code != 'A0001'){ 137 | return true; //说明发生了业务错误 138 | }else{ 139 | return false; 140 | } 141 | } 142 | } 143 | ```` 144 | 145 | - IoConfig.headers: 请求头部配置[Headers](https://developer.mozilla.org/zh-CN/docs/Web/API/Headers) 146 | ``` 147 | { 148 | //如果Content-Type设置为false,则不传Content-Type 149 | 'Content-Type': 'application/x-www-form-urlencoded' 150 | } 151 | ``` 152 | 153 | - IoConfig.request: 请求配置[Request](https://developer.mozilla.org/zh-CN/docs/Web/API/Request).默认: 154 | ``` 155 | { 156 | method: 'GET', //GET|POST 157 | mode: 'cors', //cors|no-cors|same-origin|navigate 158 | //其他参数 159 | //body: credentials: cache: redirect: referrer: integrity 160 | /** 161 | * same-origin: 同ajax一样,同域发送cookie 162 | * include: 跨域发送cookie 163 | * @type {String} 164 | */ 165 | credentials: 'same-origin' 166 | } 167 | ``` 168 | 169 | ## Io 170 | 171 | 底层io请求方法 172 | 173 | Io.request({...}) 参数格式同 IoConfig.ioparams 174 | 175 | ## 引用到自己项目中来 176 | 177 | - 参考model.js添加项目io接口配置 178 | 179 | - 参考test.js,如何使用model.js里面的接口 180 | 181 | # 实例 182 | 183 | 请参考[zmrdlb react-native-demo](https://github.com/zmrdlb/react-native-demo/tree/master/AwesomeProject) 中的common/model.js和page/list.js 184 | --------------------------------------------------------------------------------