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)

24
demandé sur Dmitry Shvedov 2016-03-09 21:54:53

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

Démo https://plnkr.co/edit/0ZLpc8jgKI18w4c0f905?p=preview

18
répondu ForceUser 2016-10-17 08:52:50

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);
});
1
répondu Daniel 2018-04-12 02:16:22