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();
    })
  }
}


你可能感兴趣的:(fetch拦截器的实现)