序列化响应 #
一、序列化概述 #
Fastify使用fast-json-stringify进行高性能JSON序列化,比JSON.stringify快2-3倍。
1.1 序列化流程 #
text
Handler返回数据
│
▼
┌─────────────────┐
│ preSerialization│ ← 钩子处理
└────────┬────────┘
│
▼
┌─────────────────┐
│ Schema序列化 │ ← fast-json-stringify
└────────┬────────┘
│
▼
┌─────────────────┐
│ onSend │ ← 发送前钩子
└────────┬────────┘
│
▼
发送响应
1.2 性能对比 #
| 方法 | 性能 |
|---|---|
| JSON.stringify | ~400,000 ops/s |
| fast-json-stringify | ~1,200,000 ops/s |
二、Schema序列化 #
2.1 基本序列化 #
javascript
fastify.get('/users/:id', {
schema: {
response: {
200: {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' },
email: { type: 'string' }
}
}
}
}
}, async (request, reply) => {
return {
id: 1,
name: 'John',
email: 'john@example.com',
password: 'secret'
}
})
响应(password被过滤):
json
{
"id": 1,
"name": "John",
"email": "john@example.com"
}
2.2 数组序列化 #
javascript
fastify.get('/users', {
schema: {
response: {
200: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' }
}
}
}
}
}
}, async (request, reply) => {
return [
{ id: 1, name: 'John', password: 'secret' },
{ id: 2, name: 'Jane', password: 'secret' }
]
})
2.3 嵌套对象序列化 #
javascript
fastify.get('/users/:id/profile', {
schema: {
response: {
200: {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' },
profile: {
type: 'object',
properties: {
bio: { type: 'string' },
avatar: { type: 'string' }
}
}
}
}
}
}
}, handler)
2.4 多状态码序列化 #
javascript
fastify.get('/users/:id', {
schema: {
response: {
200: {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' }
}
},
404: {
type: 'object',
properties: {
error: { type: 'string' },
message: { type: 'string' }
}
}
}
}
}, async (request, reply) => {
const user = await findUser(request.params.id)
if (!user) {
reply.code(404)
return { error: 'Not Found', message: 'User not found' }
}
return user
})
三、共享Schema #
3.1 定义共享Schema #
javascript
fastify.addSchema({
$id: 'user',
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' },
email: { type: 'string' }
}
})
fastify.addSchema({
$id: 'error',
type: 'object',
properties: {
error: { type: 'string' },
message: { type: 'string' }
}
})
3.2 使用共享Schema #
javascript
fastify.get('/users/:id', {
schema: {
response: {
200: { $ref: 'user#' },
404: { $ref: 'error#' }
}
}
}, handler)
fastify.get('/users', {
schema: {
response: {
200: {
type: 'array',
items: { $ref: 'user#' }
}
}
}
}, handler)
四、自定义序列化器 #
4.1 路由级序列化器 #
javascript
fastify.get('/custom', {
serializerCompiler: ({ schema, method, url, httpStatus }) => {
return (data) => JSON.stringify({
success: true,
data,
timestamp: Date.now()
})
}
}, async (request, reply) => {
return { message: 'Hello' }
})
4.2 全局序列化器 #
javascript
fastify.setSerializerCompiler(({ schema, method, url, httpStatus }) => {
return (data) => {
return JSON.stringify({
success: true,
data,
timestamp: new Date().toISOString()
})
}
})
4.3 条件序列化 #
javascript
fastify.get('/conditional', {
serializerCompiler: ({ schema, method, url, httpStatus }) => {
return (data) => {
if (httpStatus === 200) {
return JSON.stringify({
success: true,
data
})
}
return JSON.stringify({
success: false,
error: data
})
}
}
}, handler)
五、禁用序列化 #
5.1 路由级禁用 #
javascript
fastify.get('/raw', {
schema: {
response: {
200: {}
}
}
}, async (request, reply) => {
reply.serializer(null)
return 'Raw string response'
})
5.2 使用reply.serializer #
javascript
fastify.get('/manual', async (request, reply) => {
reply.serializer(null)
reply.type('text/plain')
return 'Plain text response'
})
六、响应包装 #
6.1 统一响应格式 #
javascript
fastify.addHook('preSerialization', async (request, reply, payload) => {
if (payload && typeof payload === 'object' && !payload.statusCode) {
return {
success: true,
data: payload,
timestamp: new Date().toISOString(),
requestId: request.id
}
}
return payload
})
6.2 分页响应包装 #
javascript
fastify.decorateReply('paginate', function (items, total, page, limit) {
return this.send({
success: true,
data: items,
meta: {
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total,
totalPages: Math.ceil(total / limit)
},
timestamp: new Date().toISOString()
}
})
})
fastify.get('/users', async (request, reply) => {
const { page = 1, limit = 10 } = request.query
const users = await getUsers(page, limit)
const total = await countUsers()
reply.paginate(users, total, page, limit)
})
6.3 错误响应包装 #
javascript
fastify.setErrorHandler((error, request, reply) => {
const statusCode = error.statusCode || 500
reply.code(statusCode).send({
success: false,
error: {
code: statusCode,
message: error.message
},
timestamp: new Date().toISOString(),
requestId: request.id
})
})
七、性能优化 #
7.1 使用Schema #
始终使用Schema进行序列化以获得最佳性能:
javascript
fastify.get('/users/:id', {
schema: {
response: {
200: {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' }
}
}
}
}
}, handler)
7.2 避免动态Schema #
javascript
const userSchema = {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' }
}
}
fastify.get('/users/:id', {
schema: {
response: {
200: userSchema
}
}
}, handler)
7.3 使用共享Schema #
javascript
fastify.addSchema({
$id: 'user',
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' }
}
})
fastify.get('/users/:id', {
schema: {
response: {
200: { $ref: 'user#' }
}
}
}, handler)
fastify.get('/users', {
schema: {
response: {
200: {
type: 'array',
items: { $ref: 'user#' }
}
}
}
}, handler)
八、特殊类型序列化 #
8.1 Date类型 #
javascript
fastify.get('/date', {
schema: {
response: {
200: {
type: 'object',
properties: {
date: { type: 'string', format: 'date-time' }
}
}
}
}
}, async (request, reply) => {
return { date: new Date() }
})
8.2 BigInt类型 #
javascript
fastify.get('/bigint', {
schema: {
response: {
200: {
type: 'object',
properties: {
value: { type: 'string' }
}
}
}
}
}, async (request, reply) => {
return { value: BigInt(9007199254740991).toString() }
})
8.3 Buffer类型 #
javascript
fastify.get('/buffer', async (request, reply) => {
const buffer = Buffer.from('Hello')
reply.type('application/octet-stream')
return buffer
})
8.4 Stream类型 #
javascript
const fs = require('fs')
fastify.get('/stream', async (request, reply) => {
const stream = fs.createReadStream('./file.txt')
reply.type('text/plain')
return stream
})
九、调试序列化 #
9.1 查看序列化结果 #
javascript
fastify.addHook('preSerialization', async (request, reply, payload) => {
console.log('Before serialization:', payload)
return payload
})
fastify.addHook('onSend', async (request, reply, payload) => {
console.log('After serialization:', payload)
return payload
})
9.2 序列化错误处理 #
javascript
fastify.addHook('onError', async (request, reply, error) => {
if (error.message.includes('serialization')) {
fastify.log.error('Serialization error:', error)
}
})
十、最佳实践 #
10.1 Schema组织 #
javascript
const schemas = {
user: {
$id: 'user',
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' },
email: { type: 'string' }
}
},
userList: {
$id: 'userList',
type: 'array',
items: { $ref: 'user#' }
},
error: {
$id: 'error',
type: 'object',
properties: {
error: { type: 'string' },
message: { type: 'string' }
}
}
}
for (const [name, schema] of Object.entries(schemas)) {
fastify.addSchema(schema)
}
10.2 响应格式统一 #
javascript
const responseSchema = {
success: {
type: 'object',
properties: {
success: { type: 'boolean', const: true },
data: { type: 'object' },
timestamp: { type: 'string', format: 'date-time' }
}
},
error: {
type: 'object',
properties: {
success: { type: 'boolean', const: false },
error: { type: 'object' },
timestamp: { type: 'string', format: 'date-time' }
}
}
}
10.3 版本控制 #
javascript
fastify.addSchema({
$id: 'userV1',
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' }
}
})
fastify.addSchema({
$id: 'userV2',
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' },
email: { type: 'string' }
}
})
十一、总结 #
本章我们学习了:
- 序列化概述:fast-json-stringify的性能优势
- Schema序列化:基本序列化、数组、嵌套对象、多状态码
- 共享Schema:定义和使用共享Schema
- 自定义序列化器:路由级、全局、条件序列化
- 禁用序列化:路由级禁用、手动禁用
- 响应包装:统一格式、分页包装、错误包装
- 性能优化:使用Schema、避免动态Schema、共享Schema
- 特殊类型:Date、BigInt、Buffer、Stream
- 调试序列化:查看结果、错误处理
- 最佳实践:Schema组织、格式统一、版本控制
接下来让我们学习高级特性!
最后更新:2026-03-28