跳至主要内容
版本: 最新版本 (v5.0.x)

回复

回复

简介

处理函数的第二个参数是Reply。Reply 是一个 Fastify 核心对象,它公开了以下函数和属性

  • .code(statusCode) - 设置状态码。
  • .status(statusCode) - .code(statusCode) 的别名。
  • .statusCode - 读取和设置 HTTP 状态码。
  • .elapsedTime - 返回自 Fastify 接收请求以来经过的时间。
  • .server - 对 fastify 实例对象的引用。
  • .header(name, value) - 设置响应头。
  • .headers(object) - 将对象的全部键作为响应头设置。
  • .getHeader(name) - 检索已设置头的值。
  • .getHeaders() - 获取当前所有响应头的浅拷贝。
  • .removeHeader(key) - 删除之前设置的头部的值。
  • .hasHeader(name) - 确定是否已设置头。
  • .writeEarlyHints(hints, callback) - 在响应准备过程中向用户发送早期提示。
  • .trailer(key, function) - 设置响应尾部。
  • .hasTrailer(key) - 确定是否已设置尾部。
  • .removeTrailer(key) - 删除之前设置的尾部值。
  • .type(value) - 设置 Content-Type 头部。
  • .redirect(dest, [code,]) - 重定向到指定的 URL,状态码是可选的(默认为 302)。
  • .callNotFound() - 调用自定义的未找到处理程序。
  • .serialize(payload) - 使用默认的 JSON 序列化器或自定义序列化器(如果已设置)序列化指定的有效负载,并返回序列化的有效负载。
  • .getSerializationFunction(schema | httpStatus, [contentType]) - 返回指定模式或 HTTP 状态的序列化函数(如果已设置)。
  • .compileSerializationSchema(schema, [httpStatus], [contentType]) - 编译指定的模式,并使用默认(或自定义)的 SerializerCompiler 返回一个序列化函数。如果提供,可选的 httpStatus 将转发到 SerializerCompiler,默认为 undefined
  • .serializeInput(data, schema, [,httpStatus], [contentType]) - 使用指定的模式序列化指定的数据,并返回序列化的有效负载。如果提供可选的 httpStatuscontentType,则该函数将使用为该特定内容类型和 HTTP 状态代码提供的序列化函数。默认为 undefined
  • .serializer(function) - 为有效负载设置自定义序列化器。
  • .send(payload) - 将有效负载发送给用户,可以是纯文本、缓冲区、JSON、流或错误对象。
  • .sent - 一个布尔值,如果需要知道是否已调用 send,则可以使用它。
  • .hijack() - 中断正常的请求生命周期。
  • .raw - 来自 Node 核心库的 http.ServerResponse
  • .log - 传入请求的日志实例。
  • .request - 传入的请求。
fastify.get('/', options, function (request, reply) {
// Your code
reply
.code(200)
.header('Content-Type', 'application/json; charset=utf-8')
.send({ hello: 'world' })
})

.code(statusCode)

如果未通过 reply.code 设置,则结果 statusCode 将为 200

.elapsedTime

调用自定义响应时间获取器来计算自 Fastify 接收请求以来经过的时间。

请注意,除非在 onResponse 钩子 中调用此函数,否则它将始终返回 0

const milliseconds = reply.elapsedTime

.statusCode

此属性读取和设置 HTTP 状态码。用作设置器时,它是 reply.code() 的别名。

if (reply.statusCode >= 299) {
reply.statusCode = 500
}

.server

Fastify 服务器实例,作用域限定为当前的 封装上下文

fastify.decorate('util', function util () {
return 'foo'
})

fastify.get('/', async function (req, rep) {
return rep.server.util() // foo
})

.header(key, value)

设置响应头。如果省略或未定义值,则将其强制转换为 ''

注意:头部的值必须使用 encodeURI 或类似模块(如 encodeurl)正确编码。无效字符将导致 500 TypeError 响应。

有关更多信息,请参阅 http.ServerResponse#setHeader

    • 当使用 set-cookie 作为键发送不同的值作为 cookie 时,每个值都将作为 cookie 发送,而不是替换先前的值。

      reply.header('set-cookie', 'foo');
      reply.header('set-cookie', 'bar');
    • 浏览器只会考虑 set-cookie 头部的最新引用。这样做是为了避免在将 set-cookie 头部添加到回复时解析它,并加快回复的序列化速度。

    • 要重置 set-cookie 头部,您需要显式调用 reply.removeHeader('set-cookie')此处详细了解 .removeHeader(key)

.headers(object)

将对象的全部键作为响应头设置。.header 将在幕后被调用。

