装饰器模式 #
一、装饰器概述 #
装饰器模式允许你向Fastify实例、Request对象或Reply对象添加新属性和方法。
1.1 装饰器类型 #
| 方法 | 目标 | 说明 |
|---|---|---|
| decorate | Fastify实例 | 添加全局属性和方法 |
| decorateRequest | Request对象 | 添加请求属性 |
| decorateReply | Reply对象 | 添加响应方法 |
1.2 基本用法 #
javascript
fastify.decorate('utility', function () {
return 'Utility function'
})
fastify.decorateRequest('user', null)
fastify.decorateReply('success', function (data) {
return this.send({ success: true, data })
})
二、decorate方法 #
2.1 添加属性 #
javascript
fastify.decorate('version', '1.0.0')
fastify.decorate('config', {
api: {
baseUrl: 'https://api.example.com',
timeout: 5000
}
})
fastify.get('/info', async (request, reply) => {
return {
version: fastify.version,
config: fastify.config
}
})
2.2 添加方法 #
javascript
fastify.decorate('formatDate', function (date) {
return new Date(date).toISOString()
})
fastify.decorate('generateId', function () {
return Date.now().toString(36) + Math.random().toString(36).substr(2)
})
fastify.get('/formatted-date', async (request, reply) => {
return {
date: fastify.formatDate(new Date()),
id: fastify.generateId()
}
})
2.3 添加服务 #
javascript
fastify.decorate('services', {
user: {
async findById(id) {
return { id, name: 'John' }
},
async create(data) {
return { id: 1, ...data }
}
},
email: {
async send(to, subject, body) {
console.log(`Sending email to ${to}`)
return true
}
}
})
fastify.get('/users/:id', async (request, reply) => {
const user = await fastify.services.user.findById(request.params.id)
return user
})
2.4 检查装饰器 #
javascript
if (!fastify.hasDecorator('utility')) {
fastify.decorate('utility', function () {
return 'Utility'
})
}
if (fastify.hasDecorator('config')) {
console.log('Config exists:', fastify.config)
}
三、decorateRequest方法 #
3.1 添加请求属性 #
javascript
fastify.decorateRequest('user', null)
fastify.decorateRequest('startTime', null)
fastify.addHook('onRequest', async (request, reply) => {
request.startTime = Date.now()
})
fastify.addHook('preHandler', async (request, reply) => {
const token = request.headers.authorization
if (token) {
request.user = await verifyToken(token)
}
})
fastify.get('/profile', async (request, reply) => {
return {
user: request.user,
duration: Date.now() - request.startTime
}
})
3.2 添加请求方法 #
javascript
fastify.decorateRequest('isAuthenticated', function () {
return !!this.user
})
fastify.decorateRequest('hasRole', function (role) {
return this.user && this.user.role === role
})
fastify.get('/check-auth', async (request, reply) => {
return {
authenticated: request.isAuthenticated(),
isAdmin: request.hasRole('admin')
}
})
3.3 检查请求装饰器 #
javascript
if (!fastify.hasRequestDecorator('user')) {
fastify.decorateRequest('user', null)
}
四、decorateReply方法 #
4.1 添加响应方法 #
javascript
fastify.decorateReply('success', function (data) {
return this.send({
success: true,
data,
timestamp: new Date().toISOString()
})
})
fastify.decorateReply('error', function (message, code = 400) {
return this.code(code).send({
success: false,
error: message,
timestamp: new Date().toISOString()
})
})
fastify.get('/success-response', async (request, reply) => {
reply.success({ message: 'Hello' })
})
fastify.get('/error-response', async (request, reply) => {
reply.error('Something went wrong', 400)
})
4.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)
}
}
})
})
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)
})
4.3 条件响应 #
javascript
fastify.decorateReply('conditional', function (data, condition) {
if (condition) {
return this.success(data)
}
return this.error('Condition not met')
})
fastify.get('/conditional', async (request, reply) => {
const data = { value: 42 }
reply.conditional(data, data.value > 40)
})
4.4 检查响应装饰器 #
javascript
if (!fastify.hasReplyDecorator('success')) {
fastify.decorateReply('success', function (data) {
return this.send({ success: true, data })
})
}
五、装饰器作用域 #
5.1 封装作用域 #
javascript
fastify.register(async function (fastify, opts) {
fastify.decorate('internal', 'plugin value')
fastify.get('/internal', async (request, reply) => {
return { internal: fastify.internal }
})
})
fastify.get('/external', async (request, reply) => {
return { internal: fastify.internal }
})
5.2 破坏封装 #
javascript
const fp = require('fastify-plugin')
async function myPlugin(fastify, opts) {
fastify.decorate('shared', 'global value')
}
module.exports = fp(myPlugin)
5.3 作用域继承 #
javascript
fastify.decorate('root', 'root value')
fastify.register(async function (fastify, opts) {
console.log('Can access root:', fastify.root)
fastify.decorate('level1', 'level 1 value')
fastify.register(async function (fastify, opts) {
console.log('Can access root:', fastify.root)
console.log('Can access level1:', fastify.level1)
})
})
六、装饰器最佳实践 #
6.1 使用插件封装 #
javascript
const fp = require('fastify-plugin')
async function utilityPlugin(fastify, opts) {
fastify.decorate('utils', {
formatDate(date) {
return new Date(date).toISOString()
},
generateId() {
return Date.now().toString(36)
},
slugify(text) {
return text.toLowerCase().replace(/\s+/g, '-')
}
})
}
module.exports = fp(utilityPlugin, {
name: 'utility'
})
6.2 错误处理 #
javascript
async function databasePlugin(fastify, opts) {
if (fastify.hasDecorator('db')) {
throw new Error('Database decorator already exists')
}
const db = await connectDatabase(opts)
fastify.decorate('db', db)
fastify.addHook('onClose', async (instance) => {
await instance.db.close()
})
}
6.3 类型定义 #
javascript
fastify.decorate('db', null)
fastify.addHook('onReady', async () => {
fastify.db = await connectDatabase()
})
fastify.get('/users', async (request, reply) => {
const users = await fastify.db.collection('users').find({}).toArray()
return users
})
6.4 装饰器文档 #
javascript
/**
* @typedef {Object} FastifyInstance
* @property {Object} db - Database connection
* @property {Object} cache - Cache service
* @property {Object} services - Business services
*/
/**
* @typedef {Object} FastifyRequest
* @property {Object|null} user - Authenticated user
* @property {number} startTime - Request start timestamp
*/
/**
* @typedef {Object} FastifyReply
* @property {Function} success - Send success response
* @property {Function} error - Send error response
* @property {Function} paginate - Send paginated response
*/
七、常见装饰器模式 #
7.1 服务模式 #
javascript
const fp = require('fastify-plugin')
class UserService {
constructor(db) {
this.collection = db.collection('users')
}
async findById(id) {
return this.collection.findOne({ _id: id })
}
async create(data) {
const result = await this.collection.insertOne(data)
return this.findById(result.insertedId)
}
}
async function servicesPlugin(fastify, opts) {
fastify.decorate('services', {
user: new UserService(fastify.db)
})
}
module.exports = fp(servicesPlugin, {
name: 'services',
dependencies: ['database']
})
7.2 工具模式 #
javascript
const fp = require('fastify-plugin')
async function utilsPlugin(fastify, opts) {
fastify.decorate('utils', {
formatDate(date) {
return new Date(date).toISOString()
},
parseId(id) {
return parseInt(id, 10)
},
isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
}
})
}
module.exports = fp(utilsPlugin, {
name: 'utils'
})
7.3 响应模式 #
javascript
const fp = require('fastify-plugin')
async function responsePlugin(fastify, opts) {
fastify.decorateReply('success', function (data) {
return this.send({
success: true,
data,
timestamp: new Date().toISOString()
})
})
fastify.decorateReply('error', function (message, code = 400) {
return this.code(code).send({
success: false,
error: message,
timestamp: new Date().toISOString()
})
})
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)
}
}
})
})
}
module.exports = fp(responsePlugin, {
name: 'response'
})
八、总结 #
本章我们学习了:
- 装饰器类型:decorate、decorateRequest、decorateReply
- decorate方法:添加属性、方法、服务
- decorateRequest:请求属性、请求方法
- decorateReply:响应方法、分页响应
- 装饰器作用域:封装、破坏封装、继承
- 最佳实践:插件封装、错误处理、类型定义
- 常见模式:服务模式、工具模式、响应模式
接下来让我们学习生命周期!
最后更新:2026-03-28