# 中间件错误封装

URL: https://caijiao.org/koa/error/middleware
Source: docs/koa/error/middleware.md
Description: 中间件错误封装是对 Koa 错误处理的一种改进方式，它通过封装错误处理逻辑，使得代码更加模块化和易于维护。

在 [Koa](/koa/) 应用中，错误处理是不可或缺的一部分。为了保持代码的清晰性和可维护性，我们可以将错误处理封装成一个专门的中间件。这种做法使得错误处理逻辑与应用的业务逻辑分离，并且可以复用。

## 为什么需要中间件错误封装

- **清晰的代码结构**：将错误处理逻辑与业务逻辑分离，减少冗余代码，使得每个模块的职责更加明确。
- **复用性**：封装的错误处理可以在多个路由或中间件中复用，避免重复代码。
- **一致性**：确保所有错误以相同的方式进行处理，便于调试和日志记录。

## 错误封装中间件实现

我们可以通过创建一个专门的错误处理中间件来捕获应用中的所有错误，并统一处理错误响应。

### 示例：封装错误处理中间件

```js
// errorMiddleware.js
module.exports = async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    // 统一处理错误
    console.error(`Error: ${err.message}\nStack: ${err.stack}`);

    // 设置 HTTP 状态码和错误信息
    ctx.status = err.status || 500;
    ctx.body = {
      message: err.message || 'Internal Server Error',
      // 开发环境下返回详细的堆栈信息
      stack: process.env.NODE_ENV === 'development' ? err.stack : ''
    };
  }
};
````

### 使用错误封装中间件

将错误封装中间件引入应用中，并在应用中进行注册：

```js
// app.js
const Koa = require('koa');
const app = new Koa();

// 引入错误封装中间件
const errorMiddleware = require('./errorMiddleware');

// 使用错误封装中间件
app.use(errorMiddleware);

// 示例路由
app.use(async (ctx) => {
  if (ctx.path === '/error') {
    throw new Error('This is a custom error!');
  }
  ctx.body = 'Hello, Koa!';
});

// 启动应用
app.listen(3000, () => {
  console.log('Koa server is running on port 3000');
});
```

在这个示例中，`errorMiddleware` 是一个单独的中间件模块，它封装了所有的错误处理逻辑。无论何时发生错误，它都会捕获并生成统一的错误响应。

## 错误封装中的日志记录

除了捕获和返回错误信息外，通常我们还需要记录错误日志，以便后续排查问题。日志记录通常包括错误消息、堆栈信息和时间戳等。可以使用日志库，如 `winston` 或 `log4js`，来记录这些错误。

### 示例：使用 `winston` 记录错误日志

```js
// errorMiddleware.js
const winston = require('winston');

// 创建日志记录器
const logger = winston.createLogger({
  level: 'error',
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
  ],
});

module.exports = async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    // 记录错误信息
    logger.error(`Error: ${err.message}\nStack: ${err.stack}`);

    // 设置 HTTP 状态码和错误信息
    ctx.status = err.status || 500;
    ctx.body = {
      message: err.message || 'Internal Server Error',
      stack: process.env.NODE_ENV === 'development' ? err.stack : ''
    };
  }
};
```

通过这种方式，所有的错误都会被记录到 `error.log` 文件中，方便后续查看和分析。

## 错误封装的最佳实践

1. **集中式错误处理**：通过封装错误处理中间件，将错误处理逻辑集中管理，避免分散在各个业务逻辑中。
2. **开发与生产环境的差异**：在开发环境中，可以返回详细的堆栈信息，以便调试；在生产环境中，返回简洁的错误信息，避免泄露敏感信息。
3. **日志记录**：将错误信息记录到日志文件中，以便后续排查问题，特别是在生产环境中尤为重要。
4. **自定义错误类型**：可以通过自定义错误类型来更精确地捕获和处理不同类型的错误。

通过封装错误处理中间件，可以使 Koa 应用的错误处理更加简洁、统一且易于维护，有助于提升应用的稳定性和可调试性。
