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() 传递控制权
- 类型分类:应用级、路由级、错误处理中间件
- 执行顺序:按照注册顺序依次执行
- 异步支持:支持异步操作和错误处理
- 最佳实践:错误处理、性能监控、请求限制
- 应用场景:认证、日志、解析、跨域等