5.3 KiB
5.3 KiB
Fastify
封装
“封装上下文”是 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"}