Fastify 回復(fù)

2020-02-06 15:41 更新

回復(fù)

簡介

處理函數(shù)的第二個(gè)參數(shù)為 Reply。 Reply 是 Fastify 的一個(gè)核心對(duì)象。它暴露了以下函數(shù)及屬性:

  • .code(statusCode) - 設(shè)置狀態(tài)碼。
  • .status(statusCode) - .code(statusCode) 的別名。
  • .statusCode - 獲取或設(shè)置 HTTP 狀態(tài)碼。
  • .header(name, value) - 設(shè)置響應(yīng) header。
  • .getHeader(name) - 獲取某個(gè) header 的值。
  • .removeHeader(key) - 清除已設(shè)置的 header 的值。
  • .hasHeader(name) - 檢查某個(gè) header 是否設(shè)置。
  • .type(value) - 設(shè)置 Content-Type header。
  • .redirect([code,] url) - 重定向至指定的 url,狀態(tài)碼可選 (默認(rèn)為 302)。
  • .callNotFound() - 調(diào)用自定義的 not found 處理函數(shù)。
  • .serialize(payload) - 使用默認(rèn)的或自定義的 json 序列化工具序列化指定的 payload,并返回處理后的結(jié)果。
  • .serializer(function) - 設(shè)置自定義的 payload 序列化工具。
  • .send(payload) - 向用戶發(fā)送 payload。類型可以是純文本、buffer、JSON、stream,或一個(gè) Error 對(duì)象。
  • .sent - 一個(gè) boolean,檢查 send 是否已被調(diào)用。
  • .res - Node 原生的 http.ServerResponse 對(duì)象。
  • .log - 請求的日志實(shí)例。
  • .request - 請求。
fastify.get('/', options, function (request, reply) {
  // 你的代碼
  reply
    .code(200)
    .header('Content-Type', 'application/json; charset=utf-8')
    .send({ hello: 'world' })
})

另外,Reply 能夠訪問請求的上下文:

fastify.get('/', {config: {foo: 'bar'}}, function (request, reply) {
  reply.send('handler config.foo = ' + reply.context.config.foo)
})

.code(statusCode)

如果沒有設(shè)置 reply.code,statusCode 會(huì)是 200。

.statusCode

獲取或設(shè)置 HTTP 狀態(tài)碼。作為 setter 使用時(shí),是 reply.code() 的別名。

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

.header(key, value)

設(shè)置響應(yīng) header。如果值被省略或?yàn)?undefined,將被強(qiáng)制設(shè)成 ''。

更多信息,請看 http.ServerResponse#setHeader

.getHeader(key)

獲取已設(shè)置的 header 的值。

reply.header('x-foo', 'foo') // 設(shè)置 x-foo header 的值為 foo
reply.getHeader('x-foo') // 'foo'

.removeHeader(key)

清除已設(shè)置的 header 的值。

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

.hasHeader(key)

返回一個(gè) boolean,用于檢查是否設(shè)置了某個(gè) header。

.redirect(dest)

重定向請求至指定的 url,狀態(tài)碼可選,當(dāng)未通過 code 方法設(shè)置時(shí),默認(rèn)為 302。

reply.redirect('/home')

.callNotFound()

調(diào)用自定義的 not found 處理函數(shù)。

reply.callNotFound()

.getResponseTime()

調(diào)用自定義響應(yīng)時(shí)間獲取函數(shù),來計(jì)算自收到請求起的時(shí)間。

const milliseconds = reply.getResponseTime()

.type(contentType, type)

設(shè)置響應(yīng)的 content type。 這是 reply.header('Content-Type', 'the/type') 的簡寫。

reply.type('text/html')

.serializer(func)

.send() 方法會(huì)默認(rèn)將 Buffer、stream、string、undefined、Error 之外類型的值 JSON-序列化。假如你需要在特定的請求上使用自定義的序列化工具,你可以通過 .serializer() 來實(shí)現(xiàn)。要注意的是,如果使用了自定義的序列化工具,你必須同時(shí)設(shè)置 'Content-Type' header。

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

注意,你并不需要在一個(gè) handler 內(nèi)部使用這一工具,因?yàn)?Buffers、streams 以及字符串 (除非已經(jīng)設(shè)置了序列化工具) 被認(rèn)為是已序列化過的。

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

請看 .send() 了解更多關(guān)于發(fā)送不同類型值的信息。

.sent

如你所見,.sent 屬性表明是否已通過 reply.send() 發(fā)送了一個(gè)響應(yīng)。

