react native 基于 fetch 简单封装自己的网络请求

导语

在项目开发中,常用的工具类,我们都会专门封装起来进行统一处理,方便以后统一调用、修改。网络请求类算是用到最多的公共类之一了,本文就简单来对 react native 的 fetch 进行简单的封装,并不会讲述太多细节,你可以使用你喜欢的搜索引擎去搜索 fetch api 关键字以了解更多信息。

参考

fetch api

  • method: 默认为 GET 请求
  • headers:请求头 用来添加一些设备信息和用户信息
  • body:请求体 请求参数

基本功能

  • 支持 GET POST 两种请求 默认为 POST 请求
  • 支持自定义请求头、传入请求参数
  • 支持自定义请求超时时间
  • 支持请求中断(防止重复发送同一个请求)
  • 便于扩展

接下来先看一下我们的网络请求调用方式
第一步:创建,继承封装好的 fetch 类 重写一下 requestUrl 方法

class XXXRequest extends BaseRequest{
  requestUrl() {
     return 'api/xxx.xxx';
}

第二步:使用

//创建一个请求对象,参数为(请求体,请求方法)
XXXRequest request = new XXXRequest();
// timeout 超时时间 单位为 s
// 显示网络请求加载动画
request.timeout(10).showLoadingView().start(
     // 成功的回调
     (success)=>{
     },
     // 失败的的回调
    (error)=>{
   });
// 中断本次请求
request&&request.setCancled(true);

接下来直接上代码 我会在代码里面进行详细的讲解

/**
* Created by jzz on 2017/8/25.
*/

import  {
   Platform,
   BackAndroid,
   Alert,
}from 'react-native'

// 是否开启 debug 模式
const DEBUG_MODE = true;
// 默认超时时间 20s
const TIMEOUT = 20;
export default class BaseRequest {
   // 构造
   constructor(body, method, mode) {
       // 是否终止请求 默认false
       this.isCancled = false;
       // 请求失败是否打印后台message 默认false
       this.isShowMessage = false;
       if (body == null) {
           body = {};
       }
       // 默认的一些参数比如 版本号 token 
       Object.assign(body, {
           // version: version
           // access_token: testToken
       });
       // 当没有指定请求方法的时候默认post
       if (method == null) {
           method = 'POST';
       }
       if (mode == null) {
           mode = 'cors';
       }
       this.method = method;
       this.body = body;
       this.mode = mode;
       this.headers = {
           'Accept': 'application/json',
           'Content-Type': 'application/json',
           // 在这里可以添加你需要添加的请求头参数
           // 如 用户 token、手机设备号等等
       };
       // 扩展 网络请求耗时
       this.requestTime = this.getDefaultTestingTime();
      // 默认超时时间
      // 想要超时时间生效 必须修改 fetch 的源码 在发请求之前加上如下代码 (路径为node_modules下的fetch.js的 self.fetch 方法里面)
      // if(init&&init!==null&&typeof init.timeout!=='undefined'){
      //     xhr.timeout=init.timeout;
      // }
       this.requestTimeout = TIMEOUT;
   }

   /**
    * 请覆盖此方法
    */
   requestUrl() {
       throw ({message: 'function requestUrl must be overrided!'});
   }

   /**
    * 开启加载动画
    * 如果需要加载动画可以在这里面去出路
    * 这里在用 redux 去控制显示加载
    * 可以在这里面添加调用加载动画的代码 
    * @returns {BaseRequest}
    */
   showLoadingView() {
       this.isShowing = true;
       // 没有删除自己的 redux
       //store.dispatch(DialogAction.showLoading(true))
       return this;
   }

   /**
    * 关闭dialog
    * @returns {BaseRequest}
    */
   dismissLoadingView() {
       this.isShowing = false;
       // store.dispatch(DialogAction.showLoading(false))
       return this;
   }

   /**
    * 设置超时时间 如果不设置默认20s
    */
   timeout(timeout) {
       this.requestTimeout = typeof timeout === 'number' ? (timeout > 0 ? timeout : TIMEOUT) : TIMEOUT ;
       return this;
   }

   /**
    * 开始请求
    * @param successCallBack 成功后的回调
    * @param failCallBack 失败后的回调
    * @returns {BaseRequest}
    */
   start(successCallBack, failCallBack) {
       //接口响应时间测试
       if (this.requestTime) {
           this.startTime = new Date().getTime();
       }
       //监听android返回键,android
       let url = this.requestUrl();
       if (this.isShowing) {
           BackAndroid.addEventListener(url, () => {
               this.dismissLoadingView();
               BackAndroid.removeEventListener(url);
               return false;
           });
       }
       this._doPost(successCallBack, failCallBack);
       return this;
   }

   /**
    * 开始请求
    * @param successCallBack 成功后的回调
    * @param failCallBack 失败后的回调
    * @returns {BaseRequest}
    */
   async _doPost(successCallBack, failCallBack) {
       try {
           let url = this.getBaseUrl() + this.requestUrl();
           if ('GET' === this.method) {
               let str = this.toQueryString(this.body);
               if (str && str.length > 0) url += '?' + str;
           }
           this.showLog('requestUrl==>' + url);
           this.showLog(this.body);
           let response = await fetch(url, {
               headers: this.headers,
               method: this.method,
               mode: this.mode,
               body: this.method == 'GET' ? null : JSON.stringify(this.body),
               timeout: this.requestTimeout * 1000,
               // credentials: 'include',
           });
           let responseJson = await response.json();
           // console.log('---> BaseRequest start body' + JSON.stringify(this.body));
           this.showLog('response==>' + responseJson);
           this.showLog(responseJson);
           if (this.isShowing) {
               this.dismissLoadingView();
           }
           if (responseJson && !this.isCancled) {
               this.handleResponse(responseJson, successCallBack, failCallBack);
           } else {
               if (failCallBack && !this.isCancled) failCallBack('请求失败');
           }
       } catch (erro) {
           if (this.isShowing) {
               this.dismissLoadingView();
           }
           this.showLog('erro==>');
           this.showLog(erro);
           if (failCallBack && !this.isCancled) failCallBack(erro);
       }
       return this;
   }

   /**
    * 处理response
    * @param responseJson
    * @param successCallBack
    */
   handleResponse(responseJson, successCallBack, failCallBack) {
       if (this.requestTime) {
           try {
               console.log('********requestUrl: ' + this.requestUrl());
               console.log('********This request consumes time: ' + (new Date().getTime() - this.startTime) + 'ms');
           } catch (e) {
           }
       }
       if (this.isShowing) {
           this.dismissLoadingView();
       }
       // 我们项目里面 code 为200 或者 '200' 作为网络请求成功的标示 其余为失败 大家可以根据自己项目的实际情况做更改
       if ('200' == responseJson.code || parseInt(responseJson.code) === 200) {
           if (successCallBack) successCallBack(responseJson);
       } else if (responseJson.message && responseJson.message.length > 0 && this.isShowMessage) {
           // 可以在这里利用 code 处理错误信息  例如:token失效 调用登陆
           // Alert(responseJson.message);
           if (failCallBack) failCallBack(responseJson);
       } else {
           if (failCallBack) failCallBack(responseJson);
       }
   }

   /**
    * 请求是否已取消
    * @returns {*|boolean}
    */
   isCancel() {
       return this.isCancled;
   }

   /**
    * 是否取消请求
    * @param cancle
    */
   setCancled(cancle) {
       this.isCancled = cancle;
       if (this.isShowing) {
           this.dismissLoadingView();
       }
   }

   /**
    * 请求失败后是否显示后台message
    * @param show
    * @returns {BaseRequest}
    */
   setShowMessage(show) {
       this.isShowMessage = show;
       return this;
   }

   /**
    * 用于对对象编码以便进行传输
    * @param obj 对象参数
    * @returns {string} 返回字符串
    */
   toQueryString(obj) {
       let str = '';
       if (obj) {
           let keys = [];
           for (let key in obj) {
               keys.push(key);
           }
           keys.forEach((key, index) => {
               str += key + '=' + obj[key];
               if (index !== keys.length - 1) {
                   str += '&';
               }
           });
       }
       return str;
   }

   /**
    * 打印后台message
    * @param str
    */
   alertWithStr(str) {
       TimerMiXin.setTimeout(() => {
           if (Platform.OS == 'ios') {
               Alert.alert(
                   str,
                   null,
                   [
                       {text: '确定'}
                   ]
               );
           }
           if (Platform.OS == 'android') {
               // 弹窗  可以自己写
           }
       }, 1000);
   }

   /**
    * 打印log信息
    * @param log
    */
   showLog(log) {
       if (DEBUG_MODE) {
           console.log(log);
       }
   }

   /**
    * 返回baseurl
    */
   getBaseUrl() {
       return baseUrl;
   }

   /**
    * 是否打印接口请求时间
    * @returns {boolean}
    */
   getDefaultTestingTime() {
       return false;
   }
}

如需扩展,添加一些功能,可以参考showLoadingView()。
如有错误,欢迎指正。等有时间我会写个 demo 上传到github。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,265评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,274评论 1 288
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,087评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,479评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,782评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,218评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,594评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,316评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,955评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,274评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,803评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,177评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,732评论 3 229
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,953评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,687评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,263评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,189评论 2 258

推荐阅读更多精彩内容