# 全局错误处理

URL: https://caijiao.org/koa/error/global
Source: docs/koa/error/global.md
Description: 全局错误处理是 Koa 应用中的重要部分，可以捕获并处理请求过程中的各种错误，提供更好的用户体验和错误日志。

在 [Koa 应用](/koa/intro/application)中，全局错误处理是保证应用稳定性和提升用户体验的关键部分。通过有效的错误处理机制，可以捕获和处理请求过程中的各种错误，防止应用崩溃，并提供详细的错误信息或友好的错误提示。

## 错误处理中间件

Koa 提供了一个非常简洁的方式来进行全局错误处理：通过创建一个中间件来捕获和处理所有的错误。以下是一个简单的示例，展示了如何实现全局错误处理中间件。

### 示例：全局错误处理中间件

```js
// app.js
const Koa = require('koa');
const app = new Koa();

// 错误处理中间件
app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    // 捕获错误
    ctx.status = err.status || 500;
    ctx.body = {
      message: err.message,
      stack: process.env.NODE_ENV === 'development' ? err.stack : ''
    };

    // 打印错误信息
    console.error('Error occurred:', err);
  }
});

// 示例路由
app.use(async ctx => {
  if (ctx.path === '/error') {
    throw new Error('Something went wrong!');
  }
  ctx.body = 'Hello, World!';
});

// 启动服务
app.listen(3000, () => {
  console.log('Koa server is running on port 3000');
});
````

在上面的例子中，`app.use` 中的错误处理中间件通过 `try...catch` 捕获请求过程中发生的错误，并设置相应的 HTTP 状态码和错误消息。

* 如果 `NODE_ENV` 是 `development`，将输出详细的堆栈信息，有助于调试；
* 如果是生产环境，则只返回简洁的错误消息。

### 错误处理中间件的工作原理

* 在请求处理的过程中，错误可能发生在任何地方（如数据库操作、API 调用等）。
* 通过在中间件中使用 `try...catch`，我们可以捕获这些错误，避免应用崩溃。
* 错误被捕获后，Koa 会根据错误的类型和状态码返回适当的 HTTP 响应。

## 自定义错误类型

有时候，除了捕获常规错误外，可能需要定义特定类型的错误。例如，某些错误可能是由于无效输入导致的，或者是由于请求的数据不存在。我们可以通过自定义错误类型来更清晰地描述错误。

### 示例：自定义错误类型

```js
class NotFoundError extends Error {
  constructor(message) {
    super(message);
    this.status = 404;
    this.name = 'NotFoundError';
  }
}

class ValidationError extends Error {
  constructor(message) {
    super(message);
    this.status = 400;
    this.name = 'ValidationError';
  }
}

// 错误处理中间件
app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    if (err instanceof NotFoundError) {
      ctx.status = err.status;
      ctx.body = { message: err.message };
    } else if (err instanceof ValidationError) {
      ctx.status = err.status;
      ctx.body = { message: err.message };
    } else {
      ctx.status = err.status || 500;
      ctx.body = { message: 'Internal Server Error' };
    }
    console.error('Error occurred:', err);
  }
});
```

在此例中，我们定义了 `NotFoundError` 和 `ValidationError` 两个自定义错误类型。这样，错误处理中间件可以根据错误类型进行有针对性的处理。

## 错误日志记录

除了将错误信息返回给客户端，通常还需要将错误信息记录到日志中，以便后续排查。可以使用像 `winston`、`log4js` 等日志库来实现错误日志的记录。

### 示例：使用 `winston` 记录错误日志

```bash
npm install winston
```

```js
const winston = require('winston');

// 创建一个日志记录器
const logger = winston.createLogger({
  level: 'error',
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
  ],
});

// 错误处理中间件
app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    // 记录错误日志
    logger.error(`${err.name}: ${err.message}\n${err.stack}`);

    ctx.status = err.status || 500;
    ctx.body = { message: 'Internal Server Error' };
  }
});
```

通过使用 `winston`，我们将错误信息记录到 `error.log` 文件中，这对于生产环境中的问题排查非常有帮助。

## 错误处理的最佳实践

1. **统一处理**：使用全局错误处理中间件来统一处理所有错误，而不是在每个路由或处理函数中手动捕获错误。
2. **详细的错误信息**：在开发环境中返回详细的堆栈信息，以便开发人员调试；在生产环境中，返回简洁的错误信息，防止泄漏敏感信息。
3. **自定义错误类型**：根据不同的错误情况自定义错误类型，使得错误处理更加清晰和结构化。
4. **错误日志记录**：将错误信息记录到日志文件中，以便日后查看和排查问题。

通过实现全局错误处理机制，可以让 Koa 应用更加健壮，提高应用的稳定性和可维护性。
