Q13: 什么是中间件(Middleware)?它的工作原理是什么?

60 阅读4分钟

Node.js 面试题详细答案 - Q13

Q13: 什么是中间件(Middleware)?它的工作原理是什么?

中间件概述

中间件是在请求和响应之间执行的函数,用于处理请求、修改响应或执行其他操作。

基本工作原理

1. 中间件函数结构
// 基本中间件函数
function middleware(req, res, next) {
  // 处理请求
  console.log('处理请求:', req.url)

  // 调用下一个中间件
  next()
}

// 错误处理中间件
function errorMiddleware(err, req, res, next) {
  console.error('错误:', err)
  res.status(500).send('服务器错误')
}
2. 中间件执行流程
const express = require('express')
const app = express()

// 中间件 1
app.use((req, res, next) => {
  console.log('中间件 1 - 开始')
  req.startTime = Date.now()
  next()
  console.log('中间件 1 - 结束')
})

// 中间件 2
app.use((req, res, next) => {
  console.log('中间件 2 - 开始')
  req.user = 'John'
  next()
  console.log('中间件 2 - 结束')
})

// 路由处理
app.get('/', (req, res) => {
  console.log('路由处理')
  res.send('Hello World')
})

app.listen(3000)

中间件类型

1. 应用级中间件
const express = require('express')
const app = express()

// 所有请求都会经过
app.use((req, res, next) => {
  console.log('应用级中间件')
  next()
})

// 特定路径的中间件
app.use('/api', (req, res, next) => {
  console.log('API 中间件')
  next()
})

// 特定方法的中间件
app.get('/users', (req, res, next) => {
  console.log('GET /users 中间件')
  next()
})
2. 路由级中间件
const express = require('express')
const router = express.Router()

// 路由中间件
router.use((req, res, next) => {
  console.log('路由中间件')
  next()
})

// 特定路由的中间件
router.get(
  '/profile',
  (req, res, next) => {
    console.log('用户资料中间件')
    next()
  },
  (req, res) => {
    res.send('用户资料')
  }
)

module.exports = router
3. 错误处理中间件
const express = require('express')
const app = express()

// 错误处理中间件
app.use((err, req, res, next) => {
  console.error('错误:', err)

  if (err.type === 'validation') {
    res.status(400).json({ error: '验证错误' })
  } else {
    res.status(500).json({ error: '服务器错误' })
  }
})

// 触发错误
app.get('/error', (req, res, next) => {
  const error = new Error('测试错误')
  error.type = 'validation'
  next(error)
})

实际应用场景

1. 日志中间件
// 日志中间件
function loggerMiddleware(req, res, next) {
  const start = Date.now()

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

  next()
}

// 使用日志中间件
app.use(loggerMiddleware)
2. 认证中间件
// 认证中间件
function authMiddleware(req, res, next) {
  const token = req.headers.authorization

  if (!token) {
    return res.status(401).json({ error: '未提供认证令牌' })
  }

  // 验证令牌
  if (token === 'valid-token') {
    req.user = { id: 1, name: 'John' }
    next()
  } else {
    res.status(401).json({ error: '无效的认证令牌' })
  }
}

// 受保护的路由
app.get('/protected', authMiddleware, (req, res) => {
  res.json({ message: '受保护的内容', user: req.user })
})
3. 请求解析中间件
// JSON 解析中间件
function jsonParser(req, res, next) {
  if (req.headers['content-type'] === 'application/json') {
    let body = ''

    req.on('data', (chunk) => {
      body += chunk.toString()
    })

    req.on('end', () => {
      try {
        req.body = JSON.parse(body)
        next()
      } catch (error) {
        res.status(400).json({ error: '无效的 JSON' })
      }
    })
  } else {
    next()
  }
}

// 使用解析中间件
app.use(jsonParser)
app.post('/data', (req, res) => {
  res.json({ received: req.body })
})
4. 跨域中间件
// CORS 中间件
function corsMiddleware(req, res, next) {
  res.header('Access-Control-Allow-Origin', '*')
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization')

  if (req.method === 'OPTIONS') {
    res.sendStatus(200)
  } else {
    next()
  }
}