reply.headers({
'x-foo': 'foo',
'x-bar': 'bar'
})

.getHeader(key)

检索之前设置的头部的值。

reply.header('x-foo', 'foo') // setHeader: key, value
reply.getHeader('x-foo') // 'foo'

.getHeaders()

获取当前所有响应头的浅拷贝,包括通过原始 http.ServerResponse 设置的那些。请注意,通过 Fastify 设置的头优先于通过 http.ServerResponse 设置的头。

reply.header('x-foo', 'foo')
reply.header('x-bar', 'bar')
reply.raw.setHeader('x-foo', 'foo2')
reply.getHeaders() // { 'x-foo': 'foo', 'x-bar': 'bar' }

.removeHeader(key)

删除之前设置的头部的值。

reply.header('x-foo', 'foo')
reply.removeHeader('x-foo')
reply.getHeader('x-foo') // undefined

.hasHeader(key)

返回一个布尔值,指示是否已设置指定的头。

.writeEarlyHints(hints, callback)

向客户端发送早期提示。早期提示允许客户端在最终响应发送之前开始处理资源。这可以通过允许客户端在服务器仍在生成响应时预加载或预连接到资源来提高性能。

hints 参数是一个包含早期提示键值对的对象。

示例

reply.writeEarlyHints({ 
Link: '</styles.css>; rel=preload; as=style'
});

可选的回调参数是一个函数,一旦提示发送或发生错误,就会调用该函数。

.trailer(key, function)

设置响应尾部。尾部通常用于需要大量资源才能在 data 后发送的头,例如 Server-TimingEtag。它可以确保客户端尽快收到响应数据。

注意:一旦使用尾部,将添加 Transfer-Encoding: chunked 头。这是在 Node.js 中使用尾部的硬性要求。

注意:传递给 done 回调的任何错误都将被忽略。如果您对错误感兴趣,可以开启 debug 级别日志记录。

reply.trailer('server-timing', function() {
return 'db;dur=53, app;dur=47.2'
})

const { createHash } = require('node:crypto')
// trailer function also receive two argument
// @param {object} reply fastify reply
// @param {string|Buffer|null} payload payload that already sent, note that it will be null when stream is sent
// @param {function} done callback to set trailer value
reply.trailer('content-md5', function(reply, payload, done) {
const hash = createHash('md5')
hash.update(payload)
done(null, hash.disgest('hex'))
})

// when you prefer async-await
reply.trailer('content-md5', async function(reply, payload) {
const hash = createHash('md5')
hash.update(payload)
return hash.disgest('hex')
})

.hasTrailer(key)

返回一个布尔值,指示是否已设置指定的尾部。

.removeTrailer(key)

删除之前设置的尾部值。

reply.trailer('server-timing', function() {
return 'db;dur=53, app;dur=47.2'
})
reply.removeTrailer('server-timing')
reply.getTrailer('server-timing') // undefined

.redirect(dest,[code ,])

将请求重定向到指定的 URL,状态码是可选的,默认为 302(如果状态码尚未通过调用 code 设置)。

注意:输入 URL 必须使用 encodeURI 或类似模块(如 encodeurl)正确编码。无效的 URL 将导致 500 TypeError 响应。

示例(无 reply.code() 调用)将状态码设置为 302 并重定向到 /home

reply.redirect('/home')

示例(无 reply.code() 调用)将状态码设置为 303 并重定向到 /home

reply.redirect('/home', 303)

示例(reply.code() 调用)将状态码设置为 303 并重定向到 /home

reply.code(303).redirect('/home')

示例(reply.code() 调用)将状态码设置为 302 并重定向到 /home

reply.code(303).redirect('/home', 302)

.callNotFound()

调用自定义的未找到处理程序。请注意,它只会调用 setNotFoundHandler 中指定的 preHandler 钩子。

reply.callNotFound()

.type(contentType)

设置响应的内容类型。这是 reply.header('Content-Type', 'the/type') 的快捷方式。

reply.type('text/html')

如果Content-Type具有 JSON 子类型,并且未设置字符集参数,则默认情况下将使用utf-8作为字符集。

.getSerializationFunction(schema | httpStatus,[contentType])

通过使用提供的schemahttpStatus以及可选的contentType调用此函数,它将返回一个可用于序列化各种输入的serialzation函数。如果使用提供的任何输入都找不到序列化函数,则返回undefined

这在很大程度上取决于附加到路由的schema#responses或使用compileSerializationSchema编译的序列化函数。

const serialize = reply
.getSerializationFunction({
type: 'object',
properties: {
foo: {
type: 'string'
}
}
})
serialize({ foo: 'bar' }) // '{"foo":"bar"}'

// or

const serialize = reply
.getSerializationFunction(200)
serialize({ foo: 'bar' }) // '{"foo":"bar"}'

// or

const serialize = reply
.getSerializationFunction(200, 'application/json')
serialize({ foo: 'bar' }) // '{"foo":"bar"}'

有关如何编译序列化模式的更多信息,请参阅.compileSerializationSchema(schema, [httpStatus], [contentType])

.compileSerializationSchema(schema,[httpStatus], [contentType])

此函数将编译序列化模式并返回一个可用于序列化数据的函数。返回的函数(也称为序列化函数)是使用提供的SerializerCompiler编译的。此外,它还使用WeakMap进行缓存,以减少编译调用次数。

如果提供了可选参数httpStatuscontentType,则会直接转发到SerializerCompiler,因此如果使用自定义SerializerCompiler,则可以使用它来编译序列化函数。

这在很大程度上取决于附加到路由的schema#responses或使用compileSerializationSchema编译的序列化函数。

const serialize = reply
.compileSerializationSchema({
type: 'object',
properties: {
foo: {
type: 'string'
}
}
})
serialize({ foo: 'bar' }) // '{"foo":"bar"}'

// or

const serialize = reply
.compileSerializationSchema({
type: 'object',
properties: {
foo: {
type: 'string'
}
}
}, 200)
serialize({ foo: 'bar' }) // '{"foo":"bar"}'

// or

const serialize = reply
.compileSerializationSchema({
'3xx': {
content: {
'application/json': {
schema: {
name: { type: 'string' },
phone: { type: 'number' }
}
}
}
}
}, '3xx', 'application/json')
serialize({ name: 'Jone', phone: 201090909090 }) // '{"name":"Jone", "phone":201090909090}'

请注意,在使用此函数时应谨慎,因为它将根据提供的模式缓存已编译的序列化函数。如果提供的模式发生变异或更改,序列化函数将无法检测到模式已更改,例如,它将根据之前提供的模式的引用重用之前编译的序列化函数。

如果需要更改模式的属性,始终选择创建一个全新的对象,否则实现将无法从缓存机制中获益。

:以下面的模式为例

const schema1 = {
type: 'object',
properties: {
foo: {
type: 'string'
}
}
}

不要

const serialize = reply.compileSerializationSchema(schema1)

// Later on...
schema1.properties.foo.type. = 'integer'
const newSerialize = reply.compileSerializationSchema(schema1)

console.log(newSerialize === serialize) // true

改为

const serialize = reply.compileSerializationSchema(schema1)

// Later on...
const newSchema = Object.assign({}, schema1)
newSchema.properties.foo.type = 'integer'

const newSerialize = reply.compileSerializationSchema(newSchema)

console.log(newSerialize === serialize) // false

.serializeInput(data,[schema | httpStatus], [httpStatus], [contentType])

此函数将根据提供的模式或 HTTP 状态代码序列化输入数据。如果两者都提供,则httpStatus将优先。

如果给定schema没有序列化函数,则将编译一个新的序列化函数,并在提供时转发httpStatuscontentType

reply
.serializeInput({ foo: 'bar'}, {
type: 'object',
properties: {
foo: {
type: 'string'
}
}
}) // '{"foo":"bar"}'

// or

reply
.serializeInput({ foo: 'bar'}, {
type: 'object',
properties: {
foo: {
type: 'string'
}
}
}, 200) // '{"foo":"bar"}'

// or

reply
.serializeInput({ foo: 'bar'}, 200) // '{"foo":"bar"}'

// or

reply
.serializeInput({ name: 'Jone', age: 18 }, '200', 'application/vnd.v1+json') // '{"name": "Jone", "age": 18}'

有关如何编译序列化模式的更多信息,请参阅.compileSerializationSchema(schema, [httpStatus], [contentType])

.serializer(func)

默认情况下,.send()将 JSON 序列化任何不是以下类型之一的值:BufferstreamstringundefinedError。如果需要为特定请求将默认序列化程序替换为自定义序列化程序,可以使用.serializer()实用程序。请注意,如果使用自定义序列化程序,则必须设置自定义'Content-Type'标头。

reply
.header('Content-Type', 'application/x-protobuf')
.serializer(protoBuf.serialize)

请注意,您不需要在handler内部使用此实用程序,因为缓冲区、流和字符串(除非设置了序列化程序)被认为已经序列化。

reply
.header('Content-Type', 'application/x-protobuf')
.send(protoBuf.serialize(data))

