Fastify 中文文档 (v4.28.1)
封装
“封装上下文”是 Fastify 的一个基础特性,负责控制路由能访问的装饰器、钩子以及插件。下图是封装上下文的抽象表现:
上图可归纳为以下几块内容:
- 顶层上下文 (root context)
- 三个 顶层插件 (root plugin)
- 两个 子上下文 (child context)_,每个 _子上下文 拥有
- 两个 子插件 (child plugin)
- 一个 _孙子上下文 (grandchild context)_,其又拥有
- 三个 子插件 (child plugin)
任意 子上下文 或 孙子上下文 都有权访问 顶层插件。孙子上下文 有权访问它上级的 子上下文 中注册的 子插件_,但 _子上下文 无权 访问它下级的 孙子上下文中 注册的 _子插件_。
在 Fastify 中,除了 _顶层上下文_,一切皆为插件。下文的例子也不例外,所有的“上下文”和“插件”都是包含装饰器、钩子、插件及路由的插件。该例子为有三个路由的 REST API 服务器,第一个路由 (/one
) 需要鉴权 (使用 fastify-bearer-auth),第二个路由 (/two
) 无需鉴权,第三个路由 (/three
) 有权访问第二个路由的上下文:
'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 会是 undefined,因为该值是在 publicContext 中定义的
foo: request.foo,
// request.bar 会是 undefined,因为该值是在 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 会是 undefined,因为该值是在 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(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`
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(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"}