【问题标题】:SSR crashing in Next.js on unsuccessful GraphQL request (HTTP code 500) using Apollo ClientSSR 在 Next.js 中因使用 Apollo 客户端不成功的 GraphQL 请求(HTTP 代码 500)而崩溃
【发布时间】:2022-02-16 00:09:43
【问题描述】:

嗯,我有点矮。我会尽量清楚地解释我的问题。

我使用 Apollo 客户端进行 GraphQL 查询。我也使用 NextJS。 由于 SEO 原因,我有一个页面需要在服务器端呈现。

所以我有一个getProductFromSlug 函数可以让我执行我的请求。

export const getProductFromSlug = async (slug: string) => {
  try {
    const { data, error } = await apolloClient.query<{
      product: Product
    }>({
      query: GET_PRODUCT_BY_SLUG_QUERY,
      variables: {
        slug,
      },
    })

    if (error) {
      return { errors: [error.message] }
    }

    if (!('product' in data) || data.product === null) {
      return { errors: ['Product with specified url not found'] }
    }

    return {
      data,
    }
  } catch (error) {
    // @ts-ignore
    const formattedErrors: ApolloError = isApolloError(error)
      ? error.graphQLErrors.map((error) => error.message)
      : [`Unhandled error : ${error}`]

    return {
      errors: formattedErrors,
    }
  }
}

这里是getServerSideProps 将数据传递到页面

export const getServerSideProps = async (
  context: GetServerSidePropsContext
) => {
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore
  const requestData = await getProductFromSlug(context.params.slug as string)
  return 'errors' in requestData
    ? { notFound: true, props: requestData }
    : { props: requestData }
}

问题是,当我从端点收到 HTTP 代码 500 时,SSR 正在崩溃,而在 Vercel 上,它会导致无服务器崩溃错误。

错误:响应不成功:收到状态码 500 生成页面时发生此错误。任何控制台日志都将显示在终端窗口中

如果需要,这是我的入口点 (_app.tsx):

function MyApp(props: AppProps) {
  return (
    <ApolloProvider client={apolloClient}>
      <RecoilRoot>
        <RecoilNexus />
        <AuthenticationFromStorage />
        <Layout>
          <props.Component {...props.pageProps} />
        </Layout>
      </RecoilRoot>
    </ApolloProvider>
  )
}

您可以在这里查看我的 Apollo 客户端:https://gist.github.com/SirMishaa/d67e7229307b77b43a0b594d0c9e6943

yarn run dev 的堆栈跟踪(下一个 dev -p 3005):

ServerError: Response not successful: Received status code 500
    at Object.throwServerError (C:\Users\misha\Documents\dev\rekk-next\node_modules\@apollo\client\link\utils\utils.cjs:45:17)
    at C:\Users\misha\Documents\dev\rekk-next\node_modules\@apollo\client\link\http\http.cjs:31:19
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
error - uncaughtException: ServerError: Response not successful: Received status code 500
error Command failed with exit code 1.

注意: 在 try and catch 范围内对 console.log 进行了一些尝试后,在 Next SSR 控制台中没有显示任何内容,因此由于某种原因没有捕获 Apollo 的内部错误。

感谢您的帮助,谢谢!

【问题讨论】:

  • 你想如何处理前端的 500 错误(如应该向用户显示的内容)? catch 块中捕获的错误还不够吗?
  • 嗨,即使尝试捕获也不行,ssr 不断崩溃
  • 你能创建一个MRE吗,你可以使用这个URL作为graphql端点来得到一个500错误
  • Vercel 中显示的服务器日志是什么?
  • 我在guide 之后创建了这个repo,并放置了与您相同的阿波罗客户端,除了推送链接。似乎它有效,因为我运行npx next dev -p 3005 没有错误。也许尝试在没有推送链接的情况下运行它,看看会发生什么。但是,为了更准确地复制它,请提供您正在使用的软件包的版本。这可能是特定版本的问题。

标签: reactjs graphql next.js apollo apollo-client


【解决方案1】:

更新

问题是PusherLink 在出现错误时没有继续执行链。添加error 和complete 处理程序解决了这个问题。

      forward(operation).subscribe({
        next: (data) => {
          ...
          this.subscribeToChannel(subscriptionChannel, observer)
        },
        // these two were missing
        error: (error) => observer.error(error),
        complete: () => observer.complete(),
      })

