Encapsulation
封装
¥Encapsulation
Fastify 的一个基本功能是 "封装上下文。"。它控制哪些 decorators、已注册的 hooks 和 plugins 可用于 routes。封装上下文的直观表示如下图所示:
¥A fundamental feature of Fastify is the "encapsulation context." It governs which decorators, registered hooks, and plugins are available to routes. A visual representation of the encapsulation context is shown in the following figure:
在上图中,有几个实体:
¥In the figure above, there are several entities:
-
根上下文
¥The root context
-
三个根插件
¥Three root plugins
-
两个子上下文,每个子上下文都有:
¥Two child contexts, each with:
-
两个子插件
¥Two child plugins
-
一个孙上下文,每个上下文都有:
¥One grandchild context, each with:
-
三个子插件
¥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. To put this
example into concrete terms, consider a basic scenario of a REST API server
with 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 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 is only defined in publicContext
foo: request.foo,
// request.bar will be undefined as it is 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 is 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 server example above demonstrates the encapsulation concepts from the original diagram:
-
每个子上下文(
authenticatedContext
、publicContext
和grandchildContext
)都可以访问根上下文中定义的answer
请求装饰器。¥Each child context (
authenticatedContext
,publicContext
, andgrandchildContext
) has access to theanswer
request decorator defined in the root context. -
只有
authenticatedContext
才能访问@fastify/bearer-auth
插件。¥Only the
authenticatedContext
has access to the@fastify/bearer-auth
plugin. -
publicContext
和grandchildContext
都可以访问foo
请求装饰器。¥Both the
publicContext
andgrandchildContext
have access to thefoo
request decorator. -
只有
grandchildContext
才能访问bar
请求装饰器。¥Only the
grandchildContext
has access to thebar
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 打破封装,使后代上下文中注册的任何内容都可用于父上下文。
¥Each context in the prior example inherits only from its parent contexts. Parent contexts cannot access entities within their descendant contexts. If needed, encapsulation can be broken using fastify-plugin, making anything registered in a descendant context available to the parent context.
要允许 publicContext
访问 grandchildContext
中的 bar
装饰器,请按如下方式重写代码:
¥To allow publicContext
access to the bar
decorator in grandchildContext
,
rewrite the code as follows:
'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"}