fetch攔截器(interceptors)一般用于發(fā)起http請(qǐng)求之前或之后對(duì)請(qǐng)求進(jìn)行統(tǒng)一的處理卵佛,如token實(shí)現(xiàn)的登錄鑒權(quán)(每個(gè)請(qǐng)求帶上token)杨赤,統(tǒng)一處理404響應(yīng)等等。ajax攔截器有很多ajax庫(kù)已經(jīng)實(shí)現(xiàn)了级遭,如jq的$.ajaxSetup()望拖,$.ajaxPrefilter(),$.ajaxError挫鸽;axios的axios.interceptors.request.use()说敏,axios.interceptors.response.use();vue-resource的Vue.http.interceptors.push()等等丢郊。
??fetch常用的庫(kù)有whatwg-fetch盔沫,node-fetch,isomorphic-fetch枫匾。whatwg-fetch是做了polyfill的讓不支持fetch的 browser也可以使用fetch架诞,node-fetch運(yùn)行在node上,isomorphic-fetch是對(duì)前兩者做了封裝干茉,既可以跑在browser上也可以跑在node上谴忧。然后下面是一個(gè)簡(jiǎn)易的fetch攔截器的實(shí)現(xiàn)。
//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) {
//執(zhí)行請(qǐng)求前的攔截操作
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) => {
//添加超時(shí)檢測(cè)
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)
//執(zhí)行請(qǐng)求后的攔截操作
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 存入請(qǐng)求后執(zhí)行的數(shù)組
_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 }
原理很簡(jiǎn)單角虫,把原生的fetch封裝起來沾谓,維護(hù)兩個(gè)數(shù)組,分別保存請(qǐng)求之前的操作和請(qǐng)求之后的操作戳鹅,用新的fetch api做請(qǐng)求均驶,依次執(zhí)行這些操作,攔截處理數(shù)據(jù)枫虏。
使用示例:
//index.js
import storage, { MyStorage } from './storage/storage';
import breadFetch, { fetch } from './util/bread-fetch'
global.fetch = fetch
//fetch攔截器 檢驗(yàn)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();
})
}
}