跳至主要內容
版本:最新版 (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]) - 返回指定 schema 或 http 狀態 (如果已設定) 的序列化函式。
  • .compileSerializationSchema(schema, [httpStatus], [contentType]) - 編譯指定的 schema,並使用預設 (或自訂) 的 SerializerCompiler 返回序列化函式。選填的 httpStatus 如果提供,則會轉送到 SerializerCompiler,預設為 undefined
  • .serializeInput(data, schema, [,httpStatus], [contentType]) - 使用指定的 schema 序列化指定的資料,並返回序列化後的酬載。如果提供了選填的 httpStatuscontentType,則函式將使用針對該特定內容類型和 HTTP 狀態碼所提供的序列化器函式。預設為 undefined
  • .serializer(function) - 設定酬載的自訂序列化器。
  • .send(payload) - 將酬載傳送給使用者,可以是純文字、緩衝區、JSON、串流或 Error 物件。
  • .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

叫用自訂回應時間 getter,以計算自 Fastify 接收請求以來經過的時間量。

請注意,除非在 onResponse hook 中呼叫此函式,否則它將始終返回 0

const milliseconds = reply.elapsedTime

.statusCode

此屬性讀取和設定 HTTP 狀態碼。當用作 setter 時,它是 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'
});

選填的 callback 參數是一個函式,該函式將在傳送提示後或發生錯誤時被呼叫。

.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() 會將任何非 BufferstreamstringundefinedError 的值進行 JSON 序列化。如果您需要將預設序列化程式替換為特定請求的自訂序列化程式,可以使用 .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' })
})

字串

如果您在沒有 Content-Type 的情況下將字串傳遞給 send,則會以 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 時,您將需要返回或等待 reply 物件

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 時,您將需要返回或等待 reply 物件

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

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 中提供的酬載被視為已預先序列化,因此會以未修改的形式傳送,且不進行回應驗證。

使用 Response 時請注意,狀態碼和標頭不會直接反映到 reply.statusCodereply.getHeaders()。此行為基於 Response 僅允許 readonly 狀態碼和標頭。不允許雙向編輯資料,並且在檢查 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 且狀態碼小於 400,Fastify 會自動將其設定為 500。

提示:您可以使用 http-errors 模組或 @fastify/sensible 外掛程式來簡化錯誤,以產生錯誤

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

若要自訂 JSON 錯誤輸出,您可以執行以下操作

  • 為您需要的狀態碼設定回應 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) 會將錯誤傳送到預設的錯誤處理常式。有關更多資訊,請查看 回應生命週期

路由器產生的找不到錯誤將會使用 setNotFoundHandler

API

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

最終酬載的類型

傳送的酬載類型(在序列化並經過任何 onSend 鉤子之後)必須是下列其中一種類型,否則會擲回錯誤

  • 字串
  • 緩衝區
  • 串流
  • 未定義
  • null

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,也就是說 await reply 會等待直到回覆被傳送。await 語法會呼叫 reply.then()

reply.then(fulfilled, rejected) 接受兩個參數

  • 當回應已完全傳送時,將會呼叫 fulfilled
  • 如果底層串流發生錯誤(例如 socket 已被銷毀),將會呼叫 rejected

更多詳細資訊,請參閱