# 中间件执行顺序

URL: https://caijiao.org/express/chapter7/3-order
Source: docs/express/chapter7/3-order.md
Description: 本章节介绍中间件在 Express 应用中的执行顺序，以及如何控制中间件的执行顺序以确保正确的请求处理。

在 Express 中，中间件的执行顺序非常重要。它决定了请求如何在应用中流动以及如何响应客户端。中间件函数按注册的顺序依次执行，通常包含多个阶段，例如请求预处理、路由匹配、响应处理等。了解中间件的执行顺序能够帮助我们更好地设计应用的请求处理逻辑。

## 中间件执行顺序概述

1. **应用级中间件**：通过 `app.use()` 或 `app.METHOD()` 注册的中间件按注册顺序执行。
2. **路由中间件**：路由特定的中间件在匹配到相应路由时执行。
3. **错误处理中间件**：错误处理中间件必须放在所有路由和常规中间件之后，它有 4 个参数：`err`, `req`, `res`, `next`。
4. **静态文件服务**：静态文件服务的中间件通常需要在路由处理之前注册。

## 中间件的顺序

### 1. 全局中间件

通过 `app.use()` 注册的全局中间件会在所有路由之前执行，适用于所有请求。

```js
app.use((req, res, next) => {
  console.log('全局中间件：请求开始');
  next(); // 传递控制权给下一个中间件
});
````

### 2. 路由特定中间件

路由特定的中间件只在匹配到某个特定路由时执行，它们可以被绑定在路由定义时，优先执行。

```js
app.get('/user', (req, res, next) => {
  console.log('用户路由中间件');
  next();
}, (req, res) => {
  res.send('用户信息');
});
```

### 3. 静态资源服务

静态资源服务的中间件应该在路由之前注册，因为它会处理客户端请求的静态文件（如 HTML、CSS、JS、图片等）。如果静态文件存在且可以响应，则不会继续执行其他中间件。

```js
app.use(express.static('public')); // 提供 public 目录中的静态文件

// 其他路由
app.get('/user', (req, res) => {
  res.send('用户信息');
});
```

### 4. 错误处理中间件

错误处理中间件与普通中间件不同，它有 4 个参数：`err`, `req`, `res`, `next`，并且必须放在所有其他中间件和路由之后。它的主要作用是捕获并处理请求过程中抛出的错误。

```js
// 错误处理中间件放在所有路由后面
app.use((err, req, res, next) => {
  console.error('发生了错误:', err.stack);
  res.status(500).send('服务器错误');
});
```

### 5. 中间件的调用顺序

1. **全局中间件**：按注册顺序执行，适用于所有路由。
2. **路由特定中间件**：只有在匹配到相应路由时才会执行。
3. **静态文件中间件**：静态文件服务会拦截匹配的请求，如果请求的是静态文件，响应会提前返回，后续的中间件将不再执行。
4. **错误处理中间件**：用于捕获所有中间件和路由中的错误，放置在最后，确保能够处理未处理的错误。

### 6. 顺序的影响

中间件执行顺序对应用行为至关重要。以下是几个常见的顺序错误：

* **静态资源服务位置不正确**：如果 `express.static()` 放在了路由之前，静态文件请求将不会被正确处理，可能会导致静态资源无法加载。
* **错误处理中间件位置不正确**：错误处理中间件必须放在所有路由和其他中间件的后面，否则它将无法捕获错误。
* **中间件漏掉 `next()` 调用**：如果在中间件中忘记调用 `next()`，请求将会挂起，后续中间件无法继续执行。

### 7. 实际示例：顺序错误与修正

#### 错误示例：

```js
// 错误的顺序
app.use((req, res, next) => {
  console.log('请求开始');
  next();
});

app.use(express.static('public')); // 静态文件中间件的位置不对

app.get('/user', (req, res) => {
  res.send('用户信息');
});
```

在上述代码中，静态文件服务中间件注册在了路由之后，导致静态文件请求不能正常返回。

#### 正确示例：

```js
// 正确的顺序
app.use(express.static('public')); // 先处理静态文件

app.use((req, res, next) => {
  console.log('请求开始');
  next();
});

app.get('/user', (req, res) => {
  res.send('用户信息');
});
```

在正确的顺序中，静态文件请求将优先被 `express.static()` 中间件处理，其他的路由和中间件不会影响静态文件的返回。

## 中间件执行顺序图示

以下是中间件的典型执行顺序图：

```
请求 -> 全局中间件 -> 静态文件服务 -> 路由特定中间件 -> 错误处理中间件 -> 响应
```

* **全局中间件**：最先执行，适用于所有请求。
* **静态文件服务**：如果请求的文件在静态目录中，则响应文件并结束请求。
* **路由特定中间件**：当请求匹配到特定路由时才执行。
* **错误处理中间件**：在其他所有中间件和路由之后，捕获并处理错误。

## 总结

理解中间件的执行顺序是编写 Express 应用的关键，它能够帮助我们更好地控制请求和响应的流程。通过合理的顺序安排，能够确保应用在处理请求时的正确性和性能。特别是在涉及多个中间件、路由和错误处理时，顺序的正确性尤为重要。

* **全局中间件**：在所有请求中都会执行。
* **路由特定中间件**：只在匹配特定路由时执行。
* **静态文件服务**：应该在路由之前注册，处理静态文件请求。
* **错误处理中间件**：应放在所有路由和中间件后面，用于捕获错误。

通过灵活地控制中间件的顺序，可以创建高效且易于维护的 Express 应用。
