Fastify 中文文档 (v4.28.1)

迁移到 V3

本文帮助你从 Fastify v2 迁移到 v3。

开始之前,请确保所有 v2 中不推荐用法的警示都已修复。v2 的这些特性在新版都已被移除,升级后你将不能使用它们。(#1750)

重大改动

中间件支持 (#2014)

从 Fastify v3 开始,框架本身便不再支持中间件功能了。

要使用 Express 的中间件的话,请安装 fastify-expressmiddie

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 的 RequestReply 对象,而非原生的对象。

任何依赖于原生对象而非 Fastify 对象上的 requestreply 属性的自定义的序列化器,都应当升级。

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)

为了未来工具的改善,setSchemaCompilersetSchemaResolver 被替换成了 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 开始,onRouteonRegister 钩子的行为发生了微小的变化。

  • 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]) (这里 reqIncomingMessage) 仍然受支持,但不推荐使用。

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,但错误得到控制
})

更多特性与改善