Fastify

    装饰器 API 是 同步 的。如果异步地添加装饰器,可能会导致在装饰器完成初始化之前, Fastify 实例就已经引导完毕了。因此,必须将 register 方法与 fastify-plugin 结合使用。详见 Plugins

    通过装饰器 API 来自定义对象,底层的 Javascript 引擎便能对其进行优化。这是因为引擎能在所有的对象实例被初始化与使用前,定义好它们的形状 (shape)。下文的例子则是不推荐的做法,因为它在对象的生命周期中修改了它们的形状:

    由于上述例子在请求对象初始化完成后,还改动了它的形状,因此 JavaScript 引擎必须对该对象去优化。使用装饰器 API 能避开去优化问题:

    1. // 使用装饰器为请求对象添加 'user' 属性。
    2. fastify.decorateRequest('user', '')
    3. // 更新属性。
    4. fastify.addHook('preHandler', (req, reply, done) => {
    5. req.user = 'Bob Dylan'
    6. done()
    7. })
    8. // 最后访问它。
    9. fastify.get('/', (req, reply) => {
    10. reply.send(`Hello, ${req.user}!`)
    11. })

    装饰器的初始值应该尽可能地与其未来将被设置的值接近。例如,字符串类型的装饰器的初始值为 '',对象或函数类型的初始值为 null

    请注意,上述例子仅可用于基本类型的值,因为引用类型会在所有请求中共享。详见 。

    更多此话题的内容,请见 JavaScript engine fundamentals: Shapes and Inline Caches

    decorate(name, value, [dependencies])

    该方法用于自定义 Fastify server 实例。

    例如,为其添加一个新方法:

    1. fastify.decorate('utility', function () {
    2. // 新功能的代码
    3. })

    正如上文所述,还可以传递非函数的值:

    1. fastify.decorate('conf', {
    2. db: 'some.db',
    3. port: 3000
    4. })
    1. console.log(fastify.conf.db)

    函数的 this 指向 Fastify server

    1. fastify.decorate('db', new DbConnection())
    2. fastify.get('/', async function (request, reply) {
    3. reply({hello: await this.db.query('world')})
    4. })

    可选的 dependencies 参数用于指定当前装饰器所依赖的其他装饰器列表。这个列表包含了其他装饰器的名称字符串。在下面的例子里,装饰器 “utility” 依赖于 “greet” 和 “log”:

    一旦有依赖项不满足,decorate 方法便会抛出异常。依赖项检查是在服务器实例启动前进行的,因此,在运行时不会发生异常。

    顾名思义,decorateReplyReply 核心对象添加新的方法或属性:

    1. fastify.decorateReply('utility', function () {
    2. // 新功能的代码
    3. })

    注:使用箭头函数会破坏 this 和 Fastify Reply 实例的绑定。

    注:使用 decorateReply 装饰引用类型,会触发警示:

    1. // 反面教材
    2. fastify.decorateReply('foo', { bar: 'fizz'})

    在这个例子里,该对象的引用存在于所有请求中,导致任何更改都会影响到所有请求,并可能触发安全漏洞或内存泄露。要合适地封装请求对象,请在 'onRequest' 钩子里设置新的值。示例如下:

    1. const fp = require('fastify-plugin')
    2. async function myPlugin (app) {
    3. app.decorateRequest('foo', null)
    4. app.addHook('onRequest', async (req, reply) => {
    5. req.foo = { bar: 42 }
    6. })
    7. }
    8. module.exports = fp(myPlugin)

    关于 dependencies 参数,请见 。

    decorateRequest(name, value, [dependencies])

    同理,decorateRequestRequest 核心对象添加新的方法或属性:

    1. fastify.decorateRequest('utility', function () {
    2. // 新功能的代码
    3. })

    注:使用 decorateRequest 装饰引用类型,会触发警示:

    1. // 反面教材
    2. fastify.decorateRequest('foo', { bar: 'fizz'})

    在这个例子里,该对象的引用存在于所有请求中,导致任何更改都会影响到所有请求,并可能触发安全漏洞或内存泄露。要合适地封装请求对象,请在 里设置新的值。示例如下:

    关于 dependencies 参数,请见 decorate

    hasDecorator(name)

    用于检查服务器实例上是否存在某个装饰器:

    hasRequestDecorator

    用于检查 Request 实例上是否存在某个装饰器:

    1. fastify.hasRequestDecorator('utility')

    hasReplyDecorator

    用于检查 Reply 实例上是否存在某个装饰器:

      封装 的同一个上下文中,如果通过 decoratedecorateRequest 以及 decorateReply 多次定义了一个同名的的装饰器,将会抛出一个异常。

      下面的示例会抛出异常:

      1. const server = require('fastify')()
      2. server.decorateReply('view', function (template, args) {
      3. // 页面渲染引擎的代码。
      4. })
      5. server.get('/', (req, reply) => {
      6. reply.view('/index.html', { hello: 'world' })
      7. })
      8. // 当在其他地方定义
      9. // view 装饰器时,抛出异常。
      10. server.decorateReply('view', function (template, args) {
      11. // 另一个渲染引擎。
      12. })
      13. server.listen(3000)

      但下面这个例子不会抛异常:

      1. const server = require('fastify')()
      2. server.decorateReply('view', function (template, args) {
      3. // 页面渲染引擎的代码。
      4. })
      5. server.register(async function (server, opts) {
      6. // 我们在当前封装的插件内添加了一个 view 装饰器。
      7. // 这么做不会抛出异常。
      8. // 因为插件外部和内部的 view 装饰器是不一样的。
      9. server.decorateReply('view', function (template, args) {
      10. // another rendering engine
      11. })
      12. server.get('/', (req, reply) => {
      13. reply.view('/index.page', { hello: 'world' })
      14. })
      15. }, { prefix: '/bar' })
      16. server.listen(3000)

      上例会在 Fastify 实例中定义一个 foo 属性: