【问题标题】:React Apollo Link - How to forward operation after a promise has resolved?React Apollo Link - 如何在承诺解决后转发操作?
【发布时间】:2020-01-18 20:18:07
【问题描述】:

所以我想出了如何设置一个中间件来处理我的身份验证令牌,以及在需要时获取新的。问题是,这里有一个边缘情况,当 promise 被解决后,操作在没有设置正确的标头的情况下被转发,导致另一个可能未经身份验证的调用。我觉得这里的技巧很简单,但我似乎无法弄清楚。有没有办法将承诺的结果返回到封闭的函数?我没有找到太多的运气,但也许还有另一种方法。这是设置我的中间件和 Apollo 客户端的代码:

const authLink = new ApolloLink((operation, forward) => {
  operation.setContext(({ headers = {} }) => {
    const token = localStorage.getItem('token');
    const tokenExp = token ? decodeJWT(token).exp : null;
    const currentTime = Date.now() / 1000;

    if(token && tokenExp >= currentTime) {
      // Check if token is expired. If so, get a new one and THEN
      // move forward
      headers = { ...headers,authorization: token ? `Bearer ${token}` : "", };
      return { headers };
    } else {

    // TODO: This would be replaced with the token service that actually
    // takes an expired token and sends back a valid one
    return fetch('http://localhost:4000/topics', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          query: `mutation LOGIN_USER(
            $email: String
            $password: String!
          ) {
            login(email: $email, password: $password) {
              id
              token
            }
          }
        `,
          variables: {
            email: "test@test.com",
            password: "test"
          }
        }),
      }).then(response => {
        return response.json()
      })
      .then(({ data: { login: { token } }}) => {
        // Put updated token in storage
        localStorage.setItem('token', token);
        headers = { ...headers,authorization: token ? `Bearer ${token}` : "", };
        return { headers };
      });
    }
  });
  return forward(operation);
});


/**
 * Setup the URLs for each service
 */
const httpTopicsServiceLink = createHttpLink({
  uri: 'http://localhost:4000/topics',
});

/**
 * Create the client instance for each GraphQL server URL
 */
export const TopicsClient = new ApolloClient({
  link:authLink.concat(httpTopicsServiceLink),
  cache: new InMemoryCache(),
});

【问题讨论】:

  • 如果setContext 正在返回一个Promise,你不能在其中添加一个then 语句并转发到那里吗?
  • 你是什么意思?我尝试将 .then 添加到操作中,但我收到错误消息告诉我 Cannot read property 'then' of undefined :/

标签: reactjs react-apollo apollo-client


【解决方案1】:

您可以返回您自己的 Promise,该 Promise 将使用标头或您的 fetch 请求中的另一个 Promise 进行解析:

const authLink = new ApolloLink(async (operation, forward) => {
  return await operation.setContext(({ headers = {} }) => {
    const token = localStorage.getItem('token');
    const tokenExp = token ? decodeJWT(token).exp : null;
    const currentTime = Date.now() / 1000;

    return new Promise((resolve, reject) => {
        if(token && tokenExp >= currentTime) {
      // Check if token is expired. If so, get a new one and THEN
      // move forward
      headers = { ...headers,authorization: token ? `Bearer ${token}` : "", };
      resolve({ headers });
    } else {

    // TODO: This would be replaced with the token service that actually
    // takes an expired token and sends back a valid one
    resolve(fetch('http://localhost:4000/topics', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          query: `mutation LOGIN_USER(
            $email: String
            $password: String!
          ) {
            login(email: $email, password: $password) {
              id
              token
            }
          }
        `,
          variables: {
            email: "test@test.com",
            password: "test"
          }
        }),
      }).then(response => {
        return response.json()
      })
      .then(({ data: { login: { token } }}) => {
        // Put updated token in storage
        localStorage.setItem('token', token);
        headers = { ...headers,authorization: token ? `Bearer ${token}` : "", };
        return { headers };
      }));
    }
    });

  }).then(res => {
    return forward(operation);
  });
});

无法对此进行测试,因此我可能遗漏了某些内容,但这应确保请求在转发之前完成。

【讨论】:

  • 感谢您的回复,但不幸的是,我收到了Cannot read property 'then' of undefined 的错误。我尝试过类似的方法,将整个 operation.setContext 包装在 Promise 中。虽然我认为该函数必须返回一个 observable 而不是简单地调用 forward(operation) hmmmmm
  • 编辑:我误解了。也许如果 ApolloLink 回调可以是异步的?你可以awaitsetContext.
  • 更新了我的答案,值得一试!
  • 感谢您的努力?我在搞乱然后尝试了你的版本,我得到了forward(...).subscribe is not a function 错误哈哈。我在做其他事情时遇到了同样的错误。我什至尝试将整个东西从rxjs 包装在from 中,但没有成功:P
猜你喜欢
  • 2021-06-13
  • 1970-01-01
  • 2016-04-05
  • 1970-01-01
  • 2020-09-07
  • 2016-01-10
  • 2017-02-18
  • 2021-07-20
  • 2015-06-24
相关资源
最近更新 更多