有关发送不同类型值的更多信息,请参阅.send()

.raw

这是来自 Node 核心库的http.ServerResponse。在使用 Fastify Reply 对象时,使用Reply.raw函数需自行承担风险,因为您跳过了 Fastify 处理 HTTP 响应的所有逻辑。例如:

app.get('/cookie-2', (req, reply) => {
reply.setCookie('session', 'value', { secure: false }) // this will not be used

// in this case we are using only the nodejs http server response object
reply.raw.writeHead(200, { 'Content-Type': 'text/plain' })
reply.raw.write('ok')
reply.raw.end()
})

Reply中解释了Reply.raw误用的另一个示例。

.sent

顾名思义,.sent是一个属性,用于指示响应是否已通过reply.send()发送。如果使用了reply.hijack(),它也将为true

如果路由处理程序定义为异步函数或返回 Promise,则可以调用reply.hijack()以指示应跳过处理程序 Promise 解析后自动调用reply.send()。通过调用reply.hijack(),应用程序声明对低级请求和响应的全部责任。此外,不会调用钩子。

直接修改.sent属性已弃用。请使用上述.hijack()方法来达到相同的效果。

.hijack()

有时您可能需要停止正常请求生命周期的执行并手动处理发送响应。

为了实现这一点,Fastify 提供了reply.hijack()方法,可以在请求生命周期中调用(在调用reply.send()之前的任何时候),并允许您阻止 Fastify 发送响应,并阻止运行其余的钩子(如果在之前劫持了回复,则包括用户处理程序)。

app.get('/', (req, reply) => {
reply.hijack()
reply.raw.end('hello world')

return Promise.resolve('this will be skipped')
})

如果使用reply.raw将响应发送回用户,则仍将执行onResponse钩子。

.send(data)

顾名思义,.send()是将有效负载发送给最终用户的函数。

对象

如上所述,如果要发送 JSON 对象,则如果设置了输出模式,send将使用fast-json-stringify序列化对象,否则将使用JSON.stringify()

fastify.get('/json', options, function (request, reply) {
reply.send({ hello: 'world' })
})

字符串

如果将字符串传递给send且没有Content-Type,则它将作为text/plain; charset=utf-8发送。如果设置了Content-Type标头并将字符串传递给send,则如果设置了自定义序列化程序,它将使用该序列化程序进行序列化,否则将按原样发送(除非Content-Type标头设置为application/json; charset=utf-8,在这种情况下,它将像对象一样进行 JSON 序列化——请参阅上面的部分)。

fastify.get('/json', options, function (request, reply) {
reply.send('plain string')
})

如果要发送流并且尚未设置'Content-Type'标头,则send会将其设置为'application/octet-stream'

如上所述,流被认为是预先序列化的,因此它们将按原样发送,无需响应验证。

const fs = require('node:fs')

fastify.get('/streams', function (request, reply) {
const stream = fs.createReadStream('some-file', 'utf8')
reply.header('Content-Type', 'application/octet-stream')
reply.send(stream)
})

在使用 async-await 时,您需要返回或等待回复对象

const fs = require('node:fs')

fastify.get('/streams', async function (request, reply) {
const stream = fs.createReadStream('some-file', 'utf8')
reply.header('Content-Type', 'application/octet-stream')
return reply.send(stream)
})

缓冲区

如果要发送缓冲区并且尚未设置'Content-Type'标头,则send会将其设置为'application/octet-stream'

如上所述,缓冲区被认为是预先序列化的,因此它们将按原样发送,无需响应验证。

const fs = require('node:fs')

fastify.get('/streams', function (request, reply) {
fs.readFile('some-file', (err, fileBuffer) => {
reply.send(err || fileBuffer)
})
})

在使用 async-await 时,您需要返回或等待回复对象

const fs = require('node:fs')

fastify.get('/streams', async function (request, reply) {
fs.readFile('some-file', (err, fileBuffer) => {
reply.send(err || fileBuffer)
})
return reply
})

类型数组

send将类型数组视为缓冲区,如果尚未设置,则将'Content-Type'标头设置为'application/octet-stream'

如上所述,类型数组/缓冲区被认为是预先序列化的,因此它们将按原样发送,无需响应验证。

const fs = require('node:fs')

fastify.get('/streams', function (request, reply) {
const typedArray = new Uint16Array(10)
reply.send(typedArray)
})

可读流

ReadableStream将被视为上面提到的节点流,内容被认为是预先序列化的,因此它们将按原样发送,无需响应验证。

const fs = require('node:fs')
const { ReadableStream } = require('node:stream/web')

