装饰器模式 #

一、装饰器概述 #

装饰器模式允许你向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'
})

八、总结 #

本章我们学习了:

  1. 装饰器类型:decorate、decorateRequest、decorateReply
  2. decorate方法:添加属性、方法、服务
  3. decorateRequest:请求属性、请求方法
  4. decorateReply:响应方法、分页响应
  5. 装饰器作用域:封装、破坏封装、继承
  6. 最佳实践:插件封装、错误处理、类型定义
  7. 常见模式:服务模式、工具模式、响应模式

接下来让我们学习生命周期!

最后更新:2026-03-28