封装
封装
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'] })
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 })
上述服务器示例显示了原始图中概述的所有封装概念。
- 每个子上下文(
authenticatedContext
、publicContext
和grandchildContext
)都可以访问在根上下文中定义的answer
请求装饰器。 - 只有
authenticatedContext
可以访问@fastify/bearer-auth
插件。 publicContext
和grandchildContext
都可以访问foo
请求装饰器。- 只有
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"}