當(dāng)控制器是一個(gè) async 函數(shù)或返回一個(gè) promise 時(shí),可以手動(dòng)設(shè)置 reply.sent = true,以防 promise resolve 時(shí)自動(dòng)調(diào)用 reply.send()。通過設(shè)置 reply.sent = true,程序能完全掌控底層的請求,且相關(guān)鉤子不會(huì)被觸發(fā)。

請看范例:

app.get('/', (req, reply) => {
  reply.sent = true
  reply.res.end('hello world')

  return Promise.resolve('this will be skipped') // 譯注:該處會(huì)被跳過
})

如果處理函數(shù) reject,將會(huì)記錄一個(gè)錯(cuò)誤。

.send(data)

顧名思義,.send() 是向用戶發(fā)送 payload 的函數(shù)。

對(duì)象

如上文所述,如果你發(fā)送 JSON 對(duì)象時(shí),設(shè)置了輸出的 schema,那么 send 會(huì)使用 fast-json-stringify 來序列化對(duì)象。否則,將使用 JSON.stringify()。

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

字符串

在未設(shè)置 Content-Type 的時(shí)候,字符串會(huì)以 text/plain; charset=utf-8 類型發(fā)送。如果設(shè)置了 Content-Type,且使用自定義序列化工具,那么 send 發(fā)出的字符串會(huì)被序列化。否則,字符串不會(huì)有任何改動(dòng) (除非 Content-Type 的值為 application/json; charset=utf-8,這時(shí),字符串會(huì)像對(duì)象一樣被 JSON-序列化,正如上一節(jié)所述)。

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

Streams

send 開箱即用地支持 stream。它使用 pump 來避免文件描述符 (file descriptors) 的泄露。如果在未設(shè)置 'Content-Type' header 的情況下發(fā)送 stream,它會(huì)被設(shè)定為 'application/octet-stream'。

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

Buffers

未設(shè)置 'Content-Type' header 的情況下發(fā)送 buffer,send 會(huì)將其設(shè)置為 'application/octet-stream'。

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

Errors

若使用 send 發(fā)送一個(gè) Error 的實(shí)例,F(xiàn)astify 會(huì)自動(dòng)創(chuàng)建一個(gè)如下的錯(cuò)誤結(jié)構(gòu):

{
  error: String        // http 錯(cuò)誤信息
  code: String         // Fastify 的錯(cuò)誤代碼
  message: String      // 用戶錯(cuò)誤信息
  statusCode: Number   // http 狀態(tài)碼
}

你可以向 Error 對(duì)象添加自定義屬性,例如 headers,這可以用來增強(qiáng) http 響應(yīng)。注意:如果 send 一個(gè)錯(cuò)誤,但狀態(tài)碼小于 400,F(xiàn)astify 會(huì)自動(dòng)將其設(shè)為 500。

貼士:你可以通過 http-errors 或 fastify-sensible 來簡化生成的錯(cuò)誤:

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

如果你想完全自定義錯(cuò)誤處理,請看 setErrorHandler API。注:當(dāng)自定義錯(cuò)誤處理時(shí),你需要自行記錄日志

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)
})

路由生成的 not found 錯(cuò)誤會(huì)使用 setNotFoundHandler。 API:

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

最終 payload 的類型

發(fā)送的 payload (序列化之后、經(jīng)過任意的 onSend 鉤子) 必須為下列類型之一,否則將會(huì)拋出一個(gè)錯(cuò)誤:

  • string
  • Buffer
  • stream
  • undefined
  • null

Async-Await 與 Promise

Fastify 原生地處理 promise 并支持 async-await。請注意,在下面的例子中我們沒有使用 reply.send。

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' }
})

被 reject 的 promise 默認(rèn)發(fā)送 500 狀態(tài)碼。要修改回復(fù),可以 reject 一個(gè) promise,或在 async 函數(shù) 中進(jìn)行 throw 操作,同時(shí)附帶上一個(gè)有 statusCode (或 status) 與 message 屬性的對(duì)象。

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

想要了解更多?請看 Routes#async-await。

.then(fullfilled, rejected)

顧名思義,Reply 對(duì)象能被等待。換句話說,await reply 將會(huì)等待,直到回復(fù)被發(fā)送。 如上的 await 語法調(diào)用了 reply.then()。

reply.then(fullfilled, rejected) 接受兩個(gè)參數(shù):

  • fullfilled 會(huì)在響應(yīng)完全發(fā)送后被調(diào)用。
  • rejected 會(huì)在底層的 stream 出現(xiàn)錯(cuò)誤時(shí)被調(diào)用。例如,socket 連接被破壞時(shí)。

更多細(xì)節(jié),請看:


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)