Fastify

封装

“封装上下文”是 Fastify 的一个基础特性,负责控制路由能访问的装饰器钩子以及插件。下图是封装上下文的抽象表现:

Figure 1

上图可归纳为以下几块内容:

  1. 顶层上下文 (root context)
  2. 三个 顶层插件 (root plugin)
  3. 两个 子上下文 (child context),每个 子上下文 拥有
    • 两个 子插件 (child plugin)
    • 一个 孙子上下文 (grandchild context),其又拥有
      • 三个 子插件 (child plugin)

任意 子上下文孙子上下文 都有权访问 顶层插件孙子上下文 有权访问它上级的 子上下文 中注册的 子插件,但 子上下文 无权 访问它下级的 孙子上下文中 注册的 子插件

在 Fastify 中,除了 顶层上下文,一切皆为插件。下文的例子也不例外,所有的“上下文”和“插件”都是包含装饰器、钩子、插件及路由的插件。该例子为有三个路由的 REST API 服务器,第一个路由 (/one) 需要鉴权 (使用 fastify-bearer-auth),第二个路由 (/two) 无需鉴权,第三个路由 (/three) 有权访问第二个路由的上下文:

  1. 'use strict'
  2. const fastify = require('fastify')()
  3. fastify.decorateRequest('answer', 42)
  4. fastify.register(async function authenticatedContext (childServer) {
  5. childServer.register(require('fastify-bearer-auth'), { keys: ['abc123'] })
  6. childServer.route({
  7. path: '/one',
  8. method: 'GET',
  9. handler (request, response) {
  10. response.send({
  11. answer: request.answer,
  12. // request.foo 会是 undefined,因为该值是在 publicContext 中定义的
  13. foo: request.foo,
  14. // request.bar 会是 undefined,因为该值是在 grandchildContext 中定义的
  15. bar: request.bar
  16. })
  17. }
  18. })
  19. })
  20. fastify.register(async function publicContext (childServer) {
  21. childServer.decorateRequest('foo', 'foo')
  22. childServer.route({
  23. path: '/two',
  24. method: 'GET',
  25. handler (request, response) {
  26. response.send({
  27. answer: request.answer,
  28. foo: request.foo,
  29. // request.bar 会是 undefined,因为该值是在 grandchildContext 中定义的
  30. bar: request.bar
  31. })
  32. }
  33. })
  34. childServer.register(async function grandchildContext (grandchildServer) {
  35. grandchildServer.decorateRequest('bar', 'bar')
  36. grandchildServer.route({
  37. path: '/three',
  38. method: 'GET',
  39. handler (request, response) {
  40. response.send({
  41. answer: request.answer,
  42. foo: request.foo,
  43. bar: request.bar
  44. })
  45. }
  46. })
  47. })
  48. })
  49. fastify.listen(8000)

上面的例子展示了所有封装相关的概念:

  1. 每个 子上下文 (authenticatedContextpublicContextgrandchildContext) 都有权访问在 顶层上下文 中定义的 answer 请求装饰器。
  2. 只有 authenticatedContext 能访问 fastify-bearer-auth 插件。
  3. publicContextgrandchildContext 都能访问 foo 请求装饰器。
  4. 只有 grandchildContext 能访问 bar 请求装饰器。

启动服务来验证这些概念吧:

  1. # curl -H 'authorization: Bearer abc123' http://127.0.0.1:8000/one
  2. {"answer":42}
  3. # curl http://127.0.0.1:8000/two
  4. {"answer":42,"foo":"foo"}
  5. # curl http://127.0.0.1:8000/three
  6. {"answer":42,"foo":"foo","bar":"bar"}

在上下文间共享

请注意,在上文例子中,每个上下文都 从父级上下文进行继承,而父级上下文无权访问后代上下文中定义的实体。在某些情况下,我们并不想要这一默认行为。使用 fastify-plugin ,便能允许父级上下文访问到后代上下文中定义的实体。

假设上例的 publicContext 需要获取 grandchildContext 中定义的 bar 装饰器,我们可以重写代码如下:

  1. 'use strict'
  2. const fastify = require('fastify')()
  3. const fastifyPlugin = require('fastify-plugin')
  4. fastify.decorateRequest('answer', 42)
  5. // 为了代码清晰,这里省略了 `authenticatedContext`
  6. fastify.register(async function publicContext (childServer) {
  7. childServer.decorateRequest('foo', 'foo')
  8. childServer.route({
  9. path: '/two',
  10. method: 'GET',
  11. handler (request, response) {
  12. response.send({
  13. answer: request.answer,
  14. foo: request.foo,
  15. bar: request.bar
  16. })
  17. }
  18. })
  19. childServer.register(fastifyPlugin(grandchildContext))
  20. async function grandchildContext (grandchildServer) {
  21. grandchildServer.decorateRequest('bar', 'bar')
  22. grandchildServer.route({
  23. path: '/three',
  24. method: 'GET',
  25. handler (request, response) {
  26. response.send({
  27. answer: request.answer,
  28. foo: request.foo,
  29. bar: request.bar
  30. })
  31. }
  32. })
  33. }
  34. })
  35. fastify.listen(8000)

重启服务,访问 /two/three 路由:

  1. # curl http://127.0.0.1:8000/two
  2. {"answer":42,"foo":"foo","bar":"bar"}
  3. # curl http://127.0.0.1:8000/three
  4. {"answer":42,"foo":"foo","bar":"bar"}