Fastify 中文文档 (v4.28.1)
迁移到 V3
本文帮助你从 Fastify v2 迁移到 v3。
开始之前,请确保所有 v2 中不推荐用法的警示都已修复。v2 的这些特性在新版都已被移除,升级后你将不能使用它们。(#1750)
重大改动
中间件支持 (#2014)
从 Fastify v3 开始,框架本身便不再支持中间件功能了。
要使用 Express 的中间件的话,请安装 fastify-express
或 middie
。
v2:
// 在 Fastify v2 中使用 Express 的 `cors` 中间件。
fastify.use(require('cors')());
v3:
// 在 Fastify v3 中使用 Express 的 `cors` 中间件。
await fastify.register(require('fastify-express'));
fastify.use(require('cors')());
日志序列化 (#2017)
日志的序列化器得到了升级,现在它接受 Fastify 的 Request
和 Reply
对象,而非原生的对象。
任何依赖于原生对象而非 Fastify 对象上的 request
或 reply
属性的自定义的序列化器,都应当升级。
v2:
const fastify = require('fastify')({
logger: {
serializers: {
res(res) {
return {
statusCode: res.statusCode,
customProp: res.customProp
};
}
}
}
});
v3:
const fastify = require('fastify')({
logger: {
serializers: {
res(reply) {
return {
statusCode: reply.statusCode, // 无需更改
customProp: reply.raw.customProp // 从 res 对象 (译注:即 Node.js 原生的响应对象,此处为 raw) 中记录属性
};
}
}
}
});
schema 代入 (schema substitution) (#2023)
非标准替换方式
的支持被移除了,取而代之的是符合 JSON Schema 标准的 $ref
方案。要更好地理解这一改变,请阅读《Fastify v3 的验证与序列化》一文。
v2:
const schema = {
body: 'schemaId#'
};
fastify.route({ method, url, schema, handler });
v3:
const schema = {
body: {
$ref: 'schemaId#'
}
};
fastify.route({ method, url, schema, handler });
schema 验证选项 (#2023)
为了未来工具的改善,setSchemaCompiler
和 setSchemaResolver
被替换成了 setValidatorCompiler
。要更好地理解这一改变,请阅读《Fastify v3 的验证与序列化》一文。
v2:
const fastify = Fastify();
const ajv = new AJV();
ajv.addSchema(schemaA);
ajv.addSchema(schemaB);
fastify.setSchemaCompiler(schema => ajv.compile(schema));
fastify.setSchemaResolver(ref => ajv.getSchema(ref).schema);
v3:
const fastify = Fastify();
const ajv = new AJV();
ajv.addSchema(schemaA);
ajv.addSchema(schemaB);
fastify.setValidatorCompiler(({ schema, method, url, httpPart }) =>
ajv.compile(schema)
);
preParsing 钩子的行为 (#2286)
为了支持针对请求 payload 的操作,从 Fastify v3 开始,preParsing
钩子的行为发生了微小的变化。
该钩子现在有一个额外的参数,payload
,因此,新的函数签名是 fn(request, reply, payload, done)
或 async fn(request, reply, payload)
。
钩子可以通过 done(null, stream)
回调,或 async 函数返回一个 stream。
如果返回了新的 stream,那么在之后的钩子里,这个新的 stream 将代替原有的 stream。这种做法的一个用途是处理经过压缩的请求。
新的 stream 还应当添加 receivedEncodedLength
属性,来反映客户端数据的真实大小。举例而言,假如请求被压缩了,那么该属性便是压缩后的 payload 的大小。该属性可以 (且应当) 在 data
事件中动态更新。
原先 Fastify v2 的语法仍然受支持,但不推荐使用。
钩子的行为 (#2004)
为了支持钩子的封装,从 Fastify v3 开始,onRoute
与 onRegister
钩子的行为发生了微小的变化。
onRoute
- 现在改为了异步调用,且在同一个封装的作用域内会继承。因此,你得在注册其他插件 之前 注册该钩子。onRegister
- 和 onRoute 一样。唯一区别在于现在第一次的调用者不是框架本身了,而是首个注册的插件。
Content Type 解析器的语法 (#2286)
在 Fastify v3 中,Content Type 解析器现在有了单一函数签名。
新的签名是 fn(request, payload, done)
或 async fn(request, payload)
。注意现在 request
是 Fastify 的请求对象,而不是 IncomingMessage
了。默认情况下,payload 是一个 stream。如果在 addContentTypeParser
中使用了 parseAs
选项,那么 payload
会被当做该选项的值来对待 (string 或 buffer)。
原先的函数签名 fn(req, [done])
或 fn(req, payload, [done])
(这里 req
是 IncomingMessage
) 仍然受支持,但不推荐使用。
TypeScript 支持
版本 3 的类型系统发生了改变。新的系统带来了泛型约束 (generic constraining) 与默认值,以及定义请求 body,querystring 等 schema 的新方式!
v2:
interface PingQuerystring {
foo?: number;
}
interface PingParams {
bar?: string;
}
interface PingHeaders {
a?: string;
}
interface PingBody {
baz?: string;
}
server.get<PingQuerystring, PingParams, PingHeaders, PingBody>(
'/ping/:bar',
opts,
(request, reply) => {
console.log(request.query); // 类型为 `PingQuerystring`
console.log(request.params); // 类型为 `PingParams`
console.log(request.headers); // 类型为 `PingHeaders`
console.log(request.body); // 类型为 `PingBody`
}
);
v3:
server.get<{
Querystring: PingQuerystring;
Params: PingParams;
Headers: PingHeaders;
Body: PingBody;
}>('/ping/:bar', opts, async (request, reply) => {
console.log(request.query); // 类型为 `PingQuerystring`
console.log(request.params); // 类型为 `PingParams`
console.log(request.headers); // 类型为 `PingHeaders`
console.log(request.body); // 类型为 `PingBody`
});
管理未捕获异常 (#2073)
在同步的路由方法里,一旦一个错误被抛出,服务器将会如设定的那样崩溃,且不调用 .setErrorHandler()
方法。现在这一行为发生了改变,所有在同步或异步的路由中未捕获的异常,都将得到控制。
v2:
fastify.setErrorHandler((error, request, reply) => {
// 不会调用
reply.send(error)
})
fastify.get('/', (request, reply) => {
const maybeAnArray = request.body.something ? [] : 'I am a string'
maybeAnArray.substr() // 抛错:[].substr is not a function,同时服务器崩溃
})
v3:
fastify.setErrorHandler((error, request, reply) => {
// 会调用
reply.send(error)
})
fastify.get('/', (request, reply) => {
const maybeAnArray = request.body.something ? [] : 'I am a string'
maybeAnArray.substr() // 抛错:[].substr is not a function,但错误得到控制
})
更多特性与改善
- 不管如何注册钩子,它们总拥有一致的上下文 (#2005)
- 推荐使用
request.raw
及reply.raw
,而非request.req
和reply.res
(#2008) - 移除
modifyCoreObjects
选项 (#2015) - 添加
connectionTimeout
选项 (#2086) - 添加
keepAliveTimeout
选项 (#2086) - 插件支持 async-await (#2093)
- 支持将对象作为错误抛出 (#2134)