web-dev-qa-db-fra.com

Comment exécuter une demande de récupération asynchrone, puis réessayer la dernière demande ayant échoué?

Le lien Apollo propose un gestionnaire d'erreurs onError

Problème: Actuellement, nous souhaitons actualiser les jetons oauth lorsqu'ils expirent lors d'un appel apollo et que nous ne pouvons pas exécuter une demande de récupération asynchrone à l'intérieur de onError correctement.

Code:

initApolloClient.js

import { ApolloClient } from 'apollo-client';
import { onError } from 'apollo-link-error';
import { ApolloLink, fromPromise } from 'apollo-link';

//Define Http link
const httpLink = new createHttpLink({
    uri: '/my-graphql-endpoint',
    credentials: 'include'
});

//Add on error handler for apollo link

return new ApolloClient({
    link: ApolloLink.from([
        onError(({ graphQLErrors, networkError, operation, forward  }) => {
            if (graphQLErrors) {
                //User access token has expired
                if(graphQLErrors[0].message==="Unauthorized") {
                    //We assume we have both tokens needed to run the async request
                    if(refreshToken && clientToken) {
                        //let's refresh token through async request
                        return fromPromise(
                            authAPI.requestRefreshToken(refreshToken,clientToken)
                            .then((refreshResponse) => {
                                let headers = {
                                    //readd old headers
                                    ...operation.getContext().headers,
                                    //switch out old access token for new one
                                    authorization: `Bearer ${refreshResponse.access_token}`,
                                };

                                operation.setContext({
                                    headers
                                });

                                //Retry last failed request
                                return forward(operation);
                            })
                            .catch(function (error) {
                                //No refresh or client token available, we force user to login
                                return error;
                            })
                        )
                    }
                }
            }
        }
    }
}),

Ce qui se produit est:

  1. La requête initiale GraphQL s'exécute et échoue en raison d'une non-autorisation
  2. La fonction onError de ApolloLink est exécutée.
  3. La promesse de rafraîchir le jeton est exécutée.
  4. La fonction onError de ApolloLink est à nouveau exécutée ??
  5. La promesse de rafraîchir le jeton est terminée.
  6. Le résultat initial de la requête graphQL est retourné et ses données sont undefined

Entre l'étape 5 et 6, apollo ne réexécute pas la requête graphQL initiale qui a échoué et donc le résultat est undefined.

Erreurs de la console:

Uncaught (in promise) Error: Network error: Error writing result to store for query:
 query UserProfile($id: ID!) {
  UserProfile(id: $id) {
    id
    email
    first_name
    last_name
    }
    __typename
  }
}

La solution devrait nous permettre de:

  1. Exécuter une demande asynchrone en cas d'échec d'une opération
  2. Attendre le résultat de la demande
  3. Nouvelle tentative ayant échoué avec les données du résultat de la demande
  4. L'opération doit réussir à retourner le résultat souhaité
12
Mysteryos

Je rafraîchis le jeton de cette façon (cela met à jour votre code):

import { ApolloClient } from 'apollo-client';
import { onError } from 'apollo-link-error';
import { ApolloLink, Observable } from 'apollo-link'; // <-- Add Observable

// Define Http link
const httpLink = new createHttpLink({
  uri: '/my-graphql-endpoint',
  credentials: 'include'
});

// Add on error handler for apollo link

return new ApolloClient({
  link: ApolloLink.from([
    onError(({ graphQLErrors, networkError, operation, forward }) => {
      if (graphQLErrors) {
        // User access token has expired
        if(graphQLErrors[0].message === "Unauthorized") {
          // We assume we have both tokens needed to run the async request
          if(refreshToken && clientToken) {
            // Let's refresh token through async request
            return new Observable(observer => {
              authAPI.requestRefreshToken(refreshToken, clientToken)
                .then(refreshResponse => {
                  operation.setContext(({ headers = {} }) => ({
                    headers: {
                      // Re-add old headers
                      ...headers,
                      // Switch out old access token for new one
                      authorization: `Bearer ${refreshResponse.access_token}` || null,
                    } 
                  }))
                })
                .then(() => {
                  const subscriber = {
                    next: observer.next.bind(observer),
                    error: observer.error.bind(observer),
                    complete: observer.complete.bind(observer)
                  }

                  // Retry last failed request
                  forward(operation).subscribe(subscriber)
                })
                .catch(error => {
                  // No refresh or client token available, we force user to login
                  observer.error(error)
                })
            })
          }
        }
      }
    })
  ])
})
27
Igor Lamoš