gpt4 book ai didi

javascript - Axios 拦截器重试原始请求并访问原始 promise

转载 作者:可可西里 更新时间:2023-11-01 01:21:14 25 4
gpt4 key购买 nike

如果访问 token 过期,我有一个拦截器可以捕获 401 错误。如果它过期,它会尝试刷新 token 以获取新的访问 token 。如果在此期间进行任何其他调用,它们将排队等待访问 token 被验证。

这一切都运作良好。但是,当使用 Axios(originalRequest) 处理队列时,最初附加的 promise 不会被调用。请参阅下面的示例。

工作拦截器代码:

Axios.interceptors.response.use(
response => response,
(error) => {
const status = error.response ? error.response.status : null
const originalRequest = error.config

if (status === 401) {
if (!store.state.auth.isRefreshing) {
store.dispatch('auth/refresh')
}

const retryOrigReq = store.dispatch('auth/subscribe', token => {
originalRequest.headers['Authorization'] = 'Bearer ' + token
Axios(originalRequest)
})

return retryOrigReq
} else {
return Promise.reject(error)
}
}
)

刷新方法(使用刷新 token 获取新的访问 token )

refresh ({ commit }) {
commit(types.REFRESHING, true)
Vue.$http.post('/login/refresh', {
refresh_token: store.getters['auth/refreshToken']
}).then(response => {
if (response.status === 401) {
store.dispatch('auth/reset')
store.dispatch('app/error', 'You have been logged out.')
} else {
commit(types.AUTH, {
access_token: response.data.access_token,
refresh_token: response.data.refresh_token
})
store.dispatch('auth/refreshed', response.data.access_token)
}
}).catch(() => {
store.dispatch('auth/reset')
store.dispatch('app/error', 'You have been logged out.')
})
},

在auth/actions模块中订阅方法:

subscribe ({ commit }, request) {
commit(types.SUBSCRIBEREFRESH, request)
return request
},

以及突变:

[SUBSCRIBEREFRESH] (state, request) {
state.refreshSubscribers.push(request)
},

这是一个示例操作:

Vue.$http.get('/users/' + rootState.auth.user.id + '/tasks').then(response => {
if (response && response.data) {
commit(types.NOTIFICATIONS, response.data || [])
}
})

如果此请求被添加到队列中,因为刷新 token 必须访问新 token ,我想附加原始的 then():

  const retryOrigReq = store.dispatch('auth/subscribe', token => {
originalRequest.headers['Authorization'] = 'Bearer ' + token
// I would like to attache the original .then() as it contained critical functions to be called after the request was completed. Usually mutating a store etc...
Axios(originalRequest).then(//if then present attache here)
})

刷新访问 token 后,处理请求队列:

refreshed ({ commit }, token) {
commit(types.REFRESHING, false)
store.state.auth.refreshSubscribers.map(cb => cb(token))
commit(types.CLEARSUBSCRIBERS)
},

最佳答案

2019 年 2 月 13 日更新

由于许多人对此主题表现出兴趣,我创建了 axios-auth-refresh package这应该可以帮助您实现此处指定的行为。


这里的关键是返回正确的 Promise 对象,所以你可以使用 .then() 进行链接。我们可以为此使用 Vuex 的状态。如果发生刷新调用,我们不仅可以将 refreshing 状态设置为 true,还可以将刷新调用设置为待处理状态。这样使用 .then() 将始终绑定(bind)到正确的 Promise 对象,并在 Promise 完成时执行。这样做将确保您不需要额外的队列来保持等待 token 刷新的调用。

function refreshToken(store) {
if (store.state.auth.isRefreshing) {
return store.state.auth.refreshingCall;
}
store.commit('auth/setRefreshingState', true);
const refreshingCall = Axios.get('get token').then(({ data: { token } }) => {
store.commit('auth/setToken', token)
store.commit('auth/setRefreshingState', false);
store.commit('auth/setRefreshingCall', undefined);
return Promise.resolve(true);
});
store.commit('auth/setRefreshingCall', refreshingCall);
return refreshingCall;
}

这将始终返回已创建的请求作为 Promise 或创建新请求并将其保存以供其他调用使用。现在您的拦截器将类似于以下拦截器。

Axios.interceptors.response.use(response => response, error => {
const status = error.response ? error.response.status : null

if (status === 401) {

return refreshToken(store).then(_ => {
error.config.headers['Authorization'] = 'Bearer ' + store.state.auth.token;
error.config.baseURL = undefined;
return Axios.request(error.config);
});
}

return Promise.reject(error);
});

这将允许您再次执行所有待处理的请求。但是一下子,没有任何询问。


如果您希望挂起的请求按照实际调用的顺序执行,您需要将回调作为第二个参数传递给 refreshToken() 函数,如下所示。

function refreshToken(store, cb) {
if (store.state.auth.isRefreshing) {
const chained = store.state.auth.refreshingCall.then(cb);
store.commit('auth/setRefreshingCall', chained);
return chained;
}
store.commit('auth/setRefreshingState', true);
const refreshingCall = Axios.get('get token').then(({ data: { token } }) => {
store.commit('auth/setToken', token)
store.commit('auth/setRefreshingState', false);
store.commit('auth/setRefreshingCall', undefined);
return Promise.resolve(token);
}).then(cb);
store.commit('auth/setRefreshingCall', refreshingCall);
return refreshingCall;
}

拦截器:

Axios.interceptors.response.use(response => response, error => {
const status = error.response ? error.response.status : null

if (status === 401) {

return refreshToken(store, _ => {
error.config.headers['Authorization'] = 'Bearer ' + store.state.auth.token;
error.config.baseURL = undefined;
return Axios.request(error.config);
});
}

return Promise.reject(error);
});

我还没有测试第二个例子,但它应该可以工作或者至少给你一个想法。

Working demo of first example - 因为他们使用的模拟请求和服务的演示版本,一段时间后它不会工作,代码仍然存在。

来源:Interceptors - how to prevent intercepted messages to resolve as an error

关于javascript - Axios 拦截器重试原始请求并访问原始 promise ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51563821/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com