数据验证 #
一、验证概述 #
Fastify内置了强大的JSON Schema验证功能,可以验证请求参数、请求体和响应数据。
1.1 验证类型 #
| 验证类型 | Schema属性 | 说明 |
|---|---|---|
| 路径参数 | params | URL路径参数验证 |
| 查询参数 | querystring | URL查询参数验证 |
| 请求头 | headers | HTTP请求头验证 |
| 请求体 | body | 请求体验证 |
| 响应体 | response | 响应数据验证 |
1.2 基本验证 #
javascript
fastify.post('/users', {
schema: {
body: {
type: 'object',
required: ['name', 'email'],
properties: {
name: { type: 'string' },
email: { type: 'string', format: 'email' }
}
}
}
}, async (request, reply) => {
return { created: true }
})
二、JSON Schema基础 #
2.1 类型定义 #
javascript
fastify.post('/types', {
schema: {
body: {
type: 'object',
properties: {
string: { type: 'string' },
integer: { type: 'integer' },
number: { type: 'number' },
boolean: { type: 'boolean' },
array: {
type: 'array',
items: { type: 'string' }
},
object: {
type: 'object',
properties: {
nested: { type: 'string' }
}
},
null: { type: 'null' }
}
}
}
}, handler)
2.2 必需字段 #
javascript
fastify.post('/required', {
schema: {
body: {
type: 'object',
required: ['name', 'email'],
properties: {
name: { type: 'string' },
email: { type: 'string' },
age: { type: 'integer' }
}
}
}
}, handler)
2.3 字符串验证 #
javascript
fastify.post('/string', {
schema: {
body: {
type: 'object',
properties: {
name: {
type: 'string',
minLength: 2,
maxLength: 100
},
email: {
type: 'string',
format: 'email'
},
url: {
type: 'string',
format: 'uri'
},
date: {
type: 'string',
format: 'date-time'
},
uuid: {
type: 'string',
format: 'uuid'
},
pattern: {
type: 'string',
pattern: '^[A-Z]{2}\\d{4}$'
}
}
}
}
}, handler)
2.4 数字验证 #
javascript
fastify.post('/number', {
schema: {
body: {
type: 'object',
properties: {
age: {
type: 'integer',
minimum: 0,
maximum: 150
},
price: {
type: 'number',
minimum: 0,
exclusiveMinimum: true
},
quantity: {
type: 'integer',
multipleOf: 5
}
}
}
}
}, handler)
2.5 数组验证 #
javascript
fastify.post('/array', {
schema: {
body: {
type: 'object',
properties: {
tags: {
type: 'array',
items: { type: 'string' },
minItems: 1,
maxItems: 10,
uniqueItems: true
},
numbers: {
type: 'array',
items: {
type: 'integer',
minimum: 0
}
},
objects: {
type: 'array',
items: {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' }
}
}
}
}
}
}
}, handler)
2.6 枚举值 #
javascript
fastify.post('/enum', {
schema: {
body: {
type: 'object',
properties: {
status: {
type: 'string',
enum: ['active', 'inactive', 'pending']
},
role: {
type: 'string',
enum: ['admin', 'user', 'guest']
},
priority: {
type: 'integer',
enum: [1, 2, 3, 4, 5]
}
}
}
}
}, handler)
三、参数验证 #
3.1 路径参数验证 #
javascript
fastify.get('/users/:id', {
schema: {
params: {
type: 'object',
properties: {
id: { type: 'integer' }
}
}
}
}, async (request, reply) => {
return { userId: request.params.id }
})
fastify.get('/posts/:postId/comments/:commentId', {
schema: {
params: {
type: 'object',
properties: {
postId: { type: 'integer' },
commentId: { type: 'integer' }
}
}
}
}, handler)
3.2 查询参数验证 #
javascript
fastify.get('/search', {
schema: {
querystring: {
type: 'object',
properties: {
q: { type: 'string', minLength: 1 },
page: { type: 'integer', minimum: 1, default: 1 },
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
sort: { type: 'string', enum: ['name', 'date', 'id'] },
order: { type: 'string', enum: ['asc', 'desc'], default: 'asc' }
},
required: ['q']
}
}
}, async (request, reply) => {
return request.query
})
3.3 请求头验证 #
javascript
fastify.get('/protected', {
schema: {
headers: {
type: 'object',
required: ['authorization'],
properties: {
authorization: {
type: 'string',
pattern: '^Bearer .+'
},
'x-api-key': {
type: 'string',
minLength: 10
}
}
}
}
}, handler)
3.4 Cookie验证 #
javascript
fastify.register(require('@fastify/cookie'))
fastify.get('/session', {
schema: {
cookies: {
type: 'object',
required: ['sessionId'],
properties: {
sessionId: { type: 'string', minLength: 10 }
}
}
}
}, handler)
四、请求体验证 #
4.1 基本请求体验证 #
javascript
fastify.post('/users', {
schema: {
body: {
type: 'object',
required: ['name', 'email'],
properties: {
name: { type: 'string', minLength: 2, maxLength: 100 },
email: { type: 'string', format: 'email' },
age: { type: 'integer', minimum: 0, maximum: 150 },
website: { type: 'string', format: 'uri' }
}
}
}
}, handler)
4.2 嵌套对象验证 #
javascript
fastify.post('/orders', {
schema: {
body: {
type: 'object',
required: ['customer', 'items'],
properties: {
customer: {
type: 'object',
required: ['name', 'email'],
properties: {
name: { type: 'string' },
email: { type: 'string', format: 'email' },
address: {
type: 'object',
properties: {
street: { type: 'string' },
city: { type: 'string' },
zipCode: { type: 'string' }
}
}
}
},
items: {
type: 'array',
minItems: 1,
items: {
type: 'object',
required: ['productId', 'quantity'],
properties: {
productId: { type: 'integer' },
quantity: { type: 'integer', minimum: 1 },
price: { type: 'number', minimum: 0 }
}
}
}
}
}
}
}, handler)
4.3 条件验证 #
javascript
fastify.post('/conditional', {
schema: {
body: {
type: 'object',
properties: {
type: { type: 'string', enum: ['personal', 'business'] },
name: { type: 'string' },
company: { type: 'string' }
},
if: {
properties: {
type: { const: 'business' }
}
},
then: {
required: ['company']
},
else: {
required: ['name']
}
}
}
}, handler)
五、响应验证 #
5.1 基本响应验证 #
javascript
fastify.get('/users/:id', {
schema: {
params: {
type: 'object',
properties: {
id: { type: 'integer' }
}
},
response: {
200: {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' },
email: { 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
})
5.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' }
]
})
5.3 多状态码响应 #
javascript
fastify.post('/users', {
schema: {
body: {
type: 'object',
required: ['name', 'email'],
properties: {
name: { type: 'string' },
email: { type: 'string', format: 'email' }
}
},
response: {
201: {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' },
email: { type: 'string' }
}
},
400: {
type: 'object',
properties: {
error: { type: 'string' },
message: { type: 'string' }
}
}
}
}
}, handler)
六、共享Schema #
6.1 定义共享Schema #
javascript
fastify.addSchema({
$id: 'user',
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' },
email: { type: 'string', format: 'email' }
}
})
fastify.addSchema({
$id: 'pagination',
type: 'object',
properties: {
page: { type: 'integer', minimum: 1 },
limit: { type: 'integer', minimum: 1, maximum: 100 },
total: { type: 'integer' }
}
})
6.2 使用共享Schema #
javascript
fastify.get('/users/:id', {
schema: {
params: {
type: 'object',
properties: {
id: { type: 'integer' }
}
},
response: {
200: { $ref: 'user#' }
}
}
}, handler)
fastify.get('/users', {
schema: {
response: {
200: {
type: 'object',
properties: {
users: {
type: 'array',
items: { $ref: 'user#' }
},
pagination: { $ref: 'pagination#' }
}
}
}
}
}, handler)
七、自定义验证器 #
7.1 自定义验证编译器 #
javascript
fastify.setValidatorCompiler(({ schema, method, url, httpPart }) => {
return (data) => {
if (!data.name) {
return { error: new Error('Name is required') }
}
return true
}
})
7.2 使用Ajv自定义 #
javascript
const Ajv = require('ajv')
const ajv = new Ajv({
allErrors: true,
useDefaults: true,
coerceTypes: true
})
fastify.setValidatorCompiler(({ schema }) => {
return ajv.compile(schema)
})
7.3 自定义格式 #
javascript
const Ajv = require('ajv')
const ajv = new Ajv()
ajv.addFormat('phone', {
type: 'string',
validate: (value) => /^\d{11}$/.test(value)
})
fastify.setValidatorCompiler(({ schema }) => {
return ajv.compile(schema)
})
fastify.post('/phone', {
schema: {
body: {
type: 'object',
properties: {
phone: { type: 'string', format: 'phone' }
}
}
}
}, handler)
八、错误处理 #
8.1 验证错误格式 #
json
{
"statusCode": 400,
"error": "Bad Request",
"message": "body must have required property 'name'"
}
8.2 自定义错误消息 #
javascript
fastify.setErrorHandler((error, request, reply) => {
if (error.validation) {
reply.code(400).send({
statusCode: 400,
error: 'Validation Error',
message: error.message,
details: error.validation.map(v => ({
field: v.instancePath || v.params.missingProperty,
message: v.message
}))
})
return
}
reply.send(error)
})
8.3 国际化错误消息 #
javascript
const errorMessages = {
en: {
required: 'is required',
minLength: 'must be at least {limit} characters',
format: 'format is invalid'
},
zh: {
required: '是必需的',
minLength: '至少需要 {limit} 个字符',
format: '格式无效'
}
}
fastify.setErrorHandler((error, request, reply) => {
if (error.validation) {
const lang = request.headers['accept-language'] || 'en'
const messages = errorMessages[lang] || errorMessages.en
const details = error.validation.map(v => {
const message = messages[v.keyword] || v.message
return {
field: v.instancePath,
message
}
})
reply.code(400).send({
statusCode: 400,
error: 'Validation Error',
details
})
return
}
reply.send(error)
})
九、最佳实践 #
9.1 Schema模块化 #
javascript
const userSchema = {
$id: 'user',
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string', minLength: 2, maxLength: 100 },
email: { type: 'string', format: 'email' }
}
}
const userCreateSchema = {
$id: 'userCreate',
type: 'object',
required: ['name', 'email'],
properties: {
name: { $ref: 'user#/properties/name' },
email: { $ref: 'user#/properties/email' }
}
}
fastify.addSchema(userSchema)
fastify.addSchema(userCreateSchema)
9.2 验证与文档同步 #
javascript
fastify.get('/users/:id', {
schema: {
description: 'Get user by ID',
tags: ['Users'],
params: {
type: 'object',
properties: {
id: { type: 'integer', description: 'User ID' }
}
},
response: {
200: {
description: 'User found',
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' }
}
}
}
}
}, handler)
十、总结 #
本章我们学习了:
- 验证类型:params、querystring、headers、body、response
- JSON Schema基础:类型、必需字段、字符串、数字、数组、枚举
- 参数验证:路径参数、查询参数、请求头、Cookie
- 请求体验证:基本验证、嵌套对象、条件验证
- 响应验证:基本响应、数组响应、多状态码
- 共享Schema:定义和使用共享Schema
- 自定义验证器:验证编译器、自定义格式
- 错误处理:错误格式、自定义消息、国际化
- 最佳实践:Schema模块化、验证与文档同步
接下来让我们学习序列化响应!
最后更新:2026-03-28