nuxt2如何在nuxt.config.js中直接捕获服务端错误?

如题,项目前端是nuxt2做的,现在遇到如下问题
当通过非正常编码编码后的url 访问的时候, pm2日志中报错 "URI malformed"
探究了一下,错误是因为 /node_moduels/@nuxt/server/dist/server.js 文件中的

const nuxtMiddleware = ({ options, nuxt, renderRoute, resources }) => async function nuxtMiddleware (req, res, next) {
  // Get context
  const context = utils.getContext(req, res);

  try {
    const url = decodeURI(req.url); //这里
    res.statusCode = 200;
    const result = await renderRoute(url, context);

    // If result is falsy, call renderLoading
    if (!result) {
      await nuxt.callHook('server:nuxt:renderLoading', req, res);
      return
    }

    await nuxt.callHook('render:route', url, result, context);
    const {
      html,
      cspScriptSrcHashes,
      error,
      redirected,
      preloadFiles
    } = result;

    if (redirected && context.target !== utils.TARGETS.static) {
      await nuxt.callHook('render:routeDone', url, result, context);
      return html
    }
    if (error) {
      res.statusCode = context.nuxt.error.statusCode || 500;
    }

    if (options.render.csp && cspScriptSrcHashes) {
      const { allowedSources, policies } = options.render.csp;
      const isReportOnly = !!options.render.csp.reportOnly;
      const cspHeader = isReportOnly ? 'Content-Security-Policy-Report-Only' : 'Content-Security-Policy';

      res.setHeader(cspHeader, getCspString({ cspScriptSrcHashes, allowedSources, policies, isReportOnly }));
    }

    // Add ETag header
    if (!error && options.render.etag) {
      const { hash } = options.render.etag;
      const etag = hash ? hash(html, options.render.etag) : generateETag__default['default'](html, options.render.etag);
      if (fresh__default['default'](req.headers, { etag })) {
        res.statusCode = 304;
        await nuxt.callHook('render:beforeResponse', url, result, context);
        res.end();
        await nuxt.callHook('render:routeDone', url, result, context);
        return
      }
      res.setHeader('ETag', etag);
    }

    // HTTP2 push headers for preload assets
    if (!error && options.render.http2.push) {
      // Parse resourceHints to extract HTTP.2 prefetch/push headers
      // https://w3c.github.io/preload/#server-push-http-2
      const { shouldPush, pushAssets } = options.render.http2;
      const { publicPath } = resources.clientManifest;

      const links = pushAssets
        ? pushAssets(req, res, publicPath, preloadFiles)
        : defaultPushAssets(preloadFiles, shouldPush, publicPath, options);

      // Pass with single Link header
      // https://blog.cloudflare.com/http-2-server-push-with-multiple-assets-per-link-header
      // https://www.w3.org/Protocols/9707-link-header.html
      if (links.length > 0) {
        res.setHeader('Link', links.join(', '));
      }
    }

    // Send response
    res.setHeader('Content-Type', 'text/html; charset=utf-8');
    res.setHeader('Accept-Ranges', 'none'); // #3870
    res.setHeader('Content-Length', Buffer.byteLength(html));
    await nuxt.callHook('render:beforeResponse', url, result, context);
    res.end(html, 'utf8');
    await nuxt.callHook('render:routeDone', url, result, context);
    return html
  } catch (err) {
    if (context && context.redirected) {
      consola__default['default'].error(err);
      return err
    }

    if (err.name === 'URIError') { //这里
      err.statusCode = 400;
    }
    next(err);
  }
};

其中

const url = decodeURI(req.url);

对url无法进行解析,导致的.

需求是想在遇到"URI malformed"错误的时候,直接跳首页,目前采用了两种做法解决

第一种:

在nuxt.config.js文件中,增加 serverMiddleware 配置项

serverMiddleware: [
    (req, res, next) => {
      // 自定义中间件逻辑...
      try {
        //尝试解析url
        decodeURIComponent(req.url)
        //往下继续执行
        next();
      } catch {
        //解析错误就跳转
        res.writeHead(302, {
          Location: '/'
        });
        res.end();

      }
    }
  ],

这样可以解决,但是无法阻止server.js 文件会对url再次进行解析,这样比较消耗资源

第二种:

