Skip to main content

Encapsulation

封装

¥Encapsulation

Fastify 的一个基本功能是 "封装上下文。" 封装上下文控制哪些 decorators、已注册的 hooksplugins 可用于 routes。封装上下文的直观表示如下图所示:

¥A fundamental feature of Fastify is the "encapsulation context." The encapsulation context governs which decorators, registered hooks, and plugins are available to routes. A visual representation of the encapsulation context is shown in the following figure:

Figure 1

上图中,有几个实体:

¥In the above figure, there are several entities:

  1. 根上下文

    ¥The root context

  2. 三个根插件

    ¥Three root plugins

  3. 两个子上下文,每个子上下文都有

    ¥Two child contexts where each child context has

    • 两个子插件

      ¥Two child plugins

    • 每个孙子上下文都有一个孙子上下文

      ¥One grandchild context where each grandchild context has

      • 三个子插件

        ¥Three child plugins

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

¥Every child context and grandchild context has access to the root plugins. Within each child context, the grandchild contexts have access to the child plugins registered within the containing child context, but the containing child context does not have access to the child plugins registered within its grandchild context.

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

¥Given that everything in Fastify is a plugin, except for the root context, every "context" and "plugin" in this example is a plugin that can consist of decorators, hooks, plugins, and routes. Thus, to put this example into concrete terms, consider a basic scenario of a REST API server that has three routes: the first route (/one) requires authentication, the second route (/two) does not, and the third route (/three) has access to the same context as the second route. Using @fastify/bearer-auth to provide the authentication, the code for this example is as follows:

'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 })

上面的服务器示例显示了原始图中概述的所有封装概念:

¥The above server example shows all of the encapsulation concepts outlined in the original diagram:

  1. 每个子上下文(authenticatedContextpublicContextgrandchildContext)都可以访问根上下文中定义的 answer 请求装饰器。

    ¥Each child context (authenticatedContext, publicContext, and grandchildContext) has access to the answer request decorator defined in the root context.

  2. 只有 authenticatedContext 才能访问 @fastify/bearer-auth 插件。

    ¥Only the authenticatedContext has access to the @fastify/bearer-auth plugin.

  3. publicContextgrandchildContext 都可以访问 foo 请求装饰器。

    ¥Both the publicContext and grandchildContext have access to the foo request decorator.

  4. 只有 grandchildContext 才能访问 bar 请求装饰器。

    ¥Only the grandchildContext has access to the bar request decorator.

要查看这一点,请启动服务器并发出请求:

¥To see this, start the server and issue requests:

# 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"}

上下文之间共享

¥Sharing Between Contexts

请注意,前面示例中的每个上下文仅继承自父上下文。父上下文无法访问其后代上下文中的任何实体。有时不需要此默认值。在这种情况下,可以通过使用 fastify-plugin 来打破封装上下文,以便在后代上下文中注册的任何内容都可用于包含父上下文。

¥Notice that each context in the prior example inherits only from the parent contexts. Parent contexts cannot access any entities within their descendent contexts. This default is occasionally not desired. In such cases, the encapsulation context can be broken through the usage of fastify-plugin such that anything registered in a descendent context is available to the containing parent context.

假设 publicContext 需要访问上例中 grandchildContext 内定义的 bar 装饰器,则代码可以重写为:

¥Assuming the publicContext needs access to the bar decorator defined within the grandchildContext in the previous example, the code can be rewritten as:

'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 的请求:

¥Restarting the server and re-issuing the requests for /two and /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"}