Fastify 中文文档 (v4.28.1)

封装

“封装上下文”是 Fastify 的一个基础特性,负责控制路由能访问的装饰器钩子以及插件。下图是封装上下文的抽象表现:

null

上图可归纳为以下几块内容:

  1. 顶层上下文 (root context)
  2. 三个 顶层插件 (root plugin)
  3. 两个 子上下文 (child context)_,每个 _子上下文 拥有
    • 两个 子插件 (child plugin)
    • 一个 _孙子上下文 (grandchild context)_,其又拥有
      • 三个 子插件 (child plugin)

任意 子上下文孙子上下文 都有权访问 顶层插件孙子上下文 有权访问它上级的 子上下文 中注册的 子插件_,但 _子上下文 无权 访问它下级的 孙子上下文中 注册的 _子插件_。

在 Fastify 中,除了 _顶层上下文_,一切皆为插件。下文的例子也不例外,所有的“上下文”和“插件”都是包含装饰器、钩子、插件及路由的插件。该例子为有三个路由的 REST API 服务器,第一个路由 (/one) 需要鉴权 (使用 fastify-bearer-auth),第二个路由 (/two) 无需鉴权,第三个路由 (/three) 有权访问第二个路由的上下文:

'use strict'

const fastify = require('fastify')()

fastify.decorateRequest('answer', 42)

fastify.register(async function authenticatedContext (childServer) {
  childServer.register(require('fastify-bearer-auth'), { keys: ['abc123'] })

  childServer.route({
    path: '/one',
    method: 'GET',
    handler (request, response) {
      response.send({
        answer: request.answer,
        // request.foo 会是 undefined,因为该值是在 publicContext 中定义的
        foo: request.foo,
        // request.bar 会是 undefined,因为该值是在 grandchildContext 中定义的
        bar: request.bar
      })
    }
  })
})

fastify.register(async function publicContext (childServer) {
  childServer.decorateRequest('foo', 'foo')

  childServer.route({
    path: '/two',
    method: 'GET',
    handler (request, response) {
      response.send({
        answer: request.answer,
        foo: request.foo,
        // request.bar 会是 undefined,因为该值是在 grandchildContext 中定义的
        bar: request.bar
      })
    }
  })

  childServer.register(async function grandchildContext (grandchildServer) {
    grandchildServer.decorateRequest('bar', 'bar')

    grandchildServer.route({
      path: '/three',
      method: 'GET',
      handler (request, response) {
        response.send({
          answer: request.answer,
          foo: request.foo,
          bar: request.bar
        })
      }
    })
  })
})

fastify.listen(8000)

上面的例子展示了所有封装相关的概念:

  1. 每个 子上下文 (authenticatedContextpublicContextgrandchildContext) 都有权访问在 顶层上下文 中定义的 answer 请求装饰器。
  2. 只有 authenticatedContext 能访问 fastify-bearer-auth 插件。
  3. publicContextgrandchildContext 都能访问 foo 请求装饰器。
  4. 只有 grandchildContext 能访问 bar 请求装饰器。

启动服务来验证这些概念吧:

# curl -H 'authorization: Bearer abc123' http://127.0.0.1:8000/one
{"answer":42}
# curl http://127.0.0.1:8000/two
{"answer":42,"foo":"foo"}
# curl http://127.0.0.1:8000/three
{"answer":42,"foo":"foo","bar":"bar"}

在上下文间共享

请注意,在上文例子中,每个上下文都 从父级上下文进行继承,而父级上下文无权访问后代上下文中定义的实体。在某些情况下,我们并不想要这一默认行为。使用 fastify-plugin ,便能允许父级上下文访问到后代上下文中定义的实体。

假设上例的 publicContext 需要获取 grandchildContext 中定义的 bar 装饰器,我们可以重写代码如下:

'use strict'

const fastify = require('fastify')()
const fastifyPlugin = require('fastify-plugin')

fastify.decorateRequest('answer', 42)

// 为了代码清晰,这里省略了 `authenticatedContext`

fastify.register(async function publicContext (childServer) {
  childServer.decorateRequest('foo', 'foo')

  childServer.route({
    path: '/two',
    method: 'GET',
    handler (request, response) {
      response.send({
        answer: request.answer,
        foo: request.foo,
        bar: request.bar
      })
    }
  })

  childServer.register(fastifyPlugin(grandchildContext))

  async function grandchildContext (grandchildServer) {
    grandchildServer.decorateRequest('bar', 'bar')

    grandchildServer.route({
      path: '/three',
      method: 'GET',
      handler (request, response) {
        response.send({
          answer: request.answer,
          foo: request.foo,
          bar: request.bar
        })
      }
    })
  }
})

fastify.listen(8000)

重启服务,访问 /two/three 路由:

# curl http://127.0.0.1:8000/two
{"answer":42,"foo":"foo","bar":"bar"}
# curl http://127.0.0.1:8000/three
{"answer":42,"foo":"foo","bar":"bar"}