app.use(corsMiddleware)

中间件组合

1. 多个中间件组合
// 多个中间件函数
function middleware1(req, res, next) {
  console.log('中间件 1')
  next()
}

function middleware2(req, res, next) {
  console.log('中间件 2')
  next()
}

function middleware3(req, res, next) {
  console.log('中间件 3')
  next()
}

// 组合使用
app.use('/api', [middleware1, middleware2, middleware3])
2. 条件中间件
// 条件中间件
function conditionalMiddleware(condition) {
  return (req, res, next) => {
    if (condition(req)) {
      console.log('条件满足,执行中间件')
      next()
    } else {
      console.log('条件不满足,跳过中间件')
      next()
    }
  }
}

// 使用条件中间件
app.use(conditionalMiddleware((req) => req.url.startsWith('/api')))

异步中间件

1. 异步操作中间件
// 异步中间件
async function asyncMiddleware(req, res, next) {
  try {
    // 模拟异步操作
    const data = await fetchDataFromDatabase()
    req.data = data
    next()
  } catch (error) {
    next(error)
  }
}

// 使用异步中间件
app.use(asyncMiddleware)
2. 数据库连接中间件
// 数据库中间件
function databaseMiddleware(req, res, next) {
  // 模拟数据库连接
  req.db = {
    query: (sql) => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve({ rows: [] })
        }, 100)
      })
    },
  }
  next()
}

// 使用数据库中间件
app.use(databaseMiddleware)
app.get('/users', async (req, res) => {
  const result = await req.db.query('SELECT * FROM users')
  res.json(result.rows)
})

中间件最佳实践

1. 错误处理
// 错误处理中间件
function errorHandler(err, req, res, next) {
  console.error('错误:', err)

  // 根据错误类型返回不同响应
  if (err.name === 'ValidationError') {
    res.status(400).json({ error: '验证错误', details: err.message })
  } else if (err.name === 'UnauthorizedError') {
    res.status(401).json({ error: '未授权' })
  } else {
    res.status(500).json({ error: '服务器错误' })
  }
}

// 使用错误处理中间件
app.use(errorHandler)
2. 性能监控
// 性能监控中间件
function performanceMiddleware(req, res, next) {
  const start = Date.now()

  res.on('finish', () => {
    const duration = Date.now() - start
    if (duration > 1000) {
      console.warn(`慢请求: ${req.method} ${req.url} - ${duration}ms`)
    }
  })

  next()
}

app.use(performanceMiddleware)
3. 请求限制
// 请求限制中间件
const requestCounts = new Map()

function rateLimitMiddleware(maxRequests = 100, windowMs = 60000) {
  return (req, res, next) => {
    const clientIp = req.ip
    const now = Date.now()
    const windowStart = now - windowMs

    // 清理过期记录
    if (requestCounts.has(clientIp)) {
      const requests = requestCounts.get(clientIp)
      const validRequests = requests.filter((time) => time > windowStart)
      requestCounts.set(clientIp, validRequests)
    }

    // 检查请求数量
    const currentRequests = requestCounts.get(clientIp) || []
    if (currentRequests.length >= maxRequests) {
      return res.status(429).json({ error: '请求过于频繁' })
    }

    // 记录请求
    currentRequests.push(now)
    requestCounts.set(clientIp, currentRequests)

    next()
  }
}

app.use(rateLimitMiddleware(10, 60000)) // 每分钟最多 10 次请求

总结

  • 中间件作用:在请求和响应之间执行逻辑
  • 工作原理:函数接收 req, res, next 参数,通过 next() 传递控制权
  • 类型分类:应用级、路由级、错误处理中间件
  • 执行顺序:按照注册顺序依次执行
  • 异步支持:支持异步操作和错误处理
  • 最佳实践:错误处理、性能监控、请求限制
  • 应用场景:认证、日志、解析、跨域等