Axios 响应拦截器:无法处理过期的 refresh_token (401)
Axios Reponse Interceptor : unable to handle an expired refresh_token (401)
我的 axios 响应中有以下拦截器:
window.axios.interceptors.response.use(
response => {
return response;
},
error => {
let errorResponse = error.response;
if (errorResponse.status === 401 && errorResponse.config && !errorResponse.config.__isRetryRequest) {
return this._getAuthToken()
.then(response => {
this.setToken(response.data.access_token, response.data.refresh_token);
errorResponse.config.__isRetryRequest = true;
errorResponse.config.headers['Authorization'] = 'Bearer ' + response.data.access_token;
return window.axios(errorResponse.config);
}).catch(error => {
return Promise.reject(error);
});
}
return Promise.reject(error);
}
);
_getAuthToken 方法是:
_getAuthToken() {
if (!this.authTokenRequest) {
this.authTokenRequest = window.axios.post('/api/refresh_token', {
'refresh_token': localStorage.getItem('refresh_token')
});
this.authTokenRequest.then(response => {
this.authTokenRequest = null;
}).catch(error => {
this.authTokenRequest = null;
});
}
return this.authTokenRequest;
}
该代码深受 https://github.com/axios/axios/issues/266#issuecomment-335420598 启发。
总结:当用户调用 API 并且他的 access_token 已过期(API 返回 401 代码)时,应用调用 /api/refresh_token 端点以获取新的 access_token。如果在进行此调用时 refresh_token 仍然有效,则一切正常:我得到一个新的 access_token 和一个新的 refresh_token 并且用户请求的初始 API 调用是再次制作并正确返回。
当 refresh_token 也已过期时出现问题。
在那种情况下,调用 /api/refresh_token returns 一个 401 并且没有任何反应。我尝试了几件事,但无法检测到,无法将用户重定向到应用程序的登录页面。
我发现在那种情况下,_getAuthToken 方法中的 if (!this.authTokenRequest) 语句 returns 是一个从未解决的未决 Promise。我不明白为什么这是一个承诺。在我看来它应该是空的...
我是 Promises 的新手,所以我可能会遗漏一些东西!
感谢您的帮助!
编辑:
我可能找到了一种更简单的方法来处理这个问题:当我调用 /api/refresh_token 端点时使用 axios.interceptors.response.eject() 禁用拦截器,然后重新启用它。
代码:
createAxiosResponseInterceptor() {
this.axiosResponseInterceptor = window.axios.interceptors.response.use(
response => {
return response;
},
error => {
let errorResponse = error.response;
if (errorResponse.status === 401) {
window.axios.interceptors.response.eject(this.axiosResponseInterceptor);
return window.axios.post('/api/refresh_token', {
'refresh_token': this._getToken('refresh_token')
}).then(response => {
this.setToken(response.data.access_token, response.data.refresh_token);
errorResponse.config.headers['Authorization'] = 'Bearer ' + response.data.access_token;
this.createAxiosResponseInterceptor();
return window.axios(errorResponse.config);
}).catch(error => {
this.destroyToken();
this.createAxiosResponseInterceptor();
this.router.push('/login');
return Promise.reject(error);
});
}
return Promise.reject(error);
}
);
},
看起来好还是不好?任何建议或评论表示赞赏。
你最后的解决方案看起来不错。如果我处于相同的情况,我会想出与您类似的实现方式。
I found that in that case the if (!this.authTokenRequest) statement inside the _getAuthToken method returns a pending Promise that is never resolved. I don't understand why this is a Promise. In my opinion it should be null...
那是因为代码中的 this.authTokenRequest
刚刚分配了从 window.axios.post
创建的 Promise。 Promise 是一种处理延迟评估的对象,因此您在 then
中实现的过程在 Promise 被解决之前不会执行。
JavaScript 为我们提供了 Promise 对象作为一种异步事件处理程序,它使我们能够将流程实现为 then
链,它将被执行以响应异步结果的结果。 HTTP 请求总是不可预测的,因为 HTTP 请求有时会消耗我们预期的更多时间,有时也不会。当我们使用 HTTP 请求时总是使用 Promise,以便使用事件处理程序处理它的异步响应。
在 ES2015 语法中,您可以使用 async/await 语法实现函数来处理 Promise 对象,因为它看起来是同步的。
我的 axios 响应中有以下拦截器:
window.axios.interceptors.response.use(
response => {
return response;
},
error => {
let errorResponse = error.response;
if (errorResponse.status === 401 && errorResponse.config && !errorResponse.config.__isRetryRequest) {
return this._getAuthToken()
.then(response => {
this.setToken(response.data.access_token, response.data.refresh_token);
errorResponse.config.__isRetryRequest = true;
errorResponse.config.headers['Authorization'] = 'Bearer ' + response.data.access_token;
return window.axios(errorResponse.config);
}).catch(error => {
return Promise.reject(error);
});
}
return Promise.reject(error);
}
);
_getAuthToken 方法是:
_getAuthToken() {
if (!this.authTokenRequest) {
this.authTokenRequest = window.axios.post('/api/refresh_token', {
'refresh_token': localStorage.getItem('refresh_token')
});
this.authTokenRequest.then(response => {
this.authTokenRequest = null;
}).catch(error => {
this.authTokenRequest = null;
});
}
return this.authTokenRequest;
}
该代码深受 https://github.com/axios/axios/issues/266#issuecomment-335420598 启发。
总结:当用户调用 API 并且他的 access_token 已过期(API 返回 401 代码)时,应用调用 /api/refresh_token 端点以获取新的 access_token。如果在进行此调用时 refresh_token 仍然有效,则一切正常:我得到一个新的 access_token 和一个新的 refresh_token 并且用户请求的初始 API 调用是再次制作并正确返回。
当 refresh_token 也已过期时出现问题。
在那种情况下,调用 /api/refresh_token returns 一个 401 并且没有任何反应。我尝试了几件事,但无法检测到,无法将用户重定向到应用程序的登录页面。
我发现在那种情况下,_getAuthToken 方法中的 if (!this.authTokenRequest) 语句 returns 是一个从未解决的未决 Promise。我不明白为什么这是一个承诺。在我看来它应该是空的...
我是 Promises 的新手,所以我可能会遗漏一些东西! 感谢您的帮助!
编辑:
我可能找到了一种更简单的方法来处理这个问题:当我调用 /api/refresh_token 端点时使用 axios.interceptors.response.eject() 禁用拦截器,然后重新启用它。
代码:
createAxiosResponseInterceptor() {
this.axiosResponseInterceptor = window.axios.interceptors.response.use(
response => {
return response;
},
error => {
let errorResponse = error.response;
if (errorResponse.status === 401) {
window.axios.interceptors.response.eject(this.axiosResponseInterceptor);
return window.axios.post('/api/refresh_token', {
'refresh_token': this._getToken('refresh_token')
}).then(response => {
this.setToken(response.data.access_token, response.data.refresh_token);
errorResponse.config.headers['Authorization'] = 'Bearer ' + response.data.access_token;
this.createAxiosResponseInterceptor();
return window.axios(errorResponse.config);
}).catch(error => {
this.destroyToken();
this.createAxiosResponseInterceptor();
this.router.push('/login');
return Promise.reject(error);
});
}
return Promise.reject(error);
}
);
},
看起来好还是不好?任何建议或评论表示赞赏。
你最后的解决方案看起来不错。如果我处于相同的情况,我会想出与您类似的实现方式。
I found that in that case the if (!this.authTokenRequest) statement inside the _getAuthToken method returns a pending Promise that is never resolved. I don't understand why this is a Promise. In my opinion it should be null...
那是因为代码中的 this.authTokenRequest
刚刚分配了从 window.axios.post
创建的 Promise。 Promise 是一种处理延迟评估的对象,因此您在 then
中实现的过程在 Promise 被解决之前不会执行。
JavaScript 为我们提供了 Promise 对象作为一种异步事件处理程序,它使我们能够将流程实现为 then
链,它将被执行以响应异步结果的结果。 HTTP 请求总是不可预测的,因为 HTTP 请求有时会消耗我们预期的更多时间,有时也不会。当我们使用 HTTP 请求时总是使用 Promise,以便使用事件处理程序处理它的异步响应。
在 ES2015 语法中,您可以使用 async/await 语法实现函数来处理 Promise 对象,因为它看起来是同步的。