Skip to Content

封装

Fastify 的一个基本特性是“封装上下文”。它决定了哪些 装饰器,注册的 钩子插件 可以在 路由 中使用。以下图展示了封装上下文的可视化表示:

Figure 1

上图中包含以下几个实体:

  1. 根上下文
  2. 三个 根插件
  3. 每个有两个 子上下文,每个子上下文中:
    • 两个 子插件
    • 一个 孙子上下文,每个孙子上下文中:
      • 三个 子插件

每一个 子上下文孙子上下文 都可以访问 根插件。在每个 子上下文 中,孙子上下文 可以访问注册在其包含的 子上下文 内的 子插件,但包含的 子上下文 不能 访问其 孙子上下文 注册的 子插件

由于 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"}