钩子
钩子
¥Hooks
钩子使用 fastify.addHook
方法注册,允许你监听应用或请求/响应生命周期中的特定事件。你必须在事件触发之前注册一个钩子,否则事件将丢失。
¥Hooks are registered with the fastify.addHook
method and allow you to listen
to specific events in the application or request/response lifecycle. You have to
register a hook before the event is triggered, otherwise, the event is lost.
通过使用钩子,你可以直接与 Fastify 的生命周期进行交互。有请求/响应钩子和应用钩子:
¥By using hooks you can interact directly with the lifecycle of Fastify. There are Request/Reply hooks and application hooks:
注意:使用 async
/await
或返回 Promise
时,done
回调不可用。如果在这种情况下调用 done
回调,可能会发生意外行为,例如重复调用处理程序。
¥Notice: the done
callback is not available when using async
/await
or
returning a Promise
. If you do invoke a done
callback in this situation
unexpected behavior may occur, e.g. duplicate invocation of handlers.
Request/Reply 钩子
¥Request/Reply Hooks
¥Request and Reply are the core Fastify objects.
done
是继续 lifecycle 的函数。
¥done
is the function to continue with the lifecycle.
通过查看 生命周期页面,很容易理解每个钩子在哪里执行。
¥It is easy to understand where each hook is executed by looking at the lifecycle page.
Hooks 受到 Fastify 封装的影响,因此可以应用于选定的路由。有关更多信息,请参阅 Scopes 部分。
¥Hooks are affected by Fastify's encapsulation, and can thus be applied to selected routes. See the Scopes section for more information.
你可以在请求/响应中使用八种不同的钩子(按执行顺序):
¥There are eight different hooks that you can use in Request/Reply (in order of execution):
onRequest
fastify.addHook('onRequest', (request, reply, done) => {
// Some code
done()
})
或 async/await
:
¥Or async/await
:
fastify.addHook('onRequest', async (request, reply) => {
// Some code
await asyncMethod()
})
注意:在 onRequest 钩子中,request.body
将始终是 undefined
,因为主体解析发生在 preValidation 钩子之前。
¥Notice: in the onRequest hook, request.body
will always be
undefined
, because the body parsing happens before the
preValidation hook.
preParsing
如果你正在使用 preParsing
钩子,则可以在解析请求有效负载流之前对其进行转换。它像其他钩子一样接收请求和响应对象,以及具有当前请求负载的流。
¥If you are using the preParsing
hook, you can transform the request payload
stream before it is parsed. It receives the request and reply objects as other
hooks, and a stream with the current request payload.
如果它返回一个值(通过 return
或通过回调函数),它必须返回一个流。
¥If it returns a value (via return
or via the callback function), it must
return a stream.
例如,你可以解压缩请求正文:
¥For instance, you can decompress the request body:
fastify.addHook('preParsing', (request, reply, payload, done) => {
// Some code
done(null, newPayload)
})
或 async/await
:
¥Or async/await
:
fastify.addHook('preParsing', async (request, reply, payload) => {
// Some code
await asyncMethod()
return newPayload
})
注意:在 preParsing 钩子中,request.body
将始终是 undefined
,因为主体解析发生在 preValidation 钩子之前。
¥Notice: in the preParsing hook, request.body
will always be
undefined
, because the body parsing happens before the
preValidation hook.
注意:你还应该将 receivedEncodedLength
属性添加到返回的流中。此属性用于将请求负载与 Content-Length
标头值正确匹配。理想情况下,应在每个收到的块上更新此属性。
¥Notice: you should also add a receivedEncodedLength
property to the
returned stream. This property is used to correctly match the request payload
with the Content-Length
header value. Ideally, this property should be updated
on each received chunk.
注意:检查返回流的大小是否不超过 bodyLimit
选项中设置的限制。
¥Notice: The size of the returned stream is checked to not exceed the limit
set in bodyLimit
option.
preValidation
如果你正在使用 preValidation
钩子,则可以在验证有效负载之前更改它。例如:
¥If you are using the preValidation
hook, you can change the payload before it
is validated. For example:
fastify.addHook('preValidation', (request, reply, done) => {
request.body = { ...request.body, importantKey: 'randomString' }
done()
})
或 async/await
:
¥Or async/await
:
fastify.addHook('preValidation', async (request, reply) => {
const importantKey = await generateRandomString()
request.body = { ...request.body, importantKey }
})
preHandler
preHandler
钩子允许你指定在路由处理程序之前执行的函数。
¥The preHandler
hook allows you to specify a function that is executed before
a routes's handler.
fastify.addHook('preHandler', (request, reply, done) => {
// some code
done()
})
或 async/await
:
¥Or async/await
:
fastify.addHook('preHandler', async (request, reply) => {
// Some code
await asyncMethod()
})
preSerialization
如果你正在使用 preSerialization
钩子,则可以在序列化之前更改(或替换)有效负载。例如:
¥If you are using the preSerialization
hook, you can change (or replace) the
payload before it is serialized. For example:
fastify.addHook('preSerialization', (request, reply, payload, done) => {
const err = null
const newPayload = { wrapped: payload }
done(err, newPayload)
})
或 async/await
:
¥Or async/await
:
fastify.addHook('preSerialization', async (request, reply, payload) => {
return { wrapped: payload }
})
注意:如果有效负载是 string
、Buffer
、stream
或 null
,则不会调用钩子。
¥Note: the hook is NOT called if the payload is a string
, a Buffer
, a
stream
, or null
.
onError
fastify.addHook('onError', (request, reply, error, done) => {
// Some code
done()
})
或 async/await
:
¥Or async/await
:
fastify.addHook('onError', async (request, reply, error) => {
// Useful for custom error logging
// You should not use this hook to update the error
})
如果你需要执行一些自定义错误日志记录或添加一些特定标头以防出现错误,则此钩子非常有用。
¥This hook is useful if you need to do some custom error logging or add some specific header in case of error.
它不用于更改错误,调用 reply.send
将引发异常。
¥It is not intended for changing the error, and calling reply.send
will throw
an exception.
此钩子仅在执行 setErrorHandler
设置的自定义错误处理程序 后执行,并且仅当自定义错误处理程序将错误发送回用户时才会执行(请注意,默认错误处理程序始终将错误发送回用户)。
¥This hook will be executed only after
the Custom Error Handler set by setErrorHandler
has been executed, and only if the custom error handler sends an error back to the
user
(Note that the default error handler always sends the error back to the
user).
注意:与其他钩子不同,不支持将错误传递给 done
函数。
¥Notice: unlike the other hooks, passing an error to the done
function is not
supported.
onSend
如果你正在使用 onSend
钩子,则可以更改有效负载。例如:
¥If you are using the onSend
hook, you can change the payload. For example:
fastify.addHook('onSend', (request, reply, payload, done) => {
const err = null;
const newPayload = payload.replace('some-text', 'some-new-text')
done(err, newPayload)
})
或 async/await
:
¥Or async/await
:
fastify.addHook('onSend', async (request, reply, payload) => {
const newPayload = payload.replace('some-text', 'some-new-text')
return newPayload
})
你还可以通过将有效负载替换为 null
来清除有效负载以发送带有空主体的响应:
¥You can also clear the payload to send a response with an empty body by
replacing the payload with null
:
fastify.addHook('onSend', (request, reply, payload, done) => {
reply.code(304)
const newPayload = null
done(null, newPayload)
})
你还可以通过将有效负载替换为空字符串
''
来发送空主体,但请注意,这将导致Content-Length
标头设置为0
,而如果有效负载为null
,则不会设置Content-Length
标头。¥You can also send an empty body by replacing the payload with the empty string
''
, but be aware that this will cause theContent-Length
header to be set to0
, whereas theContent-Length
header will not be set if the payload isnull
.
注意:如果更改有效负载,则只能将其更改为 string
、Buffer
、stream
、ReadableStream
、Response
或 null
。
¥Note: If you change the payload, you may only change it to a string
, a
Buffer
, a stream
, a ReadableStream
, a Response
, or null
.
onResponse
fastify.addHook('onResponse', (request, reply, done) => {
// Some code
done()
})
或 async/await
:
¥Or async/await
:
fastify.addHook('onResponse', async (request, reply) => {
// Some code
await asyncMethod()
})
当发送响应时,将执行 onResponse
钩子,因此你将无法向客户端发送更多数据。但是,它对于将数据发送到外部服务(例如收集统计数据)很有用。
¥The onResponse
hook is executed when a response has been sent, so you will not
be able to send more data to the client. It can however be useful for sending
data to external services, for example, to gather statistics.
注意:将 disableRequestLogging
设置为 true
将禁用 onResponse
钩子内的任何错误日志。在这种情况下,使用 try - catch
记录错误。
¥Note: setting disableRequestLogging
to true
will disable any error log
inside the onResponse
hook. In this case use try - catch
to log errors.
onTimeout
fastify.addHook('onTimeout', (request, reply, done) => {
// Some code
done()
})
或 async/await
:
¥Or async/await
:
fastify.addHook('onTimeout', async (request, reply) => {
// Some code
await asyncMethod()
})
如果你需要监视服务中的请求超时(如果在 Fastify 实例上设置了 connectionTimeout
属性),则 onTimeout
很有用。当请求超时且 HTTP 套接字已挂断时,将执行 onTimeout
钩子。因此,你将无法向客户端发送数据。
¥onTimeout
is useful if you need to monitor the request timed out in your
service (if the connectionTimeout
property is set on the Fastify instance).
The onTimeout
hook is executed when a request is timed out and the HTTP socket
has been hung up. Therefore, you will not be able to send data to the client.
onRequestAbort
fastify.addHook('onRequestAbort', (request, done) => {
// Some code
done()
})
或 async/await
:
¥Or async/await
:
fastify.addHook('onRequestAbort', async (request) => {
// Some code
await asyncMethod()
})
当客户端在处理整个请求之前关闭连接时,将执行 onRequestAbort
钩子。因此,你将无法向客户端发送数据。
¥The onRequestAbort
hook is executed when a client closes the connection before
the entire request has been processed. Therefore, you will not be able to send
data to the client.
注意:客户端中止检测并不完全可靠。看:Detecting-When-Clients-Abort.md
¥Notice: client abort detection is not completely reliable. See: Detecting-When-Clients-Abort.md
通过钩子管理错误
¥Manage Errors from a hook
如果在执行钩子时出现错误,只需将其传递给 done()
,Fastify 就会自动关闭请求并向用户发送相应的错误代码。
¥If you get an error during the execution of your hook, just pass it to done()
and Fastify will automatically close the request and send the appropriate error
code to the user.
fastify.addHook('onRequest', (request, reply, done) => {
done(new Error('Some error'))
})
如果你想将自定义错误代码传递给用户,只需使用 reply.code()
:
¥If you want to pass a custom error code to the user, just use reply.code()
:
fastify.addHook('preHandler', (request, reply, done) => {
reply.code(400)
done(new Error('Some error'))
})
错误将由 Reply
处理。
¥The error will be handled by Reply
.
或者如果你使用 async/await
,你可以抛出一个错误:
¥Or if you're using async/await
you can just throw an error:
fastify.addHook('onRequest', async (request, reply) => {
throw new Error('Some error')
})
响应来自钩子的请求
¥Respond to a request from a hook
如果需要,你可以在到达路由处理程序之前响应请求,例如在实现身份验证钩子时。从钩子回复意味着钩子链已停止,其余钩子和处理程序均未执行。如果钩子使用回调方法,即它不是 async
函数或它返回 Promise
,则只需调用 reply.send()
并避免调用回调即可。如果钩子是 async
,则必须在函数返回或 promise 解析之前调用 reply.send()
,否则请求将继续。当在 promise 链之外调用 reply.send()
时,return reply
很重要,否则请求将被执行两次。
¥If needed, you can respond to a request before you reach the route handler, for
example when implementing an authentication hook. Replying from a hook implies
that the hook chain is stopped and the rest of the hooks and handlers are
not executed. If the hook is using the callback approach, i.e. it is not an
async
function or it returns a Promise
, it is as simple as calling
reply.send()
and avoiding calling the callback. If the hook is async
,
reply.send()
must be called before the function returns or the promise
resolves, otherwise, the request will proceed. When reply.send()
is called
outside of the promise chain, it is important to return reply
otherwise the
request will be executed twice.
重要的是不要混合回调和 async
/Promise
,否则钩子链将被执行两次。
¥It is important to not mix callbacks and async
/Promise
, otherwise the
hook chain will be executed twice.
如果你正在使用 onRequest
或 preHandler
,请使用 reply.send
。
¥If you are using onRequest
or preHandler
use reply.send
.
fastify.addHook('onRequest', (request, reply, done) => {
reply.send('Early response')
})
// Works with async functions too
fastify.addHook('preHandler', async (request, reply) => {
setTimeout(() => {
reply.send({ hello: 'from prehandler' })
})
return reply // mandatory, so the request is not executed further
// Commenting the line above will allow the hooks to continue and fail with FST_ERR_REP_ALREADY_SENT
})
如果你想用流进行响应,你应该避免对钩子使用 async
函数。如果必须使用 async
函数,你的代码将需要遵循 test/hooks-async.js 中的模式。
¥If you want to respond with a stream, you should avoid using an async
function
for the hook. If you must use an async
function, your code will need to follow
the pattern in
test/hooks-async.js.
fastify.addHook('onRequest', (request, reply, done) => {
const stream = fs.createReadStream('some-file', 'utf8')
reply.send(stream)
})
如果你发送的响应没有 await
,请确保始终 return reply
:
¥If you are sending a response without await
on it, make sure to always return
reply
:
fastify.addHook('preHandler', async (request, reply) => {
setImmediate(() => { reply.send('hello') })
// This is needed to signal the handler to wait for a response
// to be sent outside of the promise chain
return reply
})
fastify.addHook('preHandler', async (request, reply) => {
// the @fastify/static plugin will send a file asynchronously,
// so we should return reply
reply.sendFile('myfile')
return reply
})
应用钩子
¥Application Hooks
你也可以连接到应用生命周期。
¥You can hook into the application-lifecycle as well.
onReady
在服务器开始监听请求之前以及调用 .ready()
时触发。它无法更改路由或添加新的钩子。注册的钩子函数是串行执行的。只有在所有 onReady
钩子函数完成后,服务器才会开始监听请求。钩子函数接受一个参数:在钩子函数完成后调用的回调 done
。使用绑定到相关 Fastify 实例的 this
调用钩子函数。
¥Triggered before the server starts listening for requests and when .ready()
is
invoked. It cannot change the routes or add new hooks. Registered hook functions
are executed serially. Only after all onReady
hook functions have completed
will the server start listening for requests. Hook functions accept one
argument: a callback, done
, to be invoked after the hook function is complete.
Hook functions are invoked with this
bound to the associated Fastify instance.
// callback style
fastify.addHook('onReady', function (done) {
// Some code
const err = null;
done(err)
})
// or async/await style
fastify.addHook('onReady', async function () {
// Some async code
await loadCacheFromDatabase()
})
onListen
当服务器开始监听请求时触发。钩子一个接一个地运行。如果钩子函数导致错误,则会记录并忽略该错误,从而允许钩子队列继续。钩子函数接受一个参数:在钩子函数完成后调用的回调 done
。使用绑定到相关 Fastify 实例的 this
调用钩子函数。
¥Triggered when the server starts listening for requests. The hooks run one
after another. If a hook function causes an error, it is logged and
ignored, allowing the queue of hooks to continue. Hook functions accept one
argument: a callback, done
, to be invoked after the hook function is
complete. Hook functions are invoked with this
bound to the associated
Fastify instance.
这是 fastify.server.on('listening', () => {})
的替代方案。
¥This is an alternative to fastify.server.on('listening', () => {})
.
// callback style
fastify.addHook('onListen', function (done) {
// Some code
const err = null;
done(err)
})
// or async/await style
fastify.addHook('onListen', async function () {
// Some async code
})
注意 当使用
fastify.inject()
或fastify.ready()
启动服务器时,此钩子将不会运行¥This hook will not run when the server is started using
fastify.inject()
orfastify.ready()
onClose
在所有正在进行的 HTTP 请求完成后,调用 fastify.close()
停止服务器时触发。当 plugins 需要 "shutdown" 事件时,它很有用,例如,关闭与数据库的打开连接。
¥Triggered when fastify.close()
is invoked to stop the server, after all in-flight
HTTP requests have been completed.
It is useful when plugins need a "shutdown" event, for example,
to close an open connection to a database.
钩子函数将 Fastify 实例作为第一个参数,并将 done
回调用于同步钩子函数。
¥The hook function takes the Fastify instance as a first argument,
and a done
callback for synchronous hook functions.
// callback style
fastify.addHook('onClose', (instance, done) => {
// Some code
done()
})
// or async/await style
fastify.addHook('onClose', async (instance) => {
// Some async code
await closeDatabaseConnections()
})
preClose
在所有正在进行的 HTTP 请求完成之前,调用 fastify.close()
停止服务器时触发。当 plugins 设置了一些附加到 HTTP 服务器的状态以防止服务器关闭时,它很有用。你不太可能需要使用此钩子,在最常见的情况下使用 onClose
。
¥Triggered when fastify.close()
is invoked to stop the server, before all in-flight
HTTP requests have been completed.
It is useful when plugins have set up some state attached
to the HTTP server that would prevent the server to close.
It is unlikely you will need to use this hook,
use the onClose
for the most common case.
// callback style
fastify.addHook('preClose', (done) => {
// Some code
done()
})
// or async/await style
fastify.addHook('preClose', async () => {
// Some async code
await removeSomeServerState()
})
onRoute
注册新路由时触发。监听器传递一个 routeOptions
对象作为唯一参数。该接口是同步的,因此不会向监听器传递回调。这个钩子是封装的。
¥Triggered when a new route is registered. Listeners are passed a routeOptions
object as the sole parameter. The interface is synchronous, and, as such, the
listeners are not passed a callback. This hook is encapsulated.
fastify.addHook('onRoute', (routeOptions) => {
//Some code
routeOptions.method
routeOptions.schema
routeOptions.url // the complete URL of the route, it will include the prefix if any
routeOptions.path // `url` alias
routeOptions.routePath // the URL of the route without the prefix
routeOptions.bodyLimit
routeOptions.logLevel
routeOptions.logSerializers
routeOptions.prefix
})
如果你正在编写插件并且需要自定义应用路由,例如修改选项或添加新的路由钩子,那么这里就是正确的地方。
¥If you are authoring a plugin and you need to customize application routes, like modifying the options or adding new route hooks, this is the right place.
fastify.addHook('onRoute', (routeOptions) => {
function onPreSerialization(request, reply, payload, done) {
// Your code
done(null, payload)
}
// preSerialization can be an array or undefined
routeOptions.preSerialization = [...(routeOptions.preSerialization || []), onPreSerialization]
})
要在 onRoute hook 中添加更多路由,必须正确标记路由。如果没有标记,钩子将陷入无限循环。推荐的方法如下所示。
¥To add more routes within an onRoute hook, the routes must be tagged correctly. The hook will run into an infinite loop if not tagged. The recommended approach is shown below.
const kRouteAlreadyProcessed = Symbol('route-already-processed')
fastify.addHook('onRoute', function (routeOptions) {
const { url, method } = routeOptions
const isAlreadyProcessed = (routeOptions.custom && routeOptions.custom[kRouteAlreadyProcessed]) || false
if (!isAlreadyProcessed) {
this.route({
url,
method,
custom: {
[kRouteAlreadyProcessed]: true
},
handler: () => {}
})
}
})
有关更多详细信息,请参阅此 issue。
¥For more details, see this issue.
onRegister
当注册新插件并创建新封装上下文时触发。该钩子将在注册代码之前执行。
¥Triggered when a new plugin is registered and a new encapsulation context is created. The hook will be executed before the registered code.
如果你正在开发一个需要知道何时形成插件上下文的插件,并且你希望在该特定上下文中进行操作,则此钩子会很有用,因此封装了此钩子。
¥This hook can be useful if you are developing a plugin that needs to know when a plugin context is formed, and you want to operate in that specific context, thus this hook is encapsulated.
注意:如果插件封装在 fastify-plugin
内,则不会调用此钩子。
¥Note: This hook will not be called if a plugin is wrapped inside
fastify-plugin
.
fastify.decorate('data', [])
fastify.register(async (instance, opts) => {
instance.data.push('hello')
console.log(instance.data) // ['hello']
instance.register(async (instance, opts) => {
instance.data.push('world')
console.log(instance.data) // ['hello', 'world']
}, { prefix: '/hola' })
}, { prefix: '/ciao' })
fastify.register(async (instance, opts) => {
console.log(instance.data) // []
}, { prefix: '/hello' })
fastify.addHook('onRegister', (instance, opts) => {
// Create a new array from the old one
// but without keeping the reference
// allowing the user to have encapsulated
// instances of the `data` property
instance.data = instance.data.slice()
// the options of the new registered instance
console.log(opts.prefix)
})
作用域
¥Scope
除 onClose 外,所有钩子都已封装。这意味着你可以使用 register
来决定钩子应该在哪里运行,如 插件指南 中所述。如果你传递一个函数,该函数将绑定到正确的 Fastify 上下文,并且从那里你可以完全访问 Fastify API。
¥Except for onClose, all hooks are encapsulated. This means that you
can decide where your hooks should run by using register
as explained in the
plugins guide. If you pass a function, that
function is bound to the right Fastify context and from there you have full
access to the Fastify API.
fastify.addHook('onRequest', function (request, reply, done) {
const self = this // Fastify context
done()
})
请注意,每个钩子中的 Fastify 上下文与注册路由的插件相同,例如:
¥Note that the Fastify context in each hook is the same as the plugin where the route was registered, for example:
fastify.addHook('onRequest', async function (req, reply) {
if (req.raw.url === '/nested') {
assert.strictEqual(this.foo, 'bar')
} else {
assert.strictEqual(this.foo, undefined)
}
})
fastify.get('/', async function (req, reply) {
assert.strictEqual(this.foo, undefined)
return { hello: 'world' }
})
fastify.register(async function plugin (fastify, opts) {
fastify.decorate('foo', 'bar')
fastify.get('/nested', async function (req, reply) {
assert.strictEqual(this.foo, 'bar')
return { hello: 'world' }
})
})
警告:如果你使用 箭头函数 声明函数,则 this
将不是 Fastify,而是当前范围的函数。
¥Warn: if you declare the function with an arrow
function,
the this
will not be Fastify, but the one of the current scope.
路由级别钩子
¥Route level hooks
你可以声明一个或多个自定义生命周期钩子(onRequest、onResponse、preParsing、preValidation、preHandler、preSerialization、onSend、onTimeout 和 onError)钩子,这些钩子对于路由是唯一的。如果这样做,这些钩子将始终作为其类别中的最后一个钩子执行。
¥You can declare one or more custom lifecycle hooks (onRequest, onResponse, preParsing, preValidation, preHandler, preSerialization, onSend, onTimeout, and onError) hook(s) that will be unique for the route. If you do so, those hooks are always executed as the last hook in their category.
如果你需要实现身份验证,这非常有用,其中 preParsing 或 preValidation 钩子正是你所需要的。多个路由级别的钩子也可以指定为一个数组。
¥This can be useful if you need to implement authentication, where the preParsing or preValidation hooks are exactly what you need. Multiple route-level hooks can also be specified as an array.
fastify.addHook('onRequest', (request, reply, done) => {
// Your code
done()
})
fastify.addHook('onResponse', (request, reply, done) => {
// your code
done()
})
fastify.addHook('preParsing', (request, reply, done) => {
// Your code
done()
})
fastify.addHook('preValidation', (request, reply, done) => {
// Your code
done()
})
fastify.addHook('preHandler', (request, reply, done) => {
// Your code
done()
})
fastify.addHook('preSerialization', (request, reply, payload, done) => {
// Your code
done(null, payload)
})
fastify.addHook('onSend', (request, reply, payload, done) => {
// Your code
done(null, payload)
})
fastify.addHook('onTimeout', (request, reply, done) => {
// Your code
done()
})
fastify.addHook('onError', (request, reply, error, done) => {
// Your code
done()
})
fastify.route({
method: 'GET',
url: '/',
schema: { ... },
onRequest: function (request, reply, done) {
// This hook will always be executed after the shared `onRequest` hooks
done()
},
// // Example with an async hook. All hooks support this syntax
//
// onRequest: async function (request, reply) {
// // This hook will always be executed after the shared `onRequest` hooks
// await ...
// }
onResponse: function (request, reply, done) {
// this hook will always be executed after the shared `onResponse` hooks
done()
},
preParsing: function (request, reply, done) {
// This hook will always be executed after the shared `preParsing` hooks
done()
},
preValidation: function (request, reply, done) {
// This hook will always be executed after the shared `preValidation` hooks
done()
},
preHandler: function (request, reply, done) {
// This hook will always be executed after the shared `preHandler` hooks
done()
},
// // Example with an array. All hooks support this syntax.
//
// preHandler: [function (request, reply, done) {
// // This hook will always be executed after the shared `preHandler` hooks
// done()
// }],
preSerialization: (request, reply, payload, done) => {
// This hook will always be executed after the shared `preSerialization` hooks
done(null, payload)
},
onSend: (request, reply, payload, done) => {
// This hook will always be executed after the shared `onSend` hooks
done(null, payload)
},
onTimeout: (request, reply, done) => {
// This hook will always be executed after the shared `onTimeout` hooks
done()
},
onError: (request, reply, error, done) => {
// This hook will always be executed after the shared `onError` hooks
done()
},
handler: function (request, reply) {
reply.send({ hello: 'world' })
}
})
注意:这两个选项都接受函数数组。
¥Note: both options also accept an array of functions.
使用 Hooks 注入自定义属性
¥Using Hooks to Inject Custom Properties
你可以使用钩子将自定义属性注入传入请求中。这对于重用控制器中的钩子处理后的数据很有用。
¥You can use a hook to inject custom properties into incoming requests. This is useful for reusing processed data from hooks in controllers.
一个非常常见的用例是,例如,根据用户的令牌检查用户身份验证,然后将恢复的数据存储到 请求 实例中。这样,你的控制器可以使用 request.authenticatedUser
或任何你想要调用的名称轻松读取它。它可能是这样的:
¥A very common use case is, for example, checking user authentication based
on their token and then storing their recovered data into
the Request instance. This way, your controllers can read it
easily with request.authenticatedUser
or whatever you want to call it.
That's how it might look like:
fastify.addHook('preParsing', async (request) => {
request.authenticatedUser = {
id: 42,
name: 'Jane Doe',
role: 'admin'
}
})
fastify.get('/me/is-admin', async function (req, reply) {
return { isAdmin: req.authenticatedUser?.role === 'admin' || false }
})
请注意,.authenticatedUser
实际上可以是你选择的任何属性名称。使用你自己的自定义属性可以防止你更改现有属性,这将是危险且具有破坏性的操作。因此,请小心并确保你的属性是全新的,并且仅针对非常具体和小型的情况(例如本示例)使用此方法。
¥Note that .authenticatedUser
could actually be any property name
chosen by yourself. Using your own custom property prevents you
from mutating existing properties, which
would be a dangerous and destructive operation. So be careful and
make sure your property is entirely new, also using this approach
only for very specific and small cases like this example.
关于此示例中的 TypeScript,你需要更新 FastifyRequest
核心接口以包含你的新属性类型(有关更多信息,请参阅 TypeScript 页面),例如:
¥Regarding TypeScript in this example, you'd need to update the
FastifyRequest
core interface to include your new property typing
(for more about it, see TypeScript page), like:
interface AuthenticatedUser { /* ... */ }
declare module 'fastify' {
export interface FastifyRequest {
authenticatedUser?: AuthenticatedUser;
}
}
虽然这是一种非常务实的方法,但如果你尝试执行更复杂的事情来更改这些核心对象,那么请考虑创建自定义 Plugin。
¥Although this is a very pragmatic approach, if you're trying to do something more complex that changes these core objects, then consider creating a custom Plugin instead.
诊断通道钩子
¥Diagnostics Channel Hooks
一个 diagnostics_channel
发布事件 'fastify.initialization'
在初始化时发生。Fastify 实例作为传入对象的属性传递到钩子中。此时,可以与实例交互以添加钩子、插件、路由或任何其他类型的修改。
¥One diagnostics_channel
publish event, 'fastify.initialization'
, happens at initialization time. The
Fastify instance is passed into the hook as a property of the object passed in.
At this point, the instance can be interacted with to add hooks, plugins,
routes, or any other sort of modification.
例如,跟踪包可能会执行如下操作(当然,这是一种简化)。这将以典型的 "首先需要检测工具" 方式加载到跟踪包初始化中的文件中。
¥For example, a tracing package might do something like the following (which is, of course, a simplification). This would be in a file loaded in the initialization of the tracking package, in the typical "require instrumentation tools first" fashion.
const tracer = /* retrieved from elsewhere in the package */
const dc = require('node:diagnostics_channel')
const channel = dc.channel('fastify.initialization')
const spans = new WeakMap()
channel.subscribe(function ({ fastify }) {
fastify.addHook('onRequest', (request, reply, done) => {
const span = tracer.startSpan('fastify.request.handler')
spans.set(request, span)
done()
})
fastify.addHook('onResponse', (request, reply, done) => {
const span = spans.get(request)
span.finish()
done()
})
})
注意:TracingChannel 类 API 目前处于实验阶段,即使在 Node.js 的 semver-patch 版本中也可能会进行重大更改。
¥Note: The TracingChannel class API is currently experimental and may undergo breaking changes even in semver-patch releases of Node.js.
根据 追踪通道 命名法,每个请求都会发布另外五个事件。通道名称及其接收的事件列表如下:
¥Five other events are published on a per-request basis following the Tracing Channel nomenclature. The list of the channel names and the event they receive is:
tracing:fastify.request.handler:start
:总是触发¥
tracing:fastify.request.handler:start
: Always fires{ request: Request, reply: Reply, route: { url, method } }
tracing:fastify.request.handler:end
:总是触发¥
tracing:fastify.request.handler:end
: Always fires{ request: Request, reply: Reply, route: { url, method }, async: Bool }
tracing:fastify.request.handler:asyncStart
:为 promise/async 处理程序触发¥
tracing:fastify.request.handler:asyncStart
: Fires for promise/async handlers{ request: Request, reply: Reply, route: { url, method } }
tracing:fastify.request.handler:asyncEnd
:为 promise/async 处理程序触发¥
tracing:fastify.request.handler:asyncEnd
: Fires for promise/async handlers{ request: Request, reply: Reply, route: { url, method } }
tracing:fastify.request.handler:error
:发生错误时触发¥
tracing:fastify.request.handler:error
: Fires when an error occurs{ request: Request, reply: Reply, route: { url, method }, error: Error }
对象实例对于与给定请求关联的所有事件保持不变。所有有效负载都包含 request
和 reply
属性,它们是 Fastify 的 Request
和 Reply
实例的实例。它们还包括一个 route
属性,它是一个具有匹配的 url
模式(例如 /collection/:id
)和 method
HTTP 方法(例如 GET
)的对象。:start
和 :end
事件始终会针对请求触发。如果请求处理程序是 async
函数或返回 Promise
的函数,则 :asyncStart
和 :asyncEnd
事件也会触发。最后,:error
事件包含与请求失败相关的 error
属性。
¥The object instance remains the same for all events associated with a given
request. All payloads include a request
and reply
property which are an
instance of Fastify's Request
and Reply
instances. They also include a
route
property which is an object with the matched url
pattern (e.g.
/collection/:id
) and the method
HTTP method (e.g. GET
). The :start
and
:end
events always fire for requests. If a request handler is an async
function or one that returns a Promise
then the :asyncStart
and :asyncEnd
events also fire. Finally, the :error
event contains an error
property
associated with the request's failure.
这些事件可以这样接收:
¥These events can be received like so:
const dc = require('node:diagnostics_channel')
const channel = dc.channel('tracing:fastify.request.handler:start')
channel.subscribe((msg) => {
console.log(msg.request, msg.reply)
})