跳至主要內容
版本:最新版 (v5.0.x)

封裝

封裝

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'] })

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 })

上面的伺服器範例展示了原始圖表中概述的所有封裝概念

  1. 每個子上下文 (authenticatedContextpublicContextgrandchildContext) 都可以存取在根上下文中定義的 answer 請求裝飾器。
  2. 只有 authenticatedContext 可以存取 @fastify/bearer-auth 外掛。
  3. publicContextgrandchildContext 都可以存取 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` 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"}