首页 / Node.js 教程 / 中间件

Node.js 教程

中间件

本教程共 76 篇 · 第 40 篇 · 更新于 2026-07-25 · 约 6 分钟阅读

Node.jsExpress中间件next错误处理

40. 中间件

本节目标:四种中间件层级、执行顺序和 Express 5 原生异步错误捕获。

中间件(Middleware)是 Express 的灵魂。官方对它的定义很直白:中间件是能访问请求对象、响应对象和 next 函数的函数。说它「灵魂」,是因为 Express 自身几乎不做任何事——路由、body 解析、静态文件、错误处理,全是中间件在干活。

你可以把中间件想象成工厂流水线上的工位。一个请求进来,依次经过日志记录、身份校验、参数解析、业务处理,最后包装成响应返回。每个工位只做一件事,做完把工件传给下一站。

中间件的基本形态

一个最简中间件长这样:

function myMiddleware(req, res, next) {
  console.log(`${req.method} ${req.url}`)
  next()   // 必须调用,否则请求卡死
}

app.use(myMiddleware)

app.use() 表示「所有请求都经过这个中间件」。如果你想限定路径:

// 只拦截 /api 开头的请求
app.use('/api', myMiddleware)

Express 5 对异步中间件做了重要改进:如果中间件返回一个 rejected Promise,错误会自动传给错误处理中间件,不再需要手动包 try/catchnext(err)

// Express 5:Promise 被拒会自动进入错误处理中间件
app.get('/data', async (req, res) => {
  const data = await fetchFromDatabase()  // 如果抛错,自动走错误中间件
  res.json(data)
})
Note

Express 4 里异步函数抛错会导致请求挂起,必须用 try/catch 或者 express-async-handler 这类库包裹。Express 5 终于原生支持了,这是升级 5 最爽的理由之一。

中间件的四种层级

1. 应用级中间件

绑定在 app 实例上,影响整个应用:

app.use(express.json())        // 解析 JSON body
app.use(express.urlencoded({ extended: true }))  // 解析表单
app.use(express.static('public'))  // 静态文件

2. 路由级中间件

绑定在 express.Router() 上,只影响该路由模块:

import express from 'express'
const router = express.Router()

router.use((req, res, next) => {
  console.log('Users router time:', Date.now())
  next()
})

router.get('/', (req, res) => res.json([]))
export default router

3. 路由特定中间件

只作用于某一条路由:

function requireAuth(req, res, next) {
  if (!req.headers.authorization) {
    return res.status(401).json({ error: 'Unauthorized' })
  }
  next()
}

app.post('/admin', requireAuth, (req, res) => {
  res.json({ message: 'Admin only' })
})

你可以连续挂多个:

app.post('/users', validateUser, requireAuth, createUser)

4. 错误处理中间件

这是唯一一种带四个参数的中间件,Express 靠参数数量来识别它:

app.use((err, req, res, next) => {
  console.error(err.stack)
  res.status(err.status || 500).json({
    error: process.env.NODE_ENV === 'production'
      ? 'Internal Server Error'
      : err.message
  })
})
Warning

错误处理中间件必须放在所有其他 app.use() 和路由之后。如果放在前面,它捕获不到后面抛出的错误。另外,四个参数缺一不可,哪怕 next 不用,也得写上,否则 Express 把它当成普通中间件。

常用内置中间件

Express 5 内置了几个最基础的中间件,开箱即用:

中间件作用示例
express.json()解析 Content-Type: application/json 的请求体app.use(express.json())
express.urlencoded()解析表单数据app.use(express.urlencoded({ extended: true }))
express.static()托管静态文件app.use(express.static('public'))
express.raw()解析原始二进制app.use(express.raw())
express.text()解析纯文本app.use(express.text())

express.urlencoded({ extended: true }) 里的 extended 选项控制解析器是否支持嵌套对象。设为 trueqs 库,能解析 a[b]=1 这种嵌套;设为 false 用原生 querystring,只支持平铺。一般保持 true 即可。

第三方中间件推荐

社区生态是 Express 最大的底气。下面这几个包我几乎每个项目都会装:

npm install morgan helmet cors compression
import morgan from 'morgan'       // HTTP 请求日志
import helmet from 'helmet'       // 安全响应头
import cors from 'cors'           // 跨域
import compression from 'compression'  // gzip 压缩

app.use(morgan('dev'))
app.use(helmet())
app.use(cors())
app.use(compression())
  • morgan:开发时看请求日志很方便,combined 格式适合生产接日志分析系统
  • helmet:帮你设好一堆安全头,防 XSS、点击劫持等,省得自己背参数
  • cors:一键开启跨域,支持配置白名单
  • compression:对响应体做 gzip,省带宽

自定义中间件实战

假设我们需要一个「请求计时」中间件,记录每个接口的处理耗时:

function requestTimer(req, res, next) {
  const start = performance.now()

  res.on('finish', () => {
    const duration = (performance.now() - start).toFixed(2)
    console.log(`${req.method} ${req.url} - ${res.statusCode} - ${duration}ms`)
  })

  next()
}

app.use(requestTimer)

这里用了 res.on('finish') 监听响应发送完毕的事件。注意中间件里不要做太重的事,否则每个请求都拖慢。

再写一个「统一响应格式」的中间件,给 res 挂一个快捷方法:

app.use((req, res, next) => {
  res.sendSuccess = (data, message = 'ok') => {
    res.json({ code: 0, message, data })
  }
  res.sendError = (message, code = 500) => {
    res.status(code).json({ code, message, data: null })
  }
  next()
})

// 后续路由里可以这样用
app.get('/users', (req, res) => {
  res.sendSuccess([{ id: 1, name: 'tom' }])
})

执行顺序:为什么我的中间件没生效

这是新手最高频的问题之一。Express 中间件按代码顺序执行,所以:

// 错误:body 还没解析,就读 req.body
app.post('/users', (req, res) => {
  console.log(req.body)  // undefined
})
app.use(express.json())

// 正确
app.use(express.json())
app.post('/users', (req, res) => {
  console.log(req.body)  // 正常
})

推荐顺序:

  1. 安全类(helmet、cors)
  2. 日志类(morgan)
  3. 解析类(json、urlencoded、static)
  4. 自定义全局中间件
  5. 路由
  6. 404 兜底
  7. 错误处理中间件