直接修改 /node_moduels/@nuxt/server/dist/server.js 文件

if (err.name === 'URIError') {
      err.statusCode = 400;
    }

中加入:

if (err.name === 'URIError') {
      err.statusCode = 400;
      //新加
      res.writeHead(302, { Location: '/' })
      //新加
      return res.end()
    }

这样也能起到效果,但是这样就直接修改了核心代码

有没有更好的办法处理?求解

注意: 这个是服务端的错误,无法通过定义路由中间件的形式捕获(即在/middleware 目录中定义中间件,在serverMiddleware中使用,这样加载不出来,已试)

引用 皆我百晓生 小程序回复内容作答:
可以通过自定义错误处理中间件来捕获服务端错误,然后重定向到首页。在nuxt.config.js文件中的serverMiddleware配置项中添加自定义错误处理中间件,代码如下:

import consola from 'consola';

export default function (req, res, next) {
  try {
    decodeURIComponent(req.url)
    next();
  } catch (error) {
    // 处理URI malformed错误
    if (error instanceof URIError) {
      res.writeHead(302, {
        Location: '/'
      });
      return res.end();
    }
    // 处理其他服务端错误
    consola.error(error);
    res.writeHead(500, {
      'Content-Type': 'text/html'
    });
    return res.end('Internal Server Error');
  }
}

然后在nuxt.config.js中将自定义错误处理中间件添加到serverMiddleware中:

serverMiddleware: [
  '~/middleware/error-handler.js'
]

这样就能捕获并处理服务端错误了。同时,通过将错误处理中间件放在serverMiddleware中的顶部,可以确保该中间件第一个被执行,从而避免了其他中间件对URL进行解析的问题。

【以下回答由 GPT 生成】

Nuxt.js的nuxtMiddleware函数中的错误捕获部分可以通过添加try...catch语句来实现。下面是修改后的代码示例:

const nuxtMiddleware = ({ options, nuxt, renderRoute, resources }) => async function nuxtMiddleware (req, res, next) {
  // Get context
  const context = utils.getContext(req, res);

  try {
    const url = decodeURI(req.url); // 可能引发错误的代码
    res.statusCode = 200;
    const result = await renderRoute(url, context);

    // If result is falsy, call renderLoading
    if (!result) {
      await nuxt.callHook('server:nuxt:renderLoading', req, res);
      return
    }

    await nuxt.callHook('render:route', url, result, context);
    const {
      html,
      cspScriptSrcHashes,
      error,
      redirected,
      preloadFiles
    } = result;

    if (redirected && context.target !== utils.TARGETS.static) {
      await nuxt.callHook('render:routeDone', url, result, context);
      return html
    }
    if (error) {
      res.statusCode = context.nuxt.error.statusCode || 500; 
    }

    if (options.render.csp && cspScriptSrcHashes) {
      const { allowedSources, policies } = options.render.csp;
      const isReportOnly = !!options.render.csp.reportOnly;
      const cspHeader = isReportOnly ? 'Content-Security-Policy-Report-Only' : 'Content-Security-Policy';

      res.setHeader(cspHeader, getCspString({ cspScriptSrcHashes, allowedSources, policies, isReportOnly }));
    }

    // Add ETag header
    if (!error && options.render.etag) {
      const { hash } = options.render.etag;
      const etag = hash ? utils.etag(html, hash) : utils.etag(html);

      if (req.headers['if-none-match'] === etag) {
        res.statusCode = 304
      } else {
        res.setHeader('ETag', etag);
      }
    }

    //...
  } catch (error) {
    // handle error
    console.error('服务端错误:', error); // 打印错误信息到控制台
    // 写入错误状态码到响应头
    res.statusCode = 500; // 可根据具体需求修改状态码
    // 发送错误响应给客户端
    res.end('服务器出现了错误,请稍后重试');
  }
}

修改后的代码将错误捕获部分放在了一个try...catch语句块中,在catch块中打印错误到控制台,并发送一个适当的错误响应给客户端。你可以根据具体需求修改错误状态码和错误响应内容。

请注意,在实际项目中,你可能还需要根据错误类型进行不同的错误处理,这个代码示例只是一个简单的处理方式。

请注意将上述代码添加到适当的位置,并将其与nuxtMiddleware函数替换掉原始的代码段。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^