路由参数 #
一、参数类型概述 #
Fastify支持多种参数类型,每种都有不同的获取方式。
1.1 参数分类 #
| 参数类型 | 位置 | 获取方式 | 示例 |
|---|---|---|---|
| 路径参数 | URL路径 | request.params | /users/:id |
| 查询参数 | URL查询字符串 | request.query | ?page=1&limit=10 |
| 请求体 | 请求体 | request.body | POST数据 |
| 请求头 | HTTP头部 | request.headers | Authorization |
| Cookie | Cookie头部 | request.cookies | sessionId |
1.2 参数获取示例 #
javascript
fastify.post('/users/:id/profile', async (request, reply) => {
const { id } = request.params
const { fields } = request.query
const body = request.body
const token = request.headers.authorization
const sessionId = request.cookies.sessionId
return { id, fields, body, token, sessionId }
})
二、路径参数 #
2.1 基本用法 #
javascript
fastify.get('/users/:id', async (request, reply) => {
return { userId: request.params.id }
})
fastify.get('/posts/:postId/comments/:commentId', async (request, reply) => {
const { postId, commentId } = request.params
return { postId, commentId }
})
2.2 参数验证 #
javascript
fastify.get('/users/:id', {
schema: {
params: {
type: 'object',
properties: {
id: { type: 'integer', minimum: 1 }
},
required: ['id']
}
}
}, async (request, reply) => {
return { userId: request.params.id }
})
2.3 正则验证 #
javascript
fastify.get('/users/:id(\\d+)', async (request, reply) => {
return { userId: parseInt(request.params.id) }
})
fastify.get('/files/:filename(.*\\.txt)', async (request, reply) => {
return { filename: request.params.filename }
})
fastify.get('/date/:date(\\d{4}-\\d{2}-\\d{2})', async (request, reply) => {
return { date: request.params.date }
})
2.4 可选参数 #
javascript
fastify.get('/users/:id?', async (request, reply) => {
if (request.params.id) {
return { userId: request.params.id }
}
return { users: 'all' }
})
2.5 通配符参数 #
javascript
fastify.get('/files/*', async (request, reply) => {
return { path: request.params['*'] }
})
fastify.get('/static/**', async (request, reply) => {
return { path: request.params['**'] }
})
三、查询参数 #
3.1 基本用法 #
javascript
fastify.get('/search', async (request, reply) => {
const { q, page, limit } = request.query
return { query: q, page, limit }
})
3.2 参数验证 #
javascript
fastify.get('/users', {
schema: {
querystring: {
type: 'object',
properties: {
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' },
search: { type: 'string', minLength: 1 }
}
}
}
}, async (request, reply) => {
const { page, limit, sort, order, search } = request.query
return { page, limit, sort, order, search }
})
3.3 数组参数 #
javascript
fastify.get('/users', {
schema: {
querystring: {
type: 'object',
properties: {
ids: {
type: 'array',
items: { type: 'integer' }
}
}
}
}
}, async (request, reply) => {
return { ids: request.query.ids }
})
请求:/users?ids=1&ids=2&ids=3
3.4 嵌套对象参数 #
javascript
fastify.get('/search', {
schema: {
querystring: {
type: 'object',
properties: {
filter: {
type: 'object',
properties: {
status: { type: 'string' },
category: { type: 'string' }
}
}
}
}
}
}, async (request, reply) => {
return { filter: request.query.filter }
})
请求:/search?filter[status]=active&filter[category]=tech
3.5 自定义解析器 #
javascript
fastify.get('/search', async (request, reply) => {
const parsed = {}
for (const [key, value] of Object.entries(request.query)) {
if (key.startsWith('filter.')) {
const filterKey = key.replace('filter.', '')
parsed[filterKey] = value
}
}
return { filters: parsed }
})
四、请求体 #
4.1 JSON请求体 #
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 },
tags: {
type: 'array',
items: { type: 'string' }
}
}
}
}
}, async (request, reply) => {
return { created: request.body }
})
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 }
}
}
}
}
}
}
}, async (request, reply) => {
return { order: request.body }
})
4.3 表单数据 #
javascript
fastify.post('/login', async (request, reply) => {
const { username, password } = request.body
return { username }
})
4.4 文件上传 #
javascript
fastify.register(require('@fastify/multipart'))
fastify.post('/upload', async (request, reply) => {
const data = await request.file()
return {
filename: data.filename,
mimetype: data.mimetype,
encoding: data.encoding
}
})
4.5 多文件上传 #
javascript
fastify.post('/uploads', async (request, reply) => {
const files = []
for await (const part of request.parts()) {
if (part.type === 'file') {
files.push({
filename: part.filename,
mimetype: part.mimetype
})
}
}
return { files }
})
五、请求头 #
5.1 获取请求头 #
javascript
fastify.get('/headers', async (request, reply) => {
return {
userAgent: request.headers['user-agent'],
authorization: request.headers.authorization,
contentType: request.headers['content-type'],
accept: request.headers.accept,
allHeaders: request.headers
}
})
5.2 请求头验证 #
javascript
fastify.get('/protected', {
schema: {
headers: {
type: 'object',
required: ['authorization'],
properties: {
authorization: { type: 'string', pattern: '^Bearer .+' }
}
}
}
}, async (request, reply) => {
const token = request.headers.authorization.replace('Bearer ', '')
return { token }
})
5.3 自定义请求头 #
javascript
fastify.get('/api', {
schema: {
headers: {
type: 'object',
properties: {
'x-api-key': { type: 'string' },
'x-request-id': { type: 'string', format: 'uuid' },
'x-api-version': { type: 'string', enum: ['v1', 'v2'] }
}
}
}
}, async (request, reply) => {
return {
apiKey: request.headers['x-api-key'],
requestId: request.headers['x-request-id'],
version: request.headers['x-api-version']
}
})
六、Cookie #
6.1 安装Cookie插件 #
javascript
fastify.register(require('@fastify/cookie'))
6.2 获取Cookie #
javascript
fastify.get('/get-cookies', async (request, reply) => {
return {
all: request.cookies,
sessionId: request.cookies.sessionId,
userId: request.cookies.userId
}
})
6.3 设置Cookie #
javascript
fastify.get('/set-cookie', async (request, reply) => {
reply.setCookie('sessionId', 'abc123', {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 3600,
path: '/'
})
return { message: 'Cookie set' }
})
6.4 Cookie验证 #
javascript
fastify.get('/protected', {
schema: {
cookies: {
type: 'object',
required: ['sessionId'],
properties: {
sessionId: { type: 'string', minLength: 10 }
}
}
}
}, async (request, reply) => {
return { sessionId: request.cookies.sessionId }
})
七、参数预处理 #
7.1 使用preHandler #
javascript
fastify.get('/users/:id', {
preHandler: async (request, reply) => {
request.params.id = parseInt(request.params.id)
}
}, async (request, reply) => {
return { userId: request.params.id }
})
7.2 参数转换 #
javascript
fastify.get('/search', {
preHandler: async (request, reply) => {
if (request.query.page) {
request.query.page = parseInt(request.query.page)
}
if (request.query.limit) {
request.query.limit = parseInt(request.query.limit)
}
}
}, async (request, reply) => {
return request.query
})
7.3 参数清理 #
javascript
fastify.post('/users', {
preHandler: async (request, reply) => {
if (request.body.name) {
request.body.name = request.body.name.trim()
}
if (request.body.email) {
request.body.email = request.body.email.toLowerCase()
}
}
}, async (request, reply) => {
return request.body
})
八、共享Schema #
8.1 定义共享Schema #
javascript
fastify.addSchema({
$id: 'userId',
type: 'integer',
minimum: 1
})
fastify.addSchema({
$id: 'pagination',
type: 'object',
properties: {
page: { type: 'integer', minimum: 1, default: 1 },
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }
}
})
8.2 使用共享Schema #
javascript
fastify.get('/users/:id', {
schema: {
params: {
type: 'object',
properties: {
id: { $ref: 'userId#' }
}
}
}
}, async (request, reply) => {
return { userId: request.params.id }
})
fastify.get('/posts', {
schema: {
querystring: { $ref: 'pagination#' }
}
}, async (request, reply) => {
return request.query
})
九、错误响应 #
9.1 验证错误格式 #
当参数验证失败时,Fastify返回标准错误格式:
json
{
"statusCode": 400,
"error": "Bad Request",
"message": "params/id must be integer"
}
9.2 自定义错误消息 #
javascript
fastify.get('/users/:id', {
schema: {
params: {
type: 'object',
properties: {
id: { type: 'integer', errorMessage: 'ID必须是整数' }
}
}
}
}, async (request, reply) => {
return { userId: request.params.id }
})
9.3 自定义错误处理器 #
javascript
fastify.setErrorHandler((error, request, reply) => {
if (error.validation) {
reply.code(400).send({
statusCode: 400,
error: 'Validation Error',
message: error.message,
details: error.validation
})
return
}
reply.send(error)
})
十、完整示例 #
10.1 用户API #
javascript
const fastify = require('fastify')({ logger: true })
fastify.addSchema({
$id: 'user',
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' },
email: { type: 'string', format: 'email' }
}
})
let users = [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' }
]
fastify.get('/users', {
schema: {
querystring: {
type: 'object',
properties: {
page: { type: 'integer', minimum: 1, default: 1 },
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
search: { type: 'string' }
}
},
response: {
200: {
type: 'object',
properties: {
users: {
type: 'array',
items: { $ref: 'user#' }
},
pagination: {
type: 'object',
properties: {
page: { type: 'integer' },
limit: { type: 'integer' },
total: { type: 'integer' }
}
}
}
}
}
}
}, async (request, reply) => {
const { page, limit, search } = request.query
let filtered = users
if (search) {
filtered = users.filter(u =>
u.name.includes(search) || u.email.includes(search)
)
}
return {
users: filtered.slice((page - 1) * limit, page * limit),
pagination: {
page,
limit,
total: filtered.length
}
}
})
fastify.post('/users', {
schema: {
body: {
type: 'object',
required: ['name', 'email'],
properties: {
name: { type: 'string', minLength: 2 },
email: { type: 'string', format: 'email' }
}
},
response: {
201: { $ref: 'user#' }
}
}
}, async (request, reply) => {
const user = {
id: users.length + 1,
...request.body
}
users.push(user)
reply.code(201)
return user
})
fastify.listen({ port: 3000 })
十一、总结 #
本章我们学习了:
- 参数类型:路径参数、查询参数、请求体、请求头、Cookie
- 路径参数:基本用法、验证、正则、通配符
- 查询参数:基本用法、验证、数组、嵌套对象
- 请求体:JSON、表单、文件上传
- 请求头:获取、验证、自定义头
- Cookie:获取、设置、验证
- 参数预处理:preHandler、转换、清理
- 共享Schema:定义、使用
- 错误处理:自定义错误消息、错误处理器
接下来让我们学习路由钩子!
最后更新:2026-03-28