跳至主要内容
版本:最新版本 (v5.0.x)

封装

封装

Fastify 的一个基本特性是“封装上下文”。封装上下文控制哪些装饰器、已注册的钩子插件可用于路由。下图显示了封装上下文的可视化表示。

Figure 1

在上图中,有几个实体

  1. 根上下文
  2. 三个根插件
  3. 两个子上下文,每个子上下文具有
    • 两个子插件
    • 一个孙子上下文,每个孙子上下文具有
      • 三个子插件

每个子上下文孙子上下文都可以访问根插件。在每个子上下文中,孙子上下文可以访问其包含的子上下文中注册的子插件,但包含的子上下文不能访问其孙子上下文中注册的子插件

鉴于 Fastify 中的所有内容都是插件(根上下文除外),此示例中的每个“上下文”和“插件”都是一个插件,它可以包含装饰器、钩子、插件和路由。因此,为了将此示例具体化,请考虑一个 REST API 服务器的基本场景,该服务器具有三个路由:第一个路由(/one)需要身份验证,第二个路由(/two)不需要身份验证,第三个路由(/three)可以访问与第二个路由相同的上下文。使用@fastify/bearer-auth提供身份验证,此示例的代码如下所示

'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 will be undefined as it's only defined in publicContext
foo: request.foo,
// request.bar will be undefined as it's only defined in 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 will be undefined as it's only defined in 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({ port: 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` omitted for clarity

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({ port: 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"}