生命周期 #

一、生命周期概述 #

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
  }
})

九、总结 #

本章我们学习了:

  1. 生命周期概述:完整流程和阶段说明
  2. 请求阶段:onRequest、preParsing、preValidation、preHandler
  3. 响应阶段:preSerialization、onSend、onResponse
  4. 错误处理:onError钩子
  5. 应用生命周期:onReady、onClose、onRoute、onRegister
  6. 执行顺序:应用级与路由级、多钩子、提前终止
  7. 生命周期调试:调试钩子、性能监控
  8. 最佳实践:钩子组织、条件性钩子、错误处理

接下来让我们学习配置管理!

最后更新:2026-03-28