# Nextjs服务端重定向导致的错误日志问题

# 问题背景

当前服务端会产生很多不必要的错误日志,虽然不影响服务运行,但对查看服务端日志有很大的干扰性,需要排查日志产生原因,并消除它

TypeError:  Converting  circular  structure  to  JSON
        -->  starting  at  object  with  constructor  'Socket'
        |          property  '_writableState'  ->  object  with  constructor  'WritableState'
        |          property  'afterWriteTickInfo'  ->  object  with  constructor  'Object'
        ---  property  'stream'  closes  the  circle
        at  JSON.stringify  ()
        at  Object.renderToHTML  (/app/node_modules/next/dist/server/render.js:583:54)
        at  runMicrotasks  ()
        at  processTicksAndRejections  (internal/process/task_queues.js:95:5)
        at  async  doRender  (/app/node_modules/next/dist/server/base-server.js:687:38)
        at  async  cacheEntry.responseCache.get.isManualRevalidate.isManualRevalidate  (/app/node_modules/next/dist/server/base-server.js:796:28)
        at  async  /app/node_modules/next/dist/server/response-cache/index.js:80:36
Error  [ERR_HTTP_HEADERS_SENT]:  Cannot  set  headers  after  they  are  sent  to  the  client
        at  new  NodeError  (internal/errors.js:322:7)
        at  ServerResponse.setHeader  (_http_outgoing.js:561:11)
        at  ServerResponse._res.setHeader  (/app/node_modules/next/dist/server/base-server.js:127:24)
        at  setHeadersFromObject  (/app/node_modules/next/dist/compiled/compression/index.js:46:680)
        at  ServerResponse.setWriteHeadHeaders  (/app/node_modules/next/dist/compiled/compression/index.js:46:914)
        at  ServerResponse.writeHead  (/app/node_modules/next/dist/compiled/compression/index.js:46:121)
        at  Function.App.getInitialProps  (/app/build/server/pages/_app.js:4375:13)
        at  Object.  (/app/node_modules/next/dist/shared/lib/utils.js:75:33)
        at  Generator.next  ()
        at  asyncGeneratorStep  (/app/node_modules/@swc/helpers/lib/_async_to_generator.js:23:28)  {
    code:  'ERR_HTTP_HEADERS_SENT'
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 问题分析

# 什么情况下产生

经过本地的测试,在不支持的浏览器访问带有重定向的页面时(在getServerSideProps里有重定向),就会产生错误日志;这是因为页面的重定向与判断浏览器不兼容的重定向冲突了,

代码一: _app.jsx文件代码,代码二: getServerSideProps实现服务端重定向的页面代码

// page/_app.jsx 文件内代码
App.getInitialProps = async ({ ctx }) => {
  // ....
  if (!browserInfo.compatible) {
    res.writeHead(307, {
      // 浏览器不兼容,重定向到xxx页面
      Location: "/xxx.html",
    });
    return res.end();
  }
  //...省略余下代码
};
1
2
3
4
5
6
7
8
9
10
11
12
// page/index.jsx  任意页面下的getServerSideProps
export async function getServerSideProps({ req, query }) {
  // 重定向到xxx页
  return {
    redirect: {
      destination: encodeURI(`/xxx`),
      permanent: false,
    },
  };
}
1
2
3
4
5
6
7
8
9
10

两次重定向会导致: Cannot  set  headers  after  they  are  sent  to  the  client  报错的产生: getServerSideProps里重定向也会执行res.end方法,多次执行end就会触发这个报错;

‘ return res.end() ’ 会导致: TypeError:  Converting  circular  structure  to  JSON  报错的产生; 因为nextjs会对getInitialProps方法返回的内容进行json序列化

# 如何解决

1、在getServerSideProps层添加是否已经做重定向的判断,如果已经重定向过了,就不在做重定向操作

优点: 不引入新功能,稳定

缺点: 需要修改多个文件,后续新增getServerSideProps的重定向时,都要做这个判断;

2、使用middleware (opens new window)功能,中间件拦截请求提前做一些处理

优点: 统一处理,修改快速

缺点: 每个请求都会走到这里,可能会存在影响服务性能问题

实现代码

import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import { getBrowser } from "./utils/getBrowser";
 
export function middleware(request: NextRequest) {
  // 如果是请求文件地址,直接返回
  if (
    /^.+\.(?:(?:[a-zA-Z0-9]+)|(?:[a-zA-Z0-9]+\/[a-zA-Z0-9]+))$/.test(
      request.nextUrl.pathname
    )
  ) {
    return NextResponse.next();
  }
 
  const ua = request.headers.get("user-agent");
  const browser = getBrowser(ua);
  if (!browser.compatible) {
    return NextResponse.redirect(new URL("/browser.html", request.url));
  }
 
  return NextResponse.next();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

3、自定义后端服务 Advanced Features: Custom Server | Next.js (nextjs.org) (opens new window)

优点: 自定义程度更高

缺点: 工作量大

以前也排查过,为啥没有解决

以前nextjs版本比较低,当时也想用middleware来解决,但当时这个功能还不完善,用的过程中存在问题,所以就暂时搁置了。

现在nextjs升级过了,middleware功能处于稳定状态,可以正常使用了

更新时间: 5/5/2023, 11:19:52 AM