Skip to content

Custom next.js 500 page, try to expose internal error.

Posted on:October 16, 2023 at 06:36 AM

When we use Next.js, typically, we can’t do much customization on the 500 error page besides changing the styles. If you want to display the specific error code thrown by an API to facilitate user bug feedback, what can you do?

Table of contents

Open Table of contents

解决办法

const Error = ({ statusCode }: ErrorProps) => {
  // hack
  const [errCode, setErrCode] = useState('')
  useEffect(() => {
    try {
      const p = JSON.parse(
        document.getElementById('__NEXT_DATA__')?.textContent as string
      )
      setTraceid(p.props.pageProps.errCode)
    } catch (err) {}
  }, [])

  return (
    <>
      <h1>500</h1>
      <div>{errCode}</div>
    </>
  )
}

Error.getInitialProps = async ({
  res,
  err,
  pathname,
  query,
  AppTree,
}: NextPageContext) => {
  const errorInitialProps = await NextError.getInitialProps({
    res,
    err,
    pathname,
    query,
    AppTree,
  })

  let errCode = err.code
  return {
    ...errorInitialProps,
    errCode,
  }
}

export default Error

The code is easy to understand, but why do it this way?

getInitialProps 返回的 errCode 是会丢失的。直接用props去接收errCode你就会发现页面上的errCode闪烁一下就消失了。

假设如果用props去接收errCode会发生什么?

next.js渲染是分为两部分的,首先是服务端渲染出html显示到页面上, 然后客户端再次渲染出html替换掉服务端渲染的html(这个过程叫做hydrate)。

首先服务端渲染出的结果是没有问题的,但是客户端hydrate的过程中渲染出来的结果是没有errCode的导致了上述现象的发生

为什么客户端渲染的时props.errCode丢失了

我的的errCode是从err.code取出来的

{
    message: "500 - Internal Server Error."
    name: "Internal Server Error."
    statusCode :500
  }

所以客户端渲染时 errCode 返回的就是undefined,然后就从页面消失了。

为什么这个错误变掉了?

直接上next.js源码,nextjs在序列化error给客户端用时调用了以下函数

function serializeError(
  dev: boolean | undefined,
  err: Error
): Error & {
  statusCode?: number
  source?: typeof COMPILER_NAMES.server | typeof COMPILER_NAMES.edgeServer
} {
  if (dev) {
    return errorToJSON(err)
  }

  return {
    name: 'Internal Server Error.',
    message: '500 - Internal Server Error.',
    statusCode: 500,
  }
}

这里原始错误就消失了

为什么next.js 这么设计

这种情况可能是由于 Next.js 默认的错误处理机制以及安全性考虑而导致的。Next.js的500错误页面通常设计成非常简洁和最小化,以减少潜在的信息泄漏风险,这可以增加应用程序的安全性。


到此已经全部明了,我们只要拿到next.js服务端返回的原始数据就可以解决最开始的问题了。

next.js服务端返回的原始数据怎么拿呢

const p = JSON.parse(
  document.getElementById("__NEXT_DATA__")?.textContent as string
);

或者你直接用

window.__NEXT_DATA__;

也行

嘿嘿!