运行时 Hooks

app.hooks.on(name, handler) 用来观察或轻量 patch 框架运行时生命周期。它适合做请求审计、校验通过后的请求记录、响应 header patch、出站调用监控、service 调用追踪、OpenAPI 文档补丁等横切逻辑。

const off = app.hooks.on("validation:success", ({ req, route }) => {
  app.logger.info(
    { requestId: req.requestId, route: route.path },
    "validated request",
  );
});

app.hooks.on("response:before", ({ headers }) => ({
  headers: { ...headers, "x-powered-by": "vext" },
}));

off();

app.hooks.on() 返回注销函数。app.hooks 是保留属性,不能用 app.extend("hooks", ...) 覆盖。

常见场景

只记录校验通过的请求

如果你想在中间件里记录请求,但排除被参数校验拒绝的请求,不需要手动捕获 VextValidationError。使用 validation:success 更直接:

app.hooks.on("validation:success", ({ req, route }) => {
  app.logger.info(
    { requestId: req.requestId, method: req.method, route: route.path },
    "request validated",
  );
});

响应发送前补 header

app.hooks.on("response:before", ({ headers }) => ({
  headers: {
    ...headers,
    "x-service": "billing",
  },
}));

response:before 是同步生命周期,不能返回 Promise。

追踪 service 调用

app.hooks.on("service:beforeCall", ({ service, method }) => {
  app.logger.debug({ service, method }, "service call");
});

app.hooks.on("service:error", ({ service, method, error }) => {
  app.logger.warn({ service, method, error }, "service failed");
});

service hook 也是同步生命周期。如需异步上报,建议写入队列或使用不阻塞主调用的日志传输。

监控出站请求和 proxy

app.hooks.on("fetch:before", ({ headers }) => {
  headers.set("x-client", "vext");
});

app.hooks.on("proxy:after", ({ target, status, requestId }) => {
  app.logger.info({ target, status, requestId }, "proxy response");
});

修改 OpenAPI 文档

app.hooks.on("openapi:afterGenerate", ({ document }) => {
  const spec = document as { info?: Record<string, unknown> };

  return {
    document: {
      ...spec,
      info: {
        ...(spec.info ?? {}),
        title: "Internal API",
      },
    },
  };
});

执行策略

Hook 类型策略
request:startvalidation:successhandler:beforefetch:beforeproxy:beforeplugin:beforeSetupserver:beforeListenhandler 抛错会向上传播,可阻止后续流程
response:beforeerror:beforeResponseservice:beforeCallservice:afterCallservice:erroropenapi:*同步生命周期,不允许返回 Promise
handler:afterhandler:errorresponse:aftererror:afterResponsefetch:after/errorproxy:after/errorcache:*plugin:afterSetup/errorroutes:readyapp:ready/closesafe emit,hook 抛错会被记录但不改变主流程

可用 Hook

名称触发点
request:startrequestId 生成后、进入全局中间件链;404 兜底也会触发,matched=false
route:matchedadapter 匹配路由后、执行校验和 handler 前
route:notFound没有路由匹配,404 响应发送前
validation:success路由 validate 全部通过,next()
validation:error路由 validate 失败,抛出 VextValidationError
handler:before业务 handler 调用前
handler:after业务 handler 成功返回后
handler:error业务 handler 抛错后、进入全局错误处理前
response:beforeres.json/rawJson/text/stream 发送前,可同步 patch data/status/headers
response:after响应发送后
error:beforeResponseerror-handler 写 JSON 错误响应前,可同步 patch body/status
error:afterResponse错误响应发送后
fetch:beforeapp.fetch 出站前,可修改 Headers
fetch:afterapp.fetch 返回 Response
fetch:errorapp.fetch 最终失败时
proxy:beforeapp.fetch.proxy 解析上游请求后、发送前
proxy:afterapp.fetch.proxy 收到上游响应后、透传前
proxy:errorapp.fetch.proxy 本地错误、超时或上游网络失败时
service:loadedservice 冷启动加载并挂载后
service:reloadeddev soft reload 重新实例化 service 后
service:beforeCallservice 方法调用前
service:afterCallservice 方法成功返回后
service:errorservice 方法抛错或 reject 后
cache:hitcache:misscache:writecache:error路由级响应缓存读写生命周期
plugin:beforeSetupplugin:afterSetupplugin:error插件 setup() 前后和失败;插件不能观察自己的 beforeSetup
routes:ready路由扫描和注册完成后
openapi:beforeGenerateopenapi:afterGenerateOpenAPI 文档生成前后;afterGenerate 可同步替换 document
server:beforeListenHTTP server 开始监听前
app:readyonReady 执行前后
app:closeonClose / shutdown 执行前后

更多参考