如何执行异步获取请求,然后重试上次失败的请求? [英] How to execute an async fetch request and then retry last failed request?

查看:54
本文介绍了如何执行异步获取请求,然后重试上次失败的请求?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

阿波罗链接提供错误处理程序 onError

问题:目前,我们希望在 apollo 调用期间过期时刷新 oauth 令牌,并且我们无法在 onError 中正确执行异步获取请求.

Issue: Currently, we wish to refresh oauth tokens when they expires during an apollo call and we are unable to execute an async fetch request inside the onError properly.

代码:

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

会发生什么:

  1. 初始 graphQL 查询运行并因未授权而失败
  2. 执行ApolloLinkonError函数.
  3. 执行刷新令牌的承诺.
  4. 再次执行ApolloLinkonError函数??
  5. 刷新令牌的承诺已完成.
  6. 返回初始graphQL查询结果,其数据为undefined
  1. Initial graphQL query runs and fails due to unauthorization
  2. The onError function of ApolloLink is executed.
  3. The promise to refresh the token is executed.
  4. The onError function of ApolloLink is executed again??
  5. The promise to refresh the token is completed.
  6. The initial graphQL query result is returned and its data is undefined

在第 5 步和第 6 步之间,apollo 不会重新运行初始失败的 graphQL 查询,因此结果是 undefined.

Between step 5 and 6, apollo doesn't re-run the initial failed graphQL query and hence the result is undefined.

来自控制台的错误:

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

解决方案应该允许我们:

The solution should allow us to:

  1. 当操作失败时运行异步请求
  2. 等待请求的结果
  3. 使用请求结果中的数据重试失败的操作
  4. 操作应该成功返回其预期结果

推荐答案

我正在以这种方式刷新令牌(更新的 OP):

I'm refreshing the token this way (updated OP's):

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 }) => {
      // User access token has expired
      if (graphQLErrors && 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);
              });
          });
        }
      }
    })
  ])
});

这篇关于如何执行异步获取请求,然后重试上次失败的请求?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