从崩溃到优雅:Egg.js错误处理机制完全指南

从崩溃到优雅:Egg.js错误处理机制完全指南

【免费下载链接】egg 🥚 Born to build better enterprise frameworks and apps with Node.js & Koa 【免费下载链接】egg 项目地址: https://gitcode.com/gh_mirrors/egg11/egg

在Node.js应用开发中,错误处理往往是决定应用健壮性的关键因素。当用户操作触发异常却看到满屏堆栈信息时,不仅影响体验,更可能泄露敏感信息。Egg.js作为企业级Node.js框架,提供了一套完整的错误处理机制,让开发者能够优雅地捕获、处理和响应各类异常。本文将深入解析Egg.js的错误处理流程,从基础捕获到高级定制,帮助你构建更可靠的应用系统。

异常捕获:同步编码模型下的错误处理

Egg.js基于Koa框架构建,借助co库实现了异步代码的同步化编写,这使得异常捕获变得简单直接——所有异步操作中产生的错误都可以通过try/catch语法捕获。这种一致性的错误处理方式极大降低了开发者的心智负担。

// app/service/test.js
try {
  const res = yield this.ctx.curl('http://eggjs.com/api/echo', { dataType: 'json' });
  if (res.status !== 200) throw new Error('response status is not 200');
  return res.data;
} catch (err) {
  this.logger.error(err);
  return {};
}

官方异常捕获文档中特别强调,为了保证异常可追踪,所有抛出的异常必须是Error类型,因为只有Error对象才会包含完整的堆栈信息,这对于问题定位至关重要。

异步链断裂问题与解决方案

在实际开发中,一个常见的陷阱是异步操作跳出了Egg.js的异步调用链,导致异常无法被捕获。例如使用setImmediatesetTimeout等API创建的异步操作,会脱离Egg.js的控制流。

// 错误示例:异常无法被捕获
exports.buy = function* (ctx) {
  const request = {};
  const config = yield ctx.service.trade.buy(request);
  // 此异步操作跳出了调用链
  setImmediate(() => {
    ctx.service.trade.check(request).catch(err => ctx.logger.error(err));
  });
}

为解决这个问题,Egg.js提供了ctx.runInBackground辅助方法,它会将异步操作重新包装到Egg.js的异步链中,确保异常能够被统一捕获:

// 正确示例:使用runInBackground
exports.buy = function* (ctx) {
  const request = {};
  const config = yield ctx.service.trade.buy(request);
  ctx.runInBackground(function* () {
    // 此处异常会被框架捕获并记录
    yield ctx.service.trade.check(request);
  });
}

框架层统一异常处理:onerror插件的工作机制

Egg.js通过内置的onerror插件实现了全局统一的异常处理。该插件会捕获应用中所有未处理的异常,并根据请求的Accept头信息自动返回合适的错误响应格式。

响应策略矩阵

onerror插件的响应行为由请求格式、运行环境和配置共同决定,具体规则如下:

请求需求格式环境errorPageUrl配置返回内容
HTML & TEXTlocal/unittest-详细错误页面(含堆栈信息)
HTML & TEXT生产环境已配置重定向到errorPageUrl
HTML & TEXT生产环境未配置简化错误页面(不含敏感信息)
JSONlocal/unittest-含详细信息的JSON对象
JSON生产环境-仅含基本信息的JSON对象

错误页面重定向配置

在生产环境中,为了避免向用户暴露详细的错误信息,通常会配置错误页面重定向。通过修改config/config.default.js文件,可以设置统一的错误页面URL:

// config/config.default.js
module.exports = {
  onerror: {
    // 线上环境发生异常时重定向到此页面
    errorPageUrl: '/50x.html',
  },
};

自定义异常处理:打造专属错误响应

尽管框架提供了默认的异常处理机制,但在实际项目中,我们常常需要根据业务需求定制错误响应格式。Egg.js允许通过中间件的方式完全掌控异常处理流程。

自定义错误处理中间件

创建一个位于app/middleware目录下的错误处理中间件,例如error_handler.js

