生命周期 #
一、生命周期概述 #
Fastify的请求生命周期定义了请求从接收到响应的完整流程。
1.1 完整生命周期 #
text
Incoming Request
│
▼
┌─────────────────┐
│ onRequest │
└────────┬────────┘
│
▼
┌─────────────────┐
│ preParsing │
└────────┬────────┘
│
▼
┌─────────────────┐
│ parsing │
└────────┬────────┘
│
▼
┌─────────────────┐
│ preValidation │
└────────┬────────┘
│
▼
┌─────────────────┐
│ validation │
└────────┬────────┘
│
▼
┌─────────────────┐
│ preHandler │
└────────┬────────┘
│
▼
┌─────────────────┐
│ handler │
└────────┬────────┘
│
▼
┌─────────────────┐
│ preSerialization│
└────────┬────────┘
│
▼
┌─────────────────┐
│ serialization │
└────────┬────────┘
│
▼
┌─────────────────┐
│ onSend │
└────────┬────────┘
│
▼
┌─────────────────┐
│ onResponse │
└────────┬────────┘
│
▼
Response Sent
1.2 生命周期阶段 #
| 阶段 | 说明 | 可修改内容 |
|---|---|---|
| onRequest | 请求开始 | 请求对象 |
| preParsing | 解析前 | 请求体流 |
| parsing | 解析请求体 | - |
| preValidation | 验证前 | 请求数据 |
| validation | Schema验证 | - |
| preHandler | 处理前 | 请求数据 |
| handler | 路由处理 | 响应数据 |
| preSerialization | 序列化前 | 响应数据 |
| serialization | 序列化响应 | - |
| onSend | 发送前 | 响应内容 |
| onResponse | 响应后 | - |
二、请求阶段 #
2.1 onRequest #
请求生命周期的第一个阶段。
javascript
fastify.addHook('onRequest', async (request, reply) => {
request.startTime = Date.now()
request.id = generateRequestId()
console.log('Request started:', request.id)
})
fastify.addHook('onRequest', async (request, reply) => {
if (request.headers['x-block'] === 'true') {
reply.code(403).send({ error: 'Blocked' })
return reply
}
})
用途:
- 请求追踪
- 认证检查
- 请求过滤
- 初始化数据
2.2 preParsing #
解析请求体前的阶段。
javascript
fastify.addHook('preParsing', async (request, reply, payload) => {
const encoding = request.headers['content-encoding']
if (encoding === 'gzip') {
const zlib = require('zlib')
return payload.pipe(zlib.createGunzip())
}
return payload
})
用途:
- 解压缩请求体
- 修改请求体流
- 预处理原始数据
2.3 preValidation #
Schema验证前的阶段。
javascript
fastify.addHook('preValidation', async (request, reply) => {
if (request.body && typeof request.body === 'string') {
try {
request.body = JSON.parse(request.body)
} catch (e) {
reply.code(400).send({ error: 'Invalid JSON' })
return reply
}
}
})
fastify.addHook('preValidation', async (request, reply) => {
request.body = {
...request.body,
timestamp: Date.now(),
ip: request.ip
}
})
用途:
- 数据预处理
- 添加默认值
- 数据转换
2.4 preHandler #
路由处理前的阶段,最常用的钩子。
javascript
fastify.addHook('preHandler', async (request, reply) => {
const token = request.headers.authorization
if (!token) {
reply.code(401).send({ error: 'Unauthorized' })
return
}
try {
request.user = await verifyToken(token)
} catch (err) {
reply.code(401).send({ error: 'Invalid token' })
}
})
fastify.addHook('preHandler', async (request, reply) => {
if (request.params.id) {
request.resource = await fastify.db.collection('resources').findOne({
_id: request.params.id
})
if (!request.resource) {
reply.code(404).send({ error: 'Resource not found' })
}
}
})
用途:
- 认证处理
- 权限检查
- 数据加载
- 业务逻辑预处理
三、响应阶段 #
3.1 preSerialization #
响应序列化前的阶段。
javascript
fastify.addHook('preSerialization', async (request, reply, payload) => {
if (payload && typeof payload === 'object') {
return {
success: true,
data: payload,
timestamp: new Date().toISOString()
}
}
return payload
})
fastify.addHook('preSerialization', async (request, reply, payload) => {
if (payload && payload.password) {
const { password, ...safe } = payload
return safe
}
return payload
})
用途:
- 响应包装
- 数据过滤
- 格式转换
3.2 onSend #
响应发送前的阶段。
javascript
fastify.addHook('onSend', async (request, reply, payload) => {
reply.header('X-Response-Time', Date.now() - request.startTime)
reply.header('X-Request-Id', request.id)
return payload
})
fastify.addHook('onSend', async (request, reply, payload) => {
if (request.query.pretty === 'true' && typeof payload === 'string') {
try {
const json = JSON.parse(payload)
return JSON.stringify(json, null, 2)
} catch (e) {
return payload
}
}
return payload
})
用途:
- 添加响应头
- 修改响应内容
- 格式化输出
3.3 onResponse #
响应发送后的阶段。
javascript
fastify.addHook('onResponse', async (request, reply) => {
const duration = Date.now() - request.startTime
fastify.log.info({
method: request.method,
url: request.url,
statusCode: reply.statusCode,
duration: `${duration}ms`,
requestId: request.id
})
})
fastify.addHook('onResponse', async (request, reply) => {
stats.requests++
stats.totalDuration += Date.now() - request.startTime
if (reply.statusCode >= 400) {
stats.errors++
}
})
用途:
- 日志记录
- 性能监控
- 统计收集
- 资源清理
四、错误处理阶段 #
4.1 onError #
错误发生时的阶段。
javascript
fastify.addHook('onError', async (request, reply, error) => {
fastify.log.error({
requestId: request.id,
method: request.method,
url: request.url,
error: {
name: error.name,
message: error.message,
stack: error.stack
}
})
})
fastify.addHook('onError', async (request, reply, error) => {
if (error.name === 'ValidationError') {
error.statusCode = 400
error.message = 'Validation failed'
}
if (error.name === 'MongoError' && error.code === 11000) {
error.statusCode = 409
error.message = 'Duplicate key error'
}
})
用途:
- 错误日志
- 错误转换
- 错误恢复
五、应用生命周期 #
5.1 onReady #
应用准备就绪时触发。
javascript
fastify.addHook('onReady', async () => {
console.log('Application is ready')
await fastify.db.connect()
await fastify.cache.initialize()
})
fastify.ready((err) => {
if (err) throw err
console.log('Server is ready')
})
5.2 onClose #
应用关闭时触发。
javascript
fastify.addHook('onClose', async (instance) => {
console.log('Application is closing')
await instance.db.close()
await instance.redis.quit()
})
process.on('SIGTERM', async () => {
await fastify.close()
process.exit(0)
})
5.3 onRoute #
路由注册时触发。
javascript
fastify.addHook('onRoute', (routeOptions) => {
console.log('Route registered:', routeOptions.method, routeOptions.url)
if (!routeOptions.schema) {
console.warn('Route without schema:', routeOptions.url)
}
})
5.4 onRegister #
插件注册时触发。
javascript
fastify.addHook('onRegister', (instance, opts) => {
console.log('Plugin registered:', opts.name || 'anonymous')
})
六、执行顺序 #
6.1 应用级与路由级 #
javascript
fastify.addHook('onRequest', async () => console.log('App onRequest'))
fastify.addHook('preHandler', async () => console.log('App preHandler'))
fastify.get('/test', {
onRequest: async () => console.log('Route onRequest'),
preHandler: async () => console.log('Route preHandler'),
handler: async () => {
console.log('Handler')
return { ok: true }
}
})
输出:
text
App onRequest
Route onRequest
App preHandler
Route preHandler
Handler
6.2 多钩子执行 #
javascript
fastify.addHook('preHandler', [
async () => console.log('Hook 1'),
async () => console.log('Hook 2'),
async () => console.log('Hook 3')
])
6.3 提前终止 #
javascript
fastify.addHook('onRequest', async (request, reply) => {
if (request.headers['x-block'] === 'true') {
reply.code(403).send({ error: 'Blocked' })
return reply
}
})
fastify.addHook('preHandler', async () => {
console.log('This will not be called if blocked')
})
七、生命周期调试 #
7.1 调试钩子 #
javascript
const stages = ['onRequest', 'preParsing', 'preValidation', 'preHandler',
'preSerialization', 'onSend', 'onResponse']
for (const stage of stages) {
fastify.addHook(stage, async (...args) => {
console.log(`[${stage}]`, args.length > 2 ? 'with payload' : '')
})
}
7.2 性能监控 #
javascript
fastify.addHook('onRequest', async (request, reply) => {
request.timings = { start: Date.now() }
})
const stages = ['preParsing', 'preValidation', 'preHandler', 'preSerialization', 'onSend']
for (const stage of stages) {
fastify.addHook(stage, async (request) => {
request.timings[stage] = Date.now()
})
}
fastify.addHook('onResponse', async (request, reply) => {
const timings = request.timings
console.log('Request timings:', {
total: Date.now() - timings.start,
stages: Object.entries(timings)
.filter(([k]) => k !== 'start')
.map(([k, v]) => ({ stage: k, time: v - timings.start }))
})
})
八、最佳实践 #
8.1 钩子组织 #
javascript
const hooks = {
logRequest: async (request, reply) => {
request.startTime = Date.now()
},
authenticate: async (request, reply) => {
// 认证逻辑
},
authorize: async (request, reply) => {
// 权限检查
}
}
fastify.addHook('onRequest', hooks.logRequest)
fastify.addHook('preHandler', [hooks.authenticate, hooks.authorize])
8.2 条件性钩子 #
javascript
fastify.addHook('preHandler', async (request, reply) => {
const config = request.routeConfig
if (config.auth) {
await authenticate(request, reply)
}
if (config.admin) {
await checkAdmin(request, reply)
}
})
8.3 错误处理 #
javascript
fastify.addHook('preHandler', async (request, reply) => {
try {
await riskyOperation()
} catch (err) {
fastify.log.error(err)
throw err
}
})
九、总结 #
本章我们学习了:
- 生命周期概述:完整流程和阶段说明
- 请求阶段:onRequest、preParsing、preValidation、preHandler
- 响应阶段:preSerialization、onSend、onResponse
- 错误处理:onError钩子
- 应用生命周期:onReady、onClose、onRoute、onRegister
- 执行顺序:应用级与路由级、多钩子、提前终止
- 生命周期调试:调试钩子、性能监控
- 最佳实践:钩子组织、条件性钩子、错误处理
接下来让我们学习配置管理!
最后更新:2026-03-28