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