// app/middleware/error_handler.js
module.exports = () => {
  return function* errorHandler(next) {
    try {
      yield next;
    } catch (err) {
      // 必须触发app的error事件,框架会统一记录错误日志
      this.app.emit('error', err, this);
      
      // 自定义错误响应格式
      this.body = {
        success: false,
        errorCode: err.code || 'INTERNAL_ERROR',
        message: this.app.config.env === 'prod' 
          ? '服务器内部错误' 
          : err.message,
        requestId: this.header['x-request-id'] || this.ctx.requestId,
      };
      
      // 设置状态码
      this.status = err.status || 500;
    }
  };
};

配置中间件作用范围

创建好中间件后,需要在配置中启用并设置其作用范围。通过matchignore配置,可以精确控制哪些路由使用自定义错误处理:

// config/config.default.js
module.exports = {
  middleware: [ 'errorHandler' ],
  errorHandler: {
    // 只对/api前缀的请求生效
    match: '/api',
    // 也可以使用ignore排除某些路径
    // ignore: '/admin',
  },
};

404处理:资源不存在时的优雅响应

Egg.js将404状态视为正常的业务响应而非异常,但仍提供了灵活的定制机制。默认情况下,框架会根据请求的Accept头返回不同格式的404响应:

  • JSON请求返回:{ "message": "Not Found" }
  • HTML请求返回:<h1>404 Not Found</h1>

404页面重定向配置

通过修改配置,可以将HTML请求的404响应重定向到自定义页面:

// config/config.default.js
module.exports = {
  notfound: {
    pageUrl: '/404.html',
  },
};

404响应自定义中间件

类似于异常处理,404响应也可以通过中间件完全定制。创建app/middleware/notfound_handler.js文件:

// app/middleware/notfound_handler.js
module.exports = () => {
  return function* (next) {
    yield next;
    if (this.status === 404 && !this.body) {
      if (this.acceptJSON) {
        this.body = { 
          error: 'Not Found',
          path: this.path,
          method: this.method
        };
      } else {
        this.body = '<!DOCTYPE html><html><head><title>页面不存在</title></head>' +
                    '<body><h1>404 - 页面不存在</h1><p>您请求的资源不存在</p></body></html>';
      }
    }
  };
};

然后在配置中启用该中间件:

// config/config.default.js
module.exports = {
  middleware: [ 'notfoundHandler' ],
};

错误处理最佳实践

错误日志记录

Egg.js提供了强大的日志系统,建议在捕获异常时使用this.logger.error()方法记录错误详情,便于问题排查:

try {
  // 业务逻辑
} catch (err) {
  // 记录错误日志,包含堆栈信息
  this.logger.error('交易处理失败', { 
    requestId: this.ctx.requestId,
    userId: this.ctx.session.userId,
    error: err 
  });
  // 业务处理...
}

错误监控与告警

对于企业级应用,仅仅记录错误日志是不够的。建议结合Egg.js的日志系统和第三方监控服务(如Sentry、Datadog等),实现错误的实时监控和告警。通过监听app的error事件,可以集中处理错误上报:

// app.js
module.exports = app => {
  app.on('error', (err, ctx) => {
    // 忽略404错误
    if (err.status === 404) return;
    
    // 上报错误到监控系统
    ctx.service.monitor.reportError({
      err,
      url: ctx.url,
      userId: ctx.session.userId,
      ip: ctx.ip,
      timestamp: Date.now()
    });
  });
};

总结与实践建议

Egg.js的错误处理机制为开发者提供了从基础到高级的完整解决方案。在实际开发中,建议遵循以下原则:

  1. 统一错误格式:定义项目级的错误基类,包含错误码、消息、状态码等标准字段
  2. 分级日志策略:根据错误严重程度使用不同级别的日志方法(debug/info/warn/error)
  3. 环境差异化处理:开发环境展示详细错误信息,生产环境返回安全的用户提示
  4. 完善的监控告警:建立错误监控体系,及时发现并解决线上问题

通过合理利用Egg.js提供的错误处理能力,结合项目实际需求进行定制,可以显著提升应用的健壮性和用户体验。完整的错误处理机制不仅是应用质量的体现,也是开发团队工程能力的重要标志。

【免费下载链接】egg 🥚 Born to build better enterprise frameworks and apps with Node.js & Koa 【免费下载链接】egg 项目地址: https://gitcode.com/gh_mirrors/egg11/egg

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值