Intercepteurs Axios et connexion asynchrone
j'implémente l'authentification token dans mon application web. Mon access token
expire toutes les N minutes et qu'un refresh token
est utilisé pour se connecter et obtenir un nouveau access token
.
J'utilise Axios pour tous mes appels API. J'ai un intercepteur mis en place pour intercepter 401
réponses.
axios.interceptors.response.use(undefined, function (err) {
if (err.status === 401 && err.config && !err.config.__isRetryRequest) {
serviceRefreshLogin(
getRefreshToken(),
success => { setTokens(success.access_token, success.refresh_token) },
error => { console.log('Refresh login error: ', error) }
)
err.config.__isRetryRequest = true
err.config.headers.Authorization = 'Bearer ' + getAccessToken()
return axios(err.config);
}
throw err
})
fondamentalement, comme j'intercepte une réponse 401, Je veux faire un login et puis rejouer la requête rejetée originale avec les nouveaux tokens. Mon serviceRefreshLogin
les appels de fonction setAccessToken()
dans son then
bloc. Mais le problème est que
then
block arrive plus tard que le getAccessToken()
dans l'intercepteur, donc le retour se produit avec les anciennes références expirées.
getAccessToken()
et getRefreshToken()
il suffit de retourner les tokens existants stockés dans le navigateur (ils gèrent localStorage, cookies, etc.).
Comment puis-je m'assurer que les énoncés ne s'exécutent pas tant qu'une promesse n'est pas revenue?
(Voici un numéro correspondant sur github: https://github.com/mzabriskie/axios/issues/266)
2 réponses
Il suffit D'utiliser une autre promesse: D
axios.interceptors.response.use(undefined, function (err) {
return new Promise(function (resolve, reject) {
if (err.status === 401 && err.config && !err.config.__isRetryRequest) {
serviceRefreshLogin(
getRefreshToken(),
success => {
setTokens(success.access_token, success.refresh_token)
err.config.__isRetryRequest = true
err.config.headers.Authorization = 'Bearer ' + getAccessToken();
axios(err.config).then(resolve, reject);
},
error => {
console.log('Refresh login error: ', error);
reject(error);
}
);
}
throw err;
});
});
https://github.com/stefanpenner/es6-promise
mais, il peut être préférable de réécrire getRefreshToken pour rendre la promesse et ensuite rendre le code plus simple
axios.interceptors.response.use(undefined, function (err) {
if (err.status === 401 && err.config && !err.config.__isRetryRequest) {
return getRefreshToken()
.then(function (success) {
setTokens(success.access_token, success.refresh_token) ;
err.config.__isRetryRequest = true;
err.config.headers.Authorization = 'Bearer ' + getAccessToken();
return axios(err.config);
})
.catch(function (error) {
console.log('Refresh login error: ', error);
throw error;
});
}
throw err;
});
pourrait le faire dans la requête au lieu de la réponse, et il serait probablement plus propre car il éviterait de frapper le serveur lorsque le jeton d'accès est expiré. La copie à partir de cet article:
function issueToken() {
return new Promise((resolve, reject) => {
return client({
...
}).then((response) => {
resolve(response);
}).catch((err) => {
reject(err);
});
});
}
client.interceptors.request.use((config) => {
let originalRequest = config;
if (tokenIsExpired && path_is_not_login) {
return issueToken().then((token) => {
originalRequest['Authorization'] = 'Bearer ' + token;
return Promise.resolve(originalRequest);
});
}
return config;
}, (err) => {
return Promise.reject(err);
});