Encapsulation
封装
¥Encapsulation
Fastify 的一个基本功能是 "封装上下文。" 封装上下文控制哪些 decorators、已注册的 hooks 和 plugins 可用于 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:
上图中,有几个实体:
¥In the above figure, there are several entities:
根上下文
¥The root context
三个根插件
¥Three root plugins
两个子上下文,每个子上下文都有
¥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:
每个子上下文(
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 来打破封装上下文,以便在后代上下文中注册的任何内容都可用于包含父上下文。
¥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"}