JIC,我还添加了一个缺失条件,来自 other link 的代码作为参考具有

    subscribeObservable.subscribe = (observerOrNext, onError, onComplete) => {
      if (typeof(observerOrNext) == "function") {
        prevSubscribe(observerOrNext, onError, onComplete)
      } else {
        prevSubscribe(observerOrNext)
      }

老答案

您在 catch 块中的代码可能会引发错误,而这正是问题所在。我不确定这种情况Array.isArray(apolloError),也许你的意思是Array.isArray(apolloError.graphQLErrors)

您可以尝试使用isApolloError 实用程序,使其更清晰一些,并从 TS 类型中获得一些提示。

import { isApolloError } from '@apollo/client';
...
        const formattedErrors = isApolloError(e)
          ? e.graphQLErrors.map(error => error.message)
          : [`Unhandled error : ${e}`];

        return {
          errors: formattedErrors,
        };

【讨论】:

  • 您好,感谢您的回复!确实, isApolloError 更干净,但我仍然有问题:/。我的请求很简单,我使用页面的 slug 从端点获取 Product 实体,当我输入无效的 slug 时,SSR 由于 HTTP 代码 500 错误(API)而崩溃。但它绝对不应该这样做,我正在使用 try / catch 和你更干净的解决方案来处理错误......有什么想法吗?
【解决方案2】:

如果未找到 slug,您将返回空的 data。您需要检查是否找不到数据。

如果未找到数据,查询仍会以有效状态码响应,因为它仍然是有效查询,并且 Apollo 也在进行错误检查。


注释示例

export const getProductFromSlug = async (slug) => {
  try {
    // destructure `errors`
    const { data, errors } = await apolloClient.query({
      query: QUERY, variables: { slug },
    });

    // if apolloError/endpoint error i.e. 404/500/validation etc
    if(errors) {
     // Return notFound or formatted Apollo/validation errors
     return { notFound: true } 
    }

    // didn't find data for a slug and there aren't Apollo errors
    if(!data){ return { notFound: true } }

    // found slug and there aren't any errors
    return { data }

  } catch (e) {
    // hard JS errors not apollo or endpoint errors
    return { notFound: true }
  }
}

未注释的示例

export const getProductFromSlug = async (slug) => {
  try {
    const { data, errors } = await apolloClient.query({
      query: QUERY, variables: { slug },
    });
    return { data, notFound: (errors || !data) };
  } catch (e) {
    return { notFound: true };
  }
}

不考虑输入验证错误,但您的用例似乎不接受用户输入


您已经在抽象代码中处理错误和数据格式,按原样返回数据

export const getServerSideProps = async (context) => {
  if(!context.params) { return { notFound: true }}
  return await getProductFromSlug(context.params.slug);
}

【讨论】:

  • 您好!不幸的是,正如您在堆栈跟踪中看到的那样,错误似乎是 Apollo 客户端内部的,所以它不会改变任何东西。无论如何我都试过了,所以在这里(即使我的错误处理完全不符合页面的顺序),我完全处理了所有可能的情况,所以我不会有任何问题,但是如果我输入无效的 slug,SSR 仍然会崩溃...我没有解决方案...看看我做了什么:gist.github.com/SirMishaa/5721d125fa3bfaf73b12125508222af4,我在数据为空的情况下添加了丢失的错误处理。
  • 我已经更新了,抱歉,我正在检查数据中是否存在产品密钥以及产品是否不为空。否则,它会被认为是一个错误(只是看看它是否有效)但这仍然不起作用,SSR无缘无故崩溃并显示相同的错误消息。
  • 有什么意义? ('product' in data) 如果数据未定义,将被评估为虚假。所以它不会传递给 data.product === null
  • 我试过了,我添加了一些console.log,但我在控制台(下一个SSR服务器)中什么也没有,所以这意味着我要离开/我不在尝试{ } 范围。即使在 catch 范围内使用 console.log,控制台中也没有任何内容。所以这意味着,基本上,阿波罗错误根本没有被捕获。
  • 还是一样,错误是内部的,不在我的代码中。我想,也许我做的不好。
猜你喜欢
  • 2022-12-20
  • 2021-07-05
  • 1970-01-01
  • 2020-01-07
  • 2021-11-03
  • 2021-10-12
  • 2020-05-03
  • 2019-07-19
  • 2020-01-24
相关资源
最近更新 更多