路由基础 #
一、路由概述 #
路由是Web框架的核心功能,Fastify提供了强大且灵活的路由系统。
1.1 什么是路由 #
路由决定了如何响应客户端对特定端点的请求,包含:
- HTTP方法:GET、POST、PUT、DELETE等
- URL路径:/users、/products/:id等
- 处理函数:处理请求并返回响应
1.2 路由注册方式 #
Fastify提供两种路由注册方式:
javascript
// 简写方式
fastify.get('/path', handler)
// 完整方式
fastify.route({
method: 'GET',
url: '/path',
handler: handler
})
二、基本路由 #
2.1 定义基本路由 #
javascript
const fastify = require('fastify')({ logger: true })
fastify.get('/', async (request, reply) => {
return { message: 'Hello World' }
})
fastify.post('/users', async (request, reply) => {
return { message: 'User created' }
})
fastify.put('/users/:id', async (request, reply) => {
return { message: 'User updated', id: request.params.id }
})
fastify.delete('/users/:id', async (request, reply) => {
return { message: 'User deleted', id: request.params.id }
})
fastify.listen({ port: 3000 })
2.2 支持的HTTP方法 #
| 方法 | 说明 | 示例 |
|---|---|---|
| GET | 获取资源 | fastify.get() |
| POST | 创建资源 | fastify.post() |
| PUT | 完整更新资源 | fastify.put() |
| PATCH | 部分更新资源 | fastify.patch() |
| DELETE | 删除资源 | fastify.delete() |
| HEAD | 获取响应头 | fastify.head() |
| OPTIONS | 获取支持的方法 | fastify.options() |
| ALL | 匹配所有方法 | fastify.all() |
2.3 多方法路由 #
javascript
fastify.route({
method: ['GET', 'POST'],
url: '/items',
handler: async (request, reply) => {
if (request.method === 'GET') {
return { action: 'list' }
}
return { action: 'create' }
}
})
三、路径参数 #
3.1 命名参数 #
javascript
fastify.get('/users/:id', async (request, reply) => {
const { id } = request.params
return { userId: id }
})
fastify.get('/posts/:postId/comments/:commentId', async (request, reply) => {
const { postId, commentId } = request.params
return { postId, commentId }
})
3.2 可选参数 #
javascript
fastify.get('/users/:id?', async (request, reply) => {
if (request.params.id) {
return { userId: request.params.id }
}
return { users: 'all users' }
})
3.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 }
})
3.4 通配符路由 #
javascript
fastify.get('/files/*', async (request, reply) => {
return { path: request.params['*'] }
})
fastify.get('/static/**', async (request, reply) => {
return { wildcard: request.params['**'] }
})
四、查询参数 #
4.1 获取查询参数 #
javascript
fastify.get('/search', async (request, reply) => {
const { q, page, limit } = request.query
return {
query: q,
page: page || 1,
limit: limit || 10
}
})
4.2 查询参数验证 #
javascript
fastify.get('/search', {
schema: {
querystring: {
type: 'object',
properties: {
q: { type: 'string' },
page: { type: 'integer', minimum: 1 },
limit: { type: 'integer', minimum: 1, maximum: 100 }
},
required: ['q']
}
}
}, async (request, reply) => {
const { q, page = 1, limit = 10 } = request.query
return { q, page, limit }
})
五、路由选项 #
5.1 完整路由配置 #
javascript
fastify.route({
method: 'POST',
url: '/users',
schema: {
body: {
type: 'object',
required: ['name', 'email'],
properties: {
name: { type: 'string' },
email: { type: 'string', format: 'email' }
}
},
response: {
200: {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' },
email: { type: 'string' }
}
}
}
},
preHandler: async (request, reply) => {
console.log('Pre-handler')
},
handler: async (request, reply) => {
return { id: 1, ...request.body }
},
errorHandler: (error, request, reply) => {
reply.code(500).send({ error: 'Custom error' })
}
})
5.2 路由选项详解 #
| 选项 | 说明 |
|---|---|
| method | HTTP方法 |
| url | 路由路径 |
| schema | 数据验证Schema |
| preHandler | 请求前置处理 |
| handler | 主处理函数 |
| preSerialization | 序列化前处理 |
| errorHandler | 错误处理器 |
| onRequest | 请求钩子 |
| onResponse | 响应钩子 |
| config | 自定义配置 |
5.3 使用config传递配置 #
javascript
fastify.get('/users', {
config: {
rateLimit: {
max: 100,
timeWindow: '1 minute'
},
auth: {
required: true
}
}
}, async (request, reply) => {
return { users: [] }
})
fastify.addHook('onRequest', async (request, reply) => {
if (request.routeConfig.auth?.required) {
console.log('Authentication required')
}
})
六、路由前缀 #
6.1 注册时设置前缀 #
javascript
fastify.register(async function (fastify, opts) {
fastify.get('/', async (request, reply) => {
return { endpoint: '/api/v1/users' }
})
fastify.get('/:id', async (request, reply) => {
return { userId: request.params.id }
})
}, { prefix: '/api/v1/users' })
6.2 嵌套前缀 #
javascript
fastify.register(async function (fastify, opts) {
fastify.register(async function (fastify, opts) {
fastify.get('/', async (request, reply) => {
return { endpoint: '/api/v1/admin/users' }
})
}, { prefix: '/users' })
}, { prefix: '/api/v1/admin' })
6.3 使用fastify-plugin避免封装 #
javascript
const fp = require('fastify-plugin')
module.exports = fp(async function (fastify, opts) {
fastify.get('/users', async (request, reply) => {
return { users: [] }
})
}, { prefix: '/api' })
七、路由分组 #
7.1 使用register分组 #
javascript
const userRoutes = async (fastify, opts) => {
fastify.get('/', async (request, reply) => {
return { users: [] }
})
fastify.post('/', async (request, reply) => {
return { created: true }
})
fastify.get('/:id', async (request, reply) => {
return { userId: request.params.id }
})
}
fastify.register(userRoutes, { prefix: '/users' })
7.2 路由模块化 #
routes/users.js:
javascript
module.exports = async function (fastify, opts) {
fastify.get('/', {
schema: {
response: {
200: {
type: 'object',
properties: {
users: { type: 'array' }
}
}
}
}
}, async (request, reply) => {
return { users: [] }
})
fastify.post('/', async (request, reply) => {
reply.code(201)
return { created: true }
})
}
app.js:
javascript
fastify.register(require('./routes/users'), { prefix: '/api/users' })
fastify.register(require('./routes/products'), { prefix: '/api/products' })
八、路由约束 #
8.1 基于Host约束 #
javascript
fastify.route({
method: 'GET',
url: '/',
constraints: { host: 'api.example.com' },
handler: async (request, reply) => {
return { message: 'API endpoint' }
}
})
fastify.route({
method: 'GET',
url: '/',
constraints: { host: 'admin.example.com' },
handler: async (request, reply) => {
return { message: 'Admin endpoint' }
}
})
8.2 自定义约束 #
javascript
fastify.addConstraintStrategy({
name: 'version',
storage: () => {
const store = {}
return {
get: (version) => store[version] || null,
set: (version, storeValue) => { store[version] = storeValue }
}
},
deriveConstraint: (req, ctx) => {
return req.headers['x-api-version']
},
validate: () => true
})
fastify.route({
method: 'GET',
url: '/api',
constraints: { version: 'v1' },
handler: async (request, reply) => {
return { version: 'v1' }
}
})
fastify.route({
method: 'GET',
url: '/api',
constraints: { version: 'v2' },
handler: async (request, reply) => {
return { version: 'v2' }
}
})
九、路由查找 #
9.1 查找已注册路由 #
javascript
fastify.get('/users/:id', async (request, reply) => {
return { userId: request.params.id }
})
const route = fastify.findRoute({
method: 'GET',
url: '/users/:id'
})
console.log(route.params)
9.2 打印所有路由 #
javascript
fastify.ready(err => {
if (err) throw err
console.log(fastify.printRoutes())
})
输出:
text
└── /
└── users (GET, POST)
└── /:id (GET, PUT, DELETE)
十、路由最佳实践 #
10.1 RESTful设计 #
javascript
// 获取用户列表
fastify.get('/users', async (request, reply) => {
return { users: [] }
})
// 创建用户
fastify.post('/users', async (request, reply) => {
reply.code(201)
return { created: true }
})
// 获取单个用户
fastify.get('/users/:id', async (request, reply) => {
return { userId: request.params.id }
})
// 更新用户
fastify.put('/users/:id', async (request, reply) => {
return { updated: true }
})
// 删除用户
fastify.delete('/users/:id', async (request, reply) => {
reply.code(204).send()
})
10.2 版本控制 #
javascript
fastify.register(require('./routes/v1'), { prefix: '/api/v1' })
fastify.register(require('./routes/v2'), { prefix: '/api/v2' })
// 或使用Header版本控制
fastify.register(require('./routes/api'), {
constraints: { version: 'v1' }
})
10.3 路由命名 #
javascript
fastify.get('/users/:id', {
name: 'getUser'
}, async (request, reply) => {
return { userId: request.params.id }
})
const url = fastify.reverse('getUser', { id: 123 })
console.log(url)
十一、总结 #
本章我们学习了:
- 基本路由:HTTP方法、路径定义
- 路径参数:命名参数、可选参数、正则参数
- 查询参数:获取和验证
- 路由选项:完整配置、自定义配置
- 路由前缀:register、嵌套前缀
- 路由分组:模块化组织
- 路由约束:Host约束、自定义约束
- 最佳实践:RESTful设计、版本控制
接下来让我们学习更多路由方法!
最后更新:2026-03-28