fetch拦截器的实现

fetch拦截器(interceptors)一般用于发起http请求之前或之后对请求进行统一的处理,如token实现的登录鉴权(每个请求带上token),统一处理404响应等等。ajax拦截器有很多ajax库已经实现了,如jq的$.ajaxSetup(),$.ajaxPrefilter(),$.ajaxError;axios的axios.interceptors.request.use(),axios.interceptors.response.use();vue-resource的Vue.http.interceptors.push()等等。
  fetch常用的库有whatwg-fetch,node-fetch,isomorphic-fetch。whatwg-fetch是做了polyfill的让不支持fetch的 browser也可以使用fetch,node-fetch运行在node上,isomorphic-fetch是对前两者做了封装,既可以跑在browser上也可以跑在node上。然后下面是一个简易的fetch拦截器的实现。

//bread-fetch.js


var oldFetch = global.fetch

var newFetch = function (url, options={}) {
  let request = {
      url,
      options
  }

  return new Promise((resolve, reject) => {

    if (this.interceptors.length > 0) {
        //执行请求前的拦截操作
        this.runInterceptors(0, request)
        .then(req => {
            oldFetchFun(this,req)
            .then((res)=>{
                resolve(res);
            })
            .catch(err => {
                reject(err)
            });
        })
    } else {
        oldFetchFun(this, request)
        .then((res)=>{
            resolve(res);
        })
        .catch(err => {
            reject(err)
        });
    }

  });
}

var oldFetchFun = function (that, request) {
    return new Promise((resolve, reject) => {
        //添加超时检测
        var timeout = request.options.timeout
        var timer
        if (timeout) {
            timer = setTimeout(function(){
                            reject(new Error("fetch timeout"))
                        }, timeout );
        }
        console.log('oldFetch request',request)
        oldFetch(request.url, request.options)
        .then(res=>{
            console.log('oldFetch res',res);
            return res.json();
        })
        .then(res => {
            console.log('oldFetch res json',res)
            //执行请求后的拦截操作
            let response = res
            if (that.interceptors_after.length > 0) {
                that.runInterceptorsAfter(0, response)
                .then(data => {
                    resolve(data);
                })
            }
        })
        .catch(err => {
            console.log('err',err)
            reject(err)
        });
    })
}

var breadFetch = function () {
}

breadFetch.prototype.newFetch = newFetch  

//fetch拦截器
breadFetch.prototype.interceptors = []
breadFetch.prototype.interceptors_after = []
breadFetch.prototype.runInterceptors = function (i, request) {
  var _that = this
  if(i===0) this.interceptors_after = []

  return new Promise((resolve, reject) => {
    if (i >= this.interceptors.length) resolve(request)
    this.interceptors[i](request, function (callback) {
        if(callback){
            //callback 存入请求后执行的数组
            _that.interceptors_after.push(callback)
        }
        _that.runInterceptors(++i, request).then(req => {
            resolve(req)
        })   
    })
  })
}

breadFetch.prototype.runInterceptorsAfter = function (i, response) {
  var _that = this
  return new Promise((resolve, reject) => {
    if (i >= this.interceptors_after.length) resolve(response)
    this.interceptors_after[i](response, function () {
        _that.runInterceptorsAfter(++i, response).then(res => {
            resolve(res)
        })   
    })
  })
}

let objFetch = new breadFetch()
let fetch = function (url, options = {}) {
     return new Promise((resolve, reject) => {
         objFetch.newFetch(url, options)    
         .then(data => {
             resolve(data);
         })
         .catch(err => {
             reject(err)
         });
     })
}

export default objFetch
export { fetch } 


原理很简单,把原生的fetch封装起来,维护两个数组,分别保存请求之前的操作和请求之后的操作,用新的fetch api做请求,依次执行这些操作,拦截处理数据。

使用示例:


//index.js

import storage, { MyStorage } from './storage/storage';
import breadFetch, { fetch } from './util/bread-fetch'

global.fetch = fetch

//fetch拦截器 检验token url带上token
breadFetch.interceptors.push((req, next) => {
  console.log('interceptors1')
  if (req.url.includes('/api/login') || req.url.includes('/api/signup')) {
      next()
      return
  }
  MyStorage.load('login-token',(token)=>{
      console.log('login-token',token)
      if (req.url.includes('?')) {
        req.url = req.url + '&token=' + token
      } else {
        req.url = req.url + '?token=' + token
      }
      next()
    },() => {
      console.log('not found token, please login')
    },() => {
      console.log('token expire')
    })

})
breadFetch.interceptors.push((req, next) => {
  console.log('interceptors2')
  next()
})
breadFetch.interceptors.push((req, next) => {
  console.log('interceptors3')
  next((res, after) => {
    console.log('interceptorsAfter1')
    after()
  }) 
})

breadFetch.interceptors.push((req, next) => {
  console.log('interceptors4')
  next((res, after) => {
    console.log('interceptorsAfter2')
    // if (res.body.code === 302) {
    //   window.location = res.body.uri
    // }
    after()
  })
})
//signin.js

export function login (username, password) {
  return (dispatch, getState) => {
    return new Promise((resolve, reject) => {
        let params = { username, password }
        console.log('params',params)

        fetch(`${config.host}:${config.port}/api/login`, {
          method: 'post',
          headers: {
            //'Accept': 'application/json, text/plain, */\*',
            'Accept': 'application/json',
            'Content-Type': 'application/json'
            //'Content-Type': 'application/x-www-form-urlencoded'
          },
          body: JSON.stringify(params)
        })
        // .then(res=>res.json()) 
        .then((data) => {
              console.log('data',data)
              dispatch(signinResult(data.success)) 
              if (data.success) {
                MyStorage.save('login-token',{token: data.token})
                resolve()
              }
        })
        .catch((err) => {  
          console.warn(err);  
        })
        .done();
    })
  }
}


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

推荐阅读更多精彩内容