封装
Fastify 的一个基本特性是“封装上下文”。它决定了哪些 装饰器,注册的 钩子 和 插件 可以在 路由 中使用。以下图展示了封装上下文的可视化表示:
上图中包含以下几个实体:
- 根上下文
- 三个 根插件
- 每个有两个 子上下文,每个子上下文中:
- 两个 子插件
- 一个 孙子上下文,每个孙子上下文中:
- 三个 子插件
每一个 子上下文 和 孙子上下文 都可以访问 根插件。在每个 子上下文 中,孙子上下文 可以访问注册在其包含的 子上下文 内的 子插件,但包含的 子上下文 不能 访问其 孙子上下文 注册的 子插件。
由于 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'] })
fastify.decorateRequest('回答', 42)
fastify.register(async function 认证上下文 (子服务器) {
子服务器.register(require('@fastify/bearer-auth'), { keys: ['abc123'] })
子服务器.route({
path: '/one',
method: 'GET',
handler (请求, 响应) {
响应.send({
回答: 请求.回答,
// 请求.foo 将为 undefined,因为它仅在 publicContext 中定义
foo: 请求.foo,
// 请求.bar 将为 undefined,因为它仅在 grandchildContext 中定义
bar: 请求.bar
})
}
})
})
fastify.register(async function 公共上下文 (子服务器) {
子服务器.decorateRequest('foo', 'foo')
子服务器.route({
path: '/two',
method: 'GET',
handler (请求, 响应) {
响应.send({
回答: 请求.回答,
foo: 请求.foo,
// 请求.bar 将为 undefined,因为它仅在 grandchildContext 中定义
bar: 请求.bar
})
}
})
子服务器.register(async function 孙上下文 (孙子服务器) {
孙子服务器.decorateRequest('bar', 'bar')
孙子服务器.route({
path: '/three',
method: 'GET',
handler (请求, 响应) {
响应.send({
回答: 请求.回答,
foo: 请求.foo,
bar: 请求.bar
})
}
})
})
})
fastify.listen({ port: 8000 })
以下是翻译后的Markdown内容:
上面的服务器示例演示了原始图中的封装概念:
1. 每个 _子上下文_ (`authenticatedContext`, `publicContext` 和 `grandchildContext`) 都可以访问在 _根上下文_ 中定义的 `answer` 请求装饰器。
2. 只有 `authenticatedContext` 可以访问 `@fastify/bearer-auth` 插件。
3. `publicContext` 和 `grandchildContext` 都可以访问 `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({ 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"}