DEBUG.WIKI
晚安云;香港 CN2 ¥15/月
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
搬瓦工;先看线路,再选 VPS;CN2 GIA · 对应线路套餐;查看线路 →

Fastify 改了序列化状态码,仍拿到第一次编译的函数

Fastify复用同一schema对象时,即使更改httpStatus与contentType,仍可能返回首个序列化函数。限定自定义编译器场景,按稳定的schema引用拆分上下文。

自定义 serializerCompiler 按状态码或 Content-Type 生成不同输出,第二次调用 reply.compileSerializationSchema() 却仍输出第一次的格式。若两次传入的是同一个 schema 对象,先检查编译缓存,而不是响应头是否覆盖成功。

Fastify 5.12.7 的这条缓存路径只用 schema 对象作键。httpStatus、contentType 会传给编译器,但它们没有一起组成缓存键;命中缓存后,编译器根本不会再运行。这会让依赖这两个参数的自定义编译器得到出乎预期的结果。

参数变了,对象引用没有变

下面的片段放在已有路由处理函数中,reply 是当前请求的回复对象:

const schema = {
  type: 'object',
  properties: { message: { type: 'string' } },
};

const first = reply.compileSerializationSchema(
  schema, '200', 'application/json',
);
const second = reply.compileSerializationSchema(
  schema, '201', 'application/vnd.example+json',
);

console.log(first === second);

在上述实现中,两次调用共享 schema 引用,所以会返回同一个缓存函数。只有自定义编译器确实使用状态码或媒体类型时,这种复用才会造成相应输出差异;不能仅凭函数相等就认定默认 JSON 序列化已经损坏。

可以在自定义编译器的入口暂时记录 httpStatus、contentType 和调用次数,不记录业务载荷。若第二组参数没有进入编译器,而返回值仍是首个函数,就符合这条路径。反过来,若编译器已经收到正确参数,继续查它自身的分支及之后的响应处理。

为不同编译上下文保留不同 schema 对象

需要两个不同序列化上下文时,提前创建两个稳定的对象引用:

const schema200 = {
  type: 'object',
  properties: { message: { type: 'string' } },
};
const schema201 = { ...schema200 };

const serialize200 = reply.compileSerializationSchema(
  schema200, '200', 'application/json',
);
const serialize201 = reply.compileSerializationSchema(
  schema201, '201', 'application/vnd.example+json',
);

这个浅复制只用于区分顶层缓存键,前提是两份定义共享的嵌套内容保持不变。如果还要修改 properties 内部结构,应独立创建对应定义,不能改了一处就意外改变另一份。也不要在每次请求中不断制造新 schema 来绕过缓存;按实际需要的有限组合预先持有引用。

若工程原本使用路由的 schema.response,可以继续在那里按状态码及 content 声明响应,再按相应入口取序列化函数。不要把所有调用都改成动态编译,或者修改 Fastify 内部的 WeakMap;serializeInput() 和 getSerializationFunction() 也使用相关缓存,单改一处容易造成行为不一致。

编译参数不会替你设置 HTTP 响应

compileSerializationSchema(schema, '201', ...) 中的 201 是提供给编译器的上下文,不会把当前响应状态改成 201。真正发回响应时,仍需由既有路由明确设置 reply.code()、reply.type(),并按原来的发送流程输出正文。

核对时把两件事分开:一是两个上下文是否各自进入自定义编译器、得到预期输出;二是客户端实际收到的状态码、Content-Type 和正文是否一致。保留同一上下文重复调用的检查,确认它仍能复用自己的函数。到 2026 年 10 月 23 日,不能把调整缓存键的设想当成某个正式版已经提供的修复;应用应按实际安装版的缓存契约组织 schema。