fastify.get('/streams', function (request, reply) {
const stream = fs.createReadStream('some-file')
reply.header('Content-Type', 'application/octet-stream')
reply.send(ReadableStream.from(stream))
})

响应

Response允许在一个地方管理回复有效负载、状态代码和标头。Response中提供的有效负载被认为是预先序列化的,因此它们将按原样发送,无需响应验证。

请注意,在使用Response时,状态代码和标头不会直接反映到reply.statusCodereply.getHeaders()。这种行为是基于Response仅允许只读状态代码和标头。数据不允许进行双向编辑,并且在onSend钩子中检查payload时可能会造成混淆。

const fs = require('node:fs')
const { ReadableStream } = require('node:stream/web')

fastify.get('/streams', function (request, reply) {
const stream = fs.createReadStream('some-file')
const readableStream = ReadableStream.from(stream)
const response = new Response(readableStream, {
status: 200,
headers: { 'content-type': 'application/octet-stream' }
})
reply.send(response)
})

错误

如果将Error的实例对象传递给send,Fastify 将自动创建如下结构的错误

{
error: String // the HTTP error message
code: String // the Fastify error code
message: String // the user error message
statusCode: Number // the HTTP status code
}

您可以向 Error 对象添加自定义属性,例如headers,这些属性将用于增强 HTTP 响应。

注意:如果将错误传递给send并且 statusCode 小于 400,Fastify 将自动将其设置为 500。

提示:您可以使用http-errors模块或@fastify/sensible插件来简化错误并生成错误

fastify.get('/', function (request, reply) {
reply.send(httpErrors.Gone())
})

要自定义 JSON 错误输出,您可以通过以下方式进行操作:

  • 为所需的 StatusCode 设置响应 JSON 模式
  • 将其他属性添加到Error实例

请注意,如果返回的状态代码不在响应模式列表中,则将应用默认行为。

fastify.get('/', {
schema: {
response: {
501: {
type: 'object',
properties: {
statusCode: { type: 'number' },
code: { type: 'string' },
error: { type: 'string' },
message: { type: 'string' },
time: { type: 'string' }
}
}
}
}
}, function (request, reply) {
const error = new Error('This endpoint has not been implemented')
error.time = 'it will be implemented in two weeks'
reply.code(501).send(error)
})

如果要自定义错误处理,请查看setErrorHandler API。

注意:自定义错误处理程序时,您负责记录日志

API

fastify.setErrorHandler(function (error, request, reply) {
request.log.warn(error)
var statusCode = error.statusCode >= 400 ? error.statusCode : 500
reply
.code(statusCode)
.type('text/plain')
.send(statusCode >= 500 ? 'Internal server error' : error.message)
})

请注意,在自定义错误处理程序中调用reply.send(error)将把错误发送到默认错误处理程序。有关更多信息,请查看Reply 生命周期

路由器生成的未找到错误将使用setNotFoundHandler

API

fastify.setNotFoundHandler(function (request, reply) {
reply
.code(404)
.type('text/plain')
.send('a custom not found')
})

最终有效负载的类型

发送的有效负载类型(序列化后并经过任何onSend钩子)必须是以下类型之一,否则将抛出错误

  • 字符串
  • 缓冲区
  • 未定义

Async-Await 和 Promise

Fastify 本机处理 Promise 并支持 async-await。

请注意,在以下示例中,我们未使用 reply.send。

const { promisify } = require('node:util')
const delay = promisify(setTimeout)

fastify.get('/promises', options, function (request, reply) {
return delay(200).then(() => { return { hello: 'world' }})
})

fastify.get('/async-await', options, async function (request, reply) {
await delay(200)
return { hello: 'world' }
})

拒绝的 Promise 默认使用500 HTTP 状态代码。拒绝 Promise 或在async functionthrow一个包含statusCode(或status)和message属性的对象以修改回复。

fastify.get('/teapot', async function (request, reply) {
const err = new Error()
err.statusCode = 418
err.message = 'short and stout'
throw err
})

fastify.get('/botnet', async function (request, reply) {
throw { statusCode: 418, message: 'short and stout' }
// will return to the client the same json
})

如果要了解更多信息,请查看Routes#async-await

.then(fulfilled, rejected)

顾名思义,可以等待Reply对象,即await reply将等待直到回复发送。await语法调用reply.then()

reply.then(fulfilled, rejected)接受两个参数

  • 当响应完全发送时,将调用fulfilled
  • 如果底层流发生错误(例如套接字已被销毁),则会调用rejected

更多详细信息,请参阅