装饰器
装饰器
¥Decorators
装饰器 API 自定义核心 Fastify 对象,例如服务器实例以及 HTTP 请求生命周期中使用的任何请求和回复对象。它可以将任何类型的属性附加到核心对象,例如函数、普通对象或原生类型。
¥The decorators API customizes core Fastify objects, such as the server instance and any request and reply objects used during the HTTP request lifecycle. It can attach any type of property to core objects, e.g., functions, plain objects, or native types.
该 API 是同步的。异步定义装饰可能会导致 Fastify 实例在装饰完成 之前启动。要注册异步修饰,请将 register API 与 fastify-plugin 结合使用。有关更多详细信息,请参阅 插件 文档。
¥This API is synchronous. Defining a decoration asynchronously could result in
the Fastify instance booting before the decoration completes. To register an
asynchronous decoration, use the register API with fastify-plugin. See the
Plugins documentation for more details.
使用此 API 装饰核心对象允许底层 JavaScript 引擎优化服务器、请求和响应对象的处理。这是通过在实例化和使用所有此类对象实例之前定义它们的形状来实现的。例如,不建议使用以下方法,因为它会在对象的生命周期中改变其形状:
¥Decorating core objects with this API allows the underlying JavaScript engine to optimize the handling of server, request, and reply objects. This is accomplished by defining the shape of all such object instances before they are instantiated and used. As an example, the following is not recommended because it will change the shape of objects during their lifecycle:
// Bad example! Continue reading.
// Attach a user property to the incoming request before the request
// handler is invoked.
fastify.addHook('preHandler', function (req, reply, done) {
req.user = 'Bob Dylan'
done()
})
// Use the attached user property in the request handler.
fastify.get('/', function (req, reply) {
reply.send(`Hello, ${req.user}`)
})
上面的例子在实例化后改变了请求对象,导致 JavaScript 引擎对访问进行去优化。使用装饰 API 可以避免这种去优化:
¥The above example mutates the request object after instantiation, causing the JavaScript engine to deoptimize access. Using the decoration API avoids this deoptimization:
// Decorate request with a 'user' property
fastify.decorateRequest('user', '')
// Update our property
fastify.addHook('preHandler', (req, reply, done) => {
req.user = 'Bob Dylan'
done()
})
// And finally access it
fastify.get('/', (req, reply) => {
reply.send(`Hello, ${req.user}!`)
})
保持装饰字段的初始形状接近其未来的动态值。将装饰器初始化为字符串的 '' 和对象或函数的 null。这仅适用于值类型;引用类型将在 Fastify 启动期间抛出错误。有关更多信息,请参阅 decorateRequest 和 JavaScript 引擎基础知识:形状和内联缓存。
¥Keep the initial shape of a decorated field close to its future dynamic value.
Initialize a decorator as '' for strings and null for objects or functions.
This works only with value types; reference types will throw an error during
Fastify startup. See decorateRequest and
JavaScript engine fundamentals: Shapes
and Inline Caches
for more information.
用法
¥Usage
decorate(name, value, [dependencies])
此方法自定义 Fastify server 实例。
¥This method customizes the Fastify server instance.
例如,要将新方法附加到服务器实例:
¥For example, to attach a new method to the server instance:
fastify.decorate('utility', function () {
// Something very useful
})
非函数值也可以附加到服务器实例:
¥Non-function values can also be attached to the server instance:
fastify.decorate('conf', {
db: 'some.db',
port: 3000
})
要访问装饰属性,请使用提供给装饰 API 的名称:
¥To access decorated properties, use the name provided to the decoration API:
fastify.utility()
console.log(fastify.conf.db)
装饰后的 Fastify 服务器 绑定到 route 处理程序中的 this:
¥The decorated Fastify server is bound to this in
route handlers:
fastify.decorate('db', new DbConnection())
fastify.get('/', async function (request, reply) {
// using return
return { hello: await this.db.query('world') }
// or
// using reply.send()
reply.send({ hello: await this.db.query('world') })
await reply
})
dependencies 参数是定义的装饰器所依赖的可选装饰器列表。此列表包含其他装饰器的名称。在以下示例中,"utility" 装饰器依赖于 "greet" 和 "hi" 装饰器:
¥The dependencies parameter is an optional list of decorators that the
decorator being defined relies upon. This list contains the names of other
decorators. In the following example, the "utility" decorator depends on the
"greet" and "hi" decorators:
async function greetDecorator (fastify, opts) {
fastify.decorate('greet', () => {
return 'greet message'
})
}
async function hiDecorator (fastify, opts) {
fastify.decorate('hi', () => {
return 'hi message'
})
}
async function utilityDecorator (fastify, opts) {
fastify.decorate('utility', () => {
return `${fastify.greet()} | ${fastify.hi()}`
})
}
fastify.register(fastifyPlugin(greetDecorator, { name: 'greet' }))
fastify.register(fastifyPlugin(hiDecorator, { name: 'hi' }))
fastify.register(fastifyPlugin(utilityDecorator, { dependencies: ['greet', 'hi'] }))
fastify.get('/', function (req, reply) {
// Response: {"hello":"greet message | hi message"}
reply.send({ hello: fastify.utility() })
})
fastify.listen({ port: 3000 }, (err, address) => {
if (err) throw err
})
使用箭头函数会破坏 this 与 FastifyInstance 的绑定。
¥Using an arrow function breaks the binding of this to
the FastifyInstance.
如果依赖不满足,decorate 方法将抛出异常。依赖检查发生在服务器实例启动之前,而不是在运行时。
¥If a dependency is not satisfied, the decorate method throws an exception.
The dependency check occurs before the server instance boots, not during
runtime.
decorateReply(name, value, [dependencies])
此 API 向核心 Reply 对象添加了新方法/属性:
¥This API adds new methods/properties to the core Reply object:
fastify.decorateReply('utility', function () {
// Something very useful
})
使用箭头函数将破坏 this 与 Fastify Reply 实例的绑定。
¥Using an arrow function will break the binding of this to the Fastify
Reply instance.
如果与引用类型一起使用,使用 decorateReply 将引发错误:
¥Using decorateReply will throw and error if used with a reference type:
// Don't do this
fastify.decorateReply('foo', { bar: 'fizz'})
在此示例中,对象引用将与所有请求共享,任何修改都会影响所有请求,从而可能造成安全漏洞或内存泄漏。Fastify 阻止此操作。
¥In this example, the object reference would be shared with all requests, and any mutation will impact all requests, potentially creating security vulnerabilities or memory leaks. Fastify blocks this.
为了实现跨请求的正确封装,请为 'onRequest' 钩 中的每个传入请求配置一个新值。
¥To achieve proper encapsulation across requests configure a new value for each
incoming request in the 'onRequest' hook.
const fp = require('fastify-plugin')
async function myPlugin (app) {
app.decorateReply('foo')
app.addHook('onRequest', async (req, reply) => {
reply.foo = { bar: 42 }
})
}
module.exports = fp(myPlugin)
请参阅 decorate 了解 dependencies 参数的信息。
¥See decorate for information about the dependencies parameter.
decorateRequest(name, value, [dependencies])
与 decorateReply 一样,此 API 向核心 Request 对象添加了新方法/属性:
¥As with decorateReply, this API adds new methods/properties
to the core Request object:
fastify.decorateRequest('utility', function () {
// something very useful
})
使用箭头函数将破坏 this 与 Fastify Request 实例的绑定。
¥Using an arrow function will break the binding of this to the Fastify
Request instance.
如果与引用类型一起使用,使用 decorateRequest 将引发错误:
¥Using decorateRequest will emit an error if used with a reference type:
// Don't do this
fastify.decorateRequest('foo', { bar: 'fizz'})
在此示例中,对象引用将与所有请求共享,任何修改都会影响所有请求,从而可能造成安全漏洞或内存泄漏。Fastify 阻止此操作。
¥In this example, the object reference would be shared with all requests, and any mutation will impact all requests, potentially creating security vulnerabilities or memory leaks. Fastify blocks this.
为了实现跨请求的正确封装,请为 'onRequest' 钩 中的每个传入请求配置一个新值。
¥To achieve proper encapsulation across requests configure a new value for each
incoming request in the 'onRequest' hook.
示例:
¥Example:
const fp = require('fastify-plugin')
async function myPlugin (app) {
app.decorateRequest('foo')
app.addHook('onRequest', async (req, reply) => {
req.foo = { bar: 42 }
})
}
module.exports = fp(myPlugin)
钩子解决方案更灵活,允许更复杂的初始化,因为可以向 onRequest 钩子添加更多逻辑。
¥The hook solution is more flexible and allows for more complex initialization
because more logic can be added to the onRequest hook.
另一种方法是使用 getter/setter 模式,但它需要 2 个装饰器:
¥Another approach is to use the getter/setter pattern, but it requires 2 decorators:
fastify.decorateRequest('my_decorator_holder') // define the holder
fastify.decorateRequest('user', {
getter () {
this.my_decorator_holder ??= {} // initialize the holder
return this.my_decorator_holder
}
})
fastify.get('/', async function (req, reply) {
req.user.access = 'granted'
// other code
})
这可确保 user 属性对于每个请求始终是唯一的。
¥This ensures that the user property is always unique for each request.
请参阅 decorate 了解 dependencies 参数的信息。
¥See decorate for information about the dependencies parameter.
hasDecorator(name)
用于检查服务器实例修饰是否存在:
¥Used to check for the existence of a server instance decoration:
fastify.hasDecorator('utility')
hasRequestDecorator
用于检查 Request 装饰是否存在:
¥Used to check for the existence of a Request decoration:
fastify.hasRequestDecorator('utility')
hasReplyDecorator
用于检查 Reply 装饰是否存在:
¥Used to check for the existence of a Reply decoration:
fastify.hasReplyDecorator('utility')
装饰器和封装
¥Decorators and Encapsulation
在同一个封装上下文中多次定义具有相同名称的装饰器(使用 decorate、decorateRequest 或 decorateReply)将引发异常。例如,以下内容将抛出:
¥Defining a decorator (using decorate, decorateRequest, or decorateReply)
with the same name more than once in the same encapsulated context will
throw an exception. For example, the following will throw:
const server = require('fastify')()
server.decorateReply('view', function (template, args) {
// Amazing view rendering engine
})
server.get('/', (req, reply) => {
reply.view('/index.html', { hello: 'world' })
})
// Somewhere else in our codebase, we define another
// view decorator. This throws.
server.decorateReply('view', function (template, args) {
// Another rendering engine
})
server.listen({ port: 3000 })
但这不会:
¥But this will not:
const server = require('fastify')()
server.decorateReply('view', function (template, args) {
// Amazing view rendering engine.
})
server.register(async function (server, opts) {
// We add a view decorator to the current encapsulated
// plugin. This will not throw as outside of this encapsulated
// plugin view is the old one, while inside it is the new one.
server.decorateReply('view', function (template, args) {
// Another rendering engine
})
server.get('/', (req, reply) => {
reply.view('/index.page', { hello: 'world' })
})
}, { prefix: '/bar' })
server.listen({ port: 3000 })
获取器和设置器
¥Getters and Setters
装饰器接受具有 getter 和可选 setter 函数的特殊 "getter/setter" 对象。这允许通过装饰器定义属性,例如:
¥Decorators accept special "getter/setter" objects with getter and optional
setter functions. This allows defining properties via decorators,
for example:
fastify.decorate('foo', {
getter () {
return 'a getter'
}
})
将在 Fastify 实例上定义 foo 属性:
¥Will define the foo property on the Fastify instance:
console.log(fastify.foo) // 'a getter'
getDecorator(name)
用于从 Fastify 实例、Request 或 Reply 中检索现有装饰器。如果装饰器未定义,则会抛出 FST_ERR_DEC_UNDECLARED 错误。
¥Used to retrieve an existing decorator from the Fastify instance, Request, or Reply.
If the decorator is not defined, an FST_ERR_DEC_UNDECLARED error is thrown.
// Get a decorator from the Fastify instance
const utility = fastify.getDecorator('utility')
// Get a decorator from the request object
const user = request.getDecorator('user')
// Get a decorator from the reply object
const helper = reply.getDecorator('helper')
getDecorator 方法可用于依赖验证 - 它可用于在注册时检查所需的装饰器。如果缺少任何一项,启动时就会失败,以确保依赖在请求生命周期内可用。
¥The getDecorator method is useful for dependency validation - it can be used to
check for required decorators at registration time. If any are missing, it fails
at boot, ensuring dependencies are available during the request lifecycle.
fastify.register(async function (fastify) {
// Verify the decorator exists before using it
const usersRepository = fastify.getDecorator('usersRepository')
fastify.get('/users', async function (request, reply) {
return usersRepository.findAll()
})
})
ℹ️ 注意:对于 TypeScript 用户,
getDecorator支持泛型类型参数。有关高级类型示例,请参阅 TypeScript 文档。¥ℹ️ Note: For TypeScript users,
getDecoratorsupports generic type parameters. See the TypeScript documentation for advanced typing examples.
setDecorator(name, value)
用于安全地更新 Request 装饰器的值。如果装饰器不存在,则会抛出 FST_ERR_DEC_UNDECLARED 错误。
¥Used to safely update the value of a Request decorator.
If the decorator does not exist, a FST_ERR_DEC_UNDECLARED error is thrown.
fastify.decorateRequest('user', null)
fastify.addHook('preHandler', async (req, reply) => {
// Safely set the decorator value
req.setDecorator('user', 'Bob Dylan')
})
setDecorator 方法通过在设置装饰器值之前确保其存在来提供运行时安全性,从而防止因装饰器名称拼写错误而导致的错误。
¥The setDecorator method provides runtime safety by ensuring the decorator exists
before setting its value, preventing errors from typos in decorator names.
fastify.decorateRequest('account', null)
fastify.addHook('preHandler', async (req, reply) => {
// This will throw FST_ERR_DEC_UNDECLARED due to typo in decorator name
req.setDecorator('acount', { id: 123 })
})
ℹ️ 注意:对于 TypeScript 用户,请参阅 TypeScript 文档 中有关使用
setDecorator<T>的高级类型示例。¥ℹ️ Note: For TypeScript users, see the TypeScript documentation for advanced typing examples using
setDecorator<T>.