路由方法 #
一、HTTP方法概述 #
HTTP方法定义了对资源的操作类型,Fastify支持所有标准HTTP方法。
1.1 方法分类 #
| 类型 | 方法 | 说明 |
|---|---|---|
| 安全方法 | GET, HEAD, OPTIONS, TRACE | 不修改资源状态 |
| 幂等方法 | GET, HEAD, OPTIONS, TRACE, PUT, DELETE | 多次执行结果相同 |
| 非幂等方法 | POST, PATCH | 多次执行可能产生不同结果 |
1.2 方法注册 #
javascript
const fastify = require('fastify')()
fastify.get('/path', handler)
fastify.post('/path', handler)
fastify.put('/path', handler)
fastify.patch('/path', handler)
fastify.delete('/path', handler)
fastify.head('/path', handler)
fastify.options('/path', handler)
fastify.all('/path', handler)
二、GET方法 #
2.1 基本用法 #
GET方法用于获取资源,应该是安全且幂等的。
javascript
fastify.get('/users', async (request, reply) => {
return { users: [] }
})
fastify.get('/users/:id', async (request, reply) => {
return { userId: request.params.id }
})
2.2 带查询参数 #
javascript
fastify.get('/users', {
schema: {
querystring: {
type: 'object',
properties: {
page: { type: 'integer', default: 1 },
limit: { type: 'integer', default: 10 },
sort: { type: 'string', enum: ['name', 'date', 'id'] },
order: { type: 'string', enum: ['asc', 'desc'], default: 'asc' }
}
}
}
}, async (request, reply) => {
const { page, limit, sort, order } = request.query
return {
users: [],
pagination: { page, limit, total: 100 }
}
})
2.3 条件请求 #
javascript
fastify.get('/users/:id', async (request, reply) => {
const user = { id: request.params.id, name: 'John', version: 2 }
const etag = `W/"${user.version}"`
if (request.headers['if-none-match'] === etag) {
reply.code(304).send()
return
}
reply.header('ETag', etag)
return user
})
2.4 缓存控制 #
javascript
fastify.get('/static-data', async (request, reply) => {
reply
.header('Cache-Control', 'public, max-age=3600')
.header('Last-Modified', new Date().toUTCString())
return { data: 'cached content' }
})
三、POST方法 #
3.1 基本用法 #
POST方法用于创建资源,不是幂等的。
javascript
fastify.post('/users', {
schema: {
body: {
type: 'object',
required: ['name', 'email'],
properties: {
name: { type: 'string', minLength: 2 },
email: { type: 'string', format: 'email' },
age: { type: 'integer', minimum: 0 }
}
}
}
}, async (request, reply) => {
const user = {
id: Date.now(),
...request.body,
createdAt: new Date()
}
reply.code(201)
reply.header('Location', `/users/${user.id}`)
return user
})
3.2 表单数据处理 #
javascript
fastify.post('/login', {
schema: {
body: {
type: 'object',
required: ['username', 'password'],
properties: {
username: { type: 'string' },
password: { type: 'string', minLength: 6 }
}
}
}
}, async (request, reply) => {
const { username, password } = request.body
if (username === 'admin' && password === 'password') {
return { token: 'jwt-token-here' }
}
reply.code(401)
return { error: 'Invalid credentials' }
})
3.3 批量创建 #
javascript
fastify.post('/users/batch', {
schema: {
body: {
type: 'object',
required: ['users'],
properties: {
users: {
type: 'array',
minItems: 1,
maxItems: 100,
items: {
type: 'object',
required: ['name', 'email'],
properties: {
name: { type: 'string' },
email: { type: 'string', format: 'email' }
}
}
}
}
}
}
}, async (request, reply) => {
const created = request.body.users.map((user, index) => ({
id: index + 1,
...user
}))
reply.code(201)
return { created, count: created.length }
})
四、PUT方法 #
4.1 基本用法 #
PUT方法用于完整更新资源,是幂等的。
javascript
fastify.put('/users/:id', {
schema: {
params: {
type: 'object',
properties: {
id: { type: 'integer' }
}
},
body: {
type: 'object',
required: ['name', 'email'],
properties: {
name: { type: 'string' },
email: { type: 'string', format: 'email' },
age: { type: 'integer' }
}
}
}
}, async (request, reply) => {
const { id } = request.params
return {
id,
...request.body,
updatedAt: new Date()
}
})
4.2 创建或更新 #
javascript
fastify.put('/users/:id', async (request, reply) => {
const { id } = request.params
const exists = await checkUserExists(id)
if (exists) {
reply.code(200)
return { action: 'updated', id }
}
reply.code(201)
return { action: 'created', id }
})
五、PATCH方法 #
5.1 基本用法 #
PATCH方法用于部分更新资源,不是幂等的。
javascript
fastify.patch('/users/:id', {
schema: {
body: {
type: 'object',
properties: {
name: { type: 'string' },
email: { type: 'string', format: 'email' },
age: { type: 'integer' }
}
}
}
}, async (request, reply) => {
const { id } = request.params
const updates = request.body
if (Object.keys(updates).length === 0) {
reply.code(400)
return { error: 'No fields to update' }
}
return {
id,
...updates,
updatedAt: new Date()
}
})
5.2 JSON Patch支持 #
javascript
fastify.patch('/users/:id', {
schema: {
body: {
type: 'array',
items: {
type: 'object',
required: ['op', 'path'],
properties: {
op: { type: 'string', enum: ['add', 'remove', 'replace', 'move', 'copy', 'test'] },
path: { type: 'string' },
value: {}
}
}
}
}
}, async (request, reply) => {
const patches = request.body
return {
message: 'Patches applied',
patches
}
})
六、DELETE方法 #
6.1 基本用法 #
DELETE方法用于删除资源,是幂等的。
javascript
fastify.delete('/users/:id', async (request, reply) => {
const { id } = request.params
reply.code(204).send()
})
6.2 返回删除的资源 #
javascript
fastify.delete('/users/:id', async (request, reply) => {
const { id } = request.params
const user = { id, name: 'Deleted User' }
return { deleted: user }
})
6.3 批量删除 #
javascript
fastify.delete('/users', {
schema: {
querystring: {
type: 'object',
properties: {
ids: { type: 'string' }
}
}
}
}, async (request, reply) => {
const ids = request.query.ids.split(',').map(Number)
return {
deleted: ids.length,
ids
}
})
七、HEAD方法 #
7.1 基本用法 #
HEAD方法与GET类似,但只返回响应头。
javascript
fastify.head('/users/:id', async (request, reply) => {
const exists = await checkUserExists(request.params.id)
if (!exists) {
reply.code(404).send()
return
}
reply
.header('Content-Type', 'application/json')
.header('Content-Length', '100')
.code(200)
.send()
})
7.2 检查资源状态 #
javascript
fastify.head('/files/:filename', async (request, reply) => {
const stats = await getFileStats(request.params.filename)
if (!stats) {
reply.code(404).send()
return
}
reply
.header('Content-Length', stats.size)
.header('Last-Modified', stats.mtime.toUTCString())
.header('Content-Type', stats.mimeType)
.code(200)
.send()
})
八、OPTIONS方法 #
8.1 基本用法 #
OPTIONS方法用于获取资源支持的方法。
javascript
fastify.options('/users', async (request, reply) => {
reply
.header('Allow', 'GET, POST, PUT, DELETE, OPTIONS')
.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
.header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
.code(204)
.send()
})
8.2 CORS预检 #
javascript
fastify.options('/users/:id', async (request, reply) => {
reply
.header('Access-Control-Allow-Origin', '*')
.header('Access-Control-Allow-Methods', 'GET, PUT, DELETE')
.header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
.header('Access-Control-Max-Age', '86400')
.code(204)
.send()
})
九、ALL方法 #
9.1 匹配所有方法 #
javascript
fastify.all('/api/*', async (request, reply) => {
return {
method: request.method,
path: request.params['*']
}
})
9.2 通用处理 #
javascript
fastify.all('/legacy/*', async (request, reply) => {
reply.code(410)
return {
error: 'This endpoint is deprecated',
migration: '/api/v2' + request.params['*']
}
})
十、自定义方法 #
10.1 注册自定义方法 #
javascript
fastify.route({
method: 'REPORT',
url: '/reports',
handler: async (request, reply) => {
return { report: 'generated' }
}
})
10.2 WebDAV方法 #
javascript
fastify.route({
method: 'PROPFIND',
url: '/dav/*',
handler: async (request, reply) => {
reply.header('Content-Type', 'application/xml')
return `<?xml version="1.0"?><response/>`
}
})
fastify.route({
method: 'COPY',
url: '/dav/*',
handler: async (request, reply) => {
return { copied: true }
}
})
十一、方法覆盖 #
11.1 X-HTTP-Method-Override #
javascript
fastify.addHook('preHandler', async (request, reply) => {
const override = request.headers['x-http-method-override']
if (override) {
request.method = override.toUpperCase()
}
})
fastify.post('/users', async (request, reply) => {
if (request.method === 'PUT') {
return { action: 'update' }
}
return { action: 'create' }
})
11.2 _method参数 #
javascript
fastify.addHook('preHandler', async (request, reply) => {
if (request.method === 'POST' && request.body._method) {
request.method = request.body._method.toUpperCase()
delete request.body._method
}
})
十二、方法最佳实践 #
12.1 RESTful API设计 #
javascript
const fastify = require('fastify')()
let users = []
fastify.get('/users', async (request, reply) => {
return { users }
})
fastify.post('/users', async (request, reply) => {
const user = { id: users.length + 1, ...request.body }
users.push(user)
reply.code(201)
return user
})
fastify.get('/users/:id', async (request, reply) => {
const user = users.find(u => u.id === parseInt(request.params.id))
if (!user) {
reply.code(404)
return { error: 'User not found' }
}
return user
})
fastify.put('/users/:id', async (request, reply) => {
const index = users.findIndex(u => u.id === parseInt(request.params.id))
if (index === -1) {
reply.code(404)
return { error: 'User not found' }
}
users[index] = { id: parseInt(request.params.id), ...request.body }
return users[index]
})
fastify.patch('/users/:id', async (request, reply) => {
const user = users.find(u => u.id === parseInt(request.params.id))
if (!user) {
reply.code(404)
return { error: 'User not found' }
}
Object.assign(user, request.body)
return user
})
fastify.delete('/users/:id', async (request, reply) => {
const index = users.findIndex(u => u.id === parseInt(request.params.id))
if (index === -1) {
reply.code(404)
return { error: 'User not found' }
}
users.splice(index, 1)
reply.code(204).send()
})
12.2 状态码规范 #
| 方法 | 成功状态码 | 说明 |
|---|---|---|
| GET | 200 | 成功返回数据 |
| POST | 201 | 创建成功 |
| PUT | 200 | 更新成功 |
| PATCH | 200 | 部分更新成功 |
| DELETE | 204 | 删除成功(无内容) |
十三、总结 #
本章我们学习了:
- GET方法:获取资源、查询参数、缓存控制
- POST方法:创建资源、表单处理、批量创建
- PUT方法:完整更新、创建或更新
- PATCH方法:部分更新、JSON Patch
- DELETE方法:删除资源、批量删除
- HEAD方法:获取响应头
- OPTIONS方法:获取支持的方法、CORS预检
- ALL方法:匹配所有方法
- 自定义方法:WebDAV等扩展方法
- 最佳实践:RESTful设计、状态码规范
接下来让我们学习路由参数!
最后更新:2026-03-28