Fastify 服務(wù)器方法

2020-02-06 15:37 更新

工廠函數(shù)

Fastify 模塊導(dǎo)出了一個(gè)工廠函數(shù),可以用于創(chuàng)建新的Fastify server 實(shí)例。這個(gè)工廠函數(shù)的參數(shù)是一個(gè)配置對象,用于自定義最終生成的實(shí)例。本文描述了這一對象中可用的屬性。

http2

設(shè)置為 true,則會(huì)使用 Node.js 原生的 HTTP/2 模塊來綁定 socket。

  • 默認(rèn)值:false

https

用于配置服務(wù)器的 TLS socket 的對象。其選項(xiàng)與 Node.js 原生的 createServer 方法一致。 當(dāng)值為 null 時(shí),socket 連接將不會(huì)配置 TLS。

當(dāng) http2 選項(xiàng)設(shè)置時(shí),https 選項(xiàng)也會(huì)被應(yīng)用。

  • 默認(rèn)值:null

ignoreTrailingSlash

Fastify 使用 find-my-way 處理路由。該選項(xiàng)為 true 時(shí),尾斜杠將被省略。 這一選項(xiàng)應(yīng)用于 server 實(shí)例上注冊的所有路由。

  • 默認(rèn)值:false
const fastify = require('fastify')({
  ignoreTrailingSlash: true
})

// 同時(shí)注冊 "/foo" 與 "/foo/"
fastify.get('/foo/', function (req, reply) {
  reply.send('foo')
})

// 同時(shí)注冊 "/bar" 與 "/bar/"
fastify.get('/bar', function (req, reply) {
  reply.send('bar')
})

maxParamLength

你可以為通過 maxParamLength 選項(xiàng)為帶參路由 (無論是標(biāo)準(zhǔn)的、正則匹配的,還是復(fù)數(shù)的) 設(shè)置最大參數(shù)長度。選項(xiàng)的默認(rèn)值為 100 字符。當(dāng)使用正則匹配的路由時(shí),這非常有用,可以幫你抵御 DoS 攻擊。當(dāng)達(dá)到長度限制時(shí),將觸發(fā) not found 路由。

bodyLimit

定義服務(wù)器可接受的最大 payload,以字節(jié)為單位。

  • 默認(rèn)值:1048576 (1MiB)

onProtoPoisoning

由 secure-json-parse 提供的功能,指定解析帶有 __proto__ 鍵的 JSON 對象時(shí)框架的行為。 更多關(guān)于原型污染 (prototype poisoning) 的內(nèi)容請看 https://hueniverse.com/a-tale-of-prototype-poisoning-2610fa170061。

允許的值為 'error'、'remove' 與 'ignore'。

  • 默認(rèn)值:'error'

onConstructorPoisoning

由 secure-json-parse 提供的功能,指定解析帶有 constructor 的 JSON 對象時(shí)框架的行為。 更多關(guān)于原型污染的內(nèi)容請看 https://hueniverse.com/a-tale-of-prototype-poisoning-2610fa170061。

允許的值為 'error'、'remove' 與 'ignore'。

  • 默認(rèn)值:'ignore'

logger

Fastify 依托 Pino 內(nèi)建了一個(gè)日志工具。該屬性用于配置日志實(shí)例。

屬性可用的值為:

  • 默認(rèn): false。禁用日志。所有記錄日志的方法將會(huì)指向一個(gè)空日志工具 abstract-logging 的實(shí)例。
  • pinoInstance: 一個(gè)已被實(shí)例化的 Pino 實(shí)例。內(nèi)建的日志工具將指向這個(gè)實(shí)例。
  • object: 標(biāo)準(zhǔn)的 Pino 選項(xiàng)對象。 它會(huì)被直接傳遞進(jìn) Pino 的構(gòu)造函數(shù)。如果下列屬性未在該對象中定義,它們將被相應(yīng)地添加:genReqId: 一個(gè)同步函數(shù),用于生成請求的標(biāo)識(shí)符。默認(rèn)生成按次序排列的標(biāo)識(shí)符。level: 最低的日志級(jí)別。若未被設(shè)置,則默認(rèn)為 'info'。serializers: 序列化函數(shù)的哈希。默認(rèn)情況下,序列化函數(shù)應(yīng)用在 req (來訪的請求對象)、res (發(fā)送的響應(yīng)對象) 以及 err (標(biāo)準(zhǔn)的 Error 對象) 之上。當(dāng)一個(gè)日志方法接收到含有上述任意屬性的對象時(shí),對應(yīng)的序列化器將會(huì)作用于該屬性。舉例如下: fastify.get('/foo', function (req, res) { req.log.info({req}) // 日志輸出經(jīng)過序列化的請求對象 res.send('foo') })用戶提供的序列化函數(shù)將會(huì)覆蓋對應(yīng)屬性默認(rèn)的序列化函數(shù)。
  • loggerInstance:自定義日志工具實(shí)例。日志工具必須實(shí)現(xiàn) Pino 的接口,即擁有如下方法:info, error, debug, fatal, warn, trace, child。例如:const pino = require('pino')();

const customLogger = { info: function (o, ...n) {}, warn: function (o, ...n) {}, error: function (o, ...n) {}, fatal: function (o, ...n) {}, trace: function (o, ...n) {}, debug: function (o, ...n) {}, child: function() { const child = Object.create(this); child.pino = pino.child(...arguments); return child; }, };

const fastify = require('fastify')({logger: customLogger});


<a name="factory-disable-request-logging"></a>
### `disableRequestLogging`
默認(rèn)情況下當(dāng)開啟日志時(shí),F(xiàn)astify 會(huì)在收到請求與發(fā)送該請求的響應(yīng)時(shí)記錄 `info` 級(jí)別的日志。你可以設(shè)置該選項(xiàng)為 `true` 來禁用該功能。這時(shí),通過自定義 `onRequest` 和 `onResponse` 鉤子,你能更靈活地記錄一個(gè)請求的開始與結(jié)束。

+ 默認(rèn)值:`false`

```js
// 例子:通過鉤子再造被禁用的請求日志功能。
fastify.addHook('onRequest', (req, reply, done) => {
  req.log.info({ url: req.req.url, id: req.id }, 'received request')
  done()
})

fastify.addHook('onResponse', (req, reply, done) => {
  req.log.info({ url: req.req.originalUrl, statusCode: res.res.statusCode }, 'request completed')
  done()
})

serverFactory

通過 serverFactory 選項(xiàng),你可以向 Fastify 傳遞一個(gè)自定義的 http server。serverFactory 函數(shù)的參數(shù)為 handler 函數(shù)及一個(gè)選項(xiàng)對象。handler 函數(shù)的參數(shù)為 request 和 response 對象,選項(xiàng)對象則與你傳遞給 Fastify 的一致。

const serverFactory = (handler, opts) => {
  const server = http.createServer((req, res) => {
    handler(req, res)
  })

  return server
}

const fastify = Fastify({ serverFactory, modifyCoreObjects: false })

fastify.get('/', (req, reply) => {
  reply.send({ hello: 'world' })
})

fastify.listen(3000)

Fastify 內(nèi)在地使用 Node 原生 http server 的 API。因此,如果你使用一個(gè)自定義的 server,你必須保證暴露了相同的 API。不這么做的話,你可以在 serverFactory 函數(shù)內(nèi)部 return 語句之前,向 server 實(shí)例添加新的屬性。要注意的是,我們也設(shè)置了 modifyCoreObjects: false。這是因?yàn)樵谥T如 Google Cloud Functions 等無服務(wù)器 (serverless) 環(huán)境下,一些 Node.js 核心的屬性是不可寫的。

caseSensitive

默認(rèn)值為 true,此時(shí)路由對大小寫敏感。這就意味著 /foo 與 /Foo 是兩個(gè)不同的路由。當(dāng)該選項(xiàng)為 false 時(shí),路由大小寫不敏感,/foo、/Foo 以及 /FOO 都是一樣的。

將 caseSensitive 設(shè)置為 false,會(huì)導(dǎo)致所有路徑變?yōu)樾?,除了路由參?shù)與通配符。

fastify.get('/user/:username', (request, reply) => {
  // 原 URL: /USER/NodeJS
  console.log(request.params.username) // -> 'NodeJS'
})

要注意的是,將該選項(xiàng)設(shè)為 false 與 RFC3986 相悖。

requestIdHeader

用來獲知請求 id 的 header 名。請看請求 id 一節(jié)。

  • 默認(rèn)值:'request-id'

requestIdLogLabel

定義日志中請求 id 的標(biāo)簽。

  • 默認(rèn)值:'reqId'

genReqId

用于生成請求 id 的函數(shù)。參數(shù)為來訪的請求對象。

  • 默認(rèn)值:'request-id' 的值 (當(dāng)存在該 header 時(shí)) 或單調(diào)遞增的整數(shù) 在分布式系統(tǒng)中,你可能會(huì)特別想覆蓋如下默認(rèn)的 id 生成行為。要生成 UUID,請看hyperid。let i = 0 const fastify = require('fastify')({ genReqId: function (req) { return i++ } })

注意:當(dāng)設(shè)置了 'request-id' header時(shí),genReqId 不會(huì) 被調(diào)用。

trustProxy

通過開啟 trustProxy 選項(xiàng),F(xiàn)astify 會(huì)認(rèn)為使用了代理服務(wù),且 X-Forwarded-* header 是可信的,否則該值被認(rèn)為是極具欺騙性的。

const fastify = Fastify({ trustProxy: true })
  • 默認(rèn)值:false
  • true/false: 信任所有代理 (true) 或不信任任意的代理 (false)。
  • string: 只信任給定的 IP/CIDR (例如 '127.0.0.1')??梢允且唤M用英文逗號(hào)分隔的地址 (例如 '127.0.0.1,192.168.1.1/24')。
  • Array<string>: 只信任給定的 IP/CIDR 列表 (例如 ['127.0.0.1'])。
  • number: 信任來自前置代理服務(wù)器的第n跳 (hop) 地址作為客戶端。
  • Function: 自定義的信任函數(shù),第一個(gè)參數(shù)為 address function myTrustFn(address, hop) { return address === '1.2.3.4' || hop === 1 }

更多示例詳見 proxy-addr

你還可以通過 request 對象獲取 ip、ips 與 hostname 的值。

fastify.get('/', (request, reply) => {
  console.log(request.ip)
  console.log(request.ips)
  console.log(request.hostname)
})

pluginTimeout

單個(gè)插件允許加載的最長時(shí)間,以毫秒計(jì)。如果某個(gè)插件加載超時(shí),則 ready 會(huì)拋出一個(gè)含有 'ERR_AVVIO_PLUGIN_TIMEOUT' 代碼的 Error 對象。

  • 默認(rèn)值:10000querystringParser

Fastify 默認(rèn)使用 Node.js 核心的 querystring 模塊作為 query string 解析器。你可以通過 querystringParser 選項(xiàng)來使用自定義的解析器,例如 qs

const qs = require('qs')
const fastify = require('fastify')({
  querystringParser: str => qs.parse(str)
})

versioning

默認(rèn)情況下,find-my-way 使用 semver 版本號(hào)規(guī)范來為路由設(shè)置版本號(hào)。你也可以使用自定義的版本號(hào)策略。更多信息請看 find-my-way 的文檔。

const versioning = {
  storage: function () {
    let versions = {}
    return {
      get: (version) => { return versions[version] || null },
      set: (version, store) => { versions[version] = store },
      del: (version) => { delete versions[version] },
      empty: () => { versions = {} }
    }
  },
  deriveVersion: (req, ctx) => {
    return req.headers['accept']
  }
}
 const fastify = require('fastify')({
  versioning
})

modifyCoreObjects

  • 默認(rèn)值:true

默認(rèn)情況下,F(xiàn)astify 會(huì)向 Node 原生的 request 對象添加 ip、ips、hostname 以及 log 屬性 (參見 Request),向原生的 response 對象添加 log 屬性。你可以將 modifyCoreObjects 設(shè)為 false 來避免上述行為。

const fastify = Fastify({ modifyCoreObjects: true }) // 默認(rèn)值

fastify.get('/', (request, reply) => {
  console.log(request.raw.ip)
  console.log(request.raw.ips)
  console.log(request.raw.hostname)
  request.raw.log('Hello')
  reply.res.log('World')
})

在諸如 Google Cloud Functions 等無服務(wù)器 (serverless) 環(huán)境下,禁用該選項(xiàng)是有用的。因?yàn)樵谶@些環(huán)境中,ip 及 ips 并不可寫。

請注意,我們不建議使用這些屬性。它們將會(huì)在 Fastify 的下個(gè)主要版本中,與該選項(xiàng)一起去除。作為替代,我們推薦使用 Fastify 的 Request 與 Reply 對象上相同的屬性。

const fastify = Fastify({ modifyCoreObjects: false })

fastify.get('/', (request, reply) => {
  console.log(request.ip)
  console.log(request.ips)
  console.log(request.hostname)
  request.log('Hello')
  reply.log('World')
})

return503OnClosing

調(diào)用 close 方法后返回 503 狀態(tài)碼。 如果為 false,服務(wù)器會(huì)正常處理請求。

  • 默認(rèn)值:true

ajv

配置 Fastify 使用的 ajv 實(shí)例。這使得你無需提供一個(gè)自定義的實(shí)例。

  • 默認(rèn)值:
{
  customOptions: {
    removeAdditional: true,
    useDefaults: true,
    coerceTypes: true,
    allErrors: true,
    nullable: true
  },
  plugins: []
}
const fastify = require('fastify')({
  ajv: {
    customOptions: {
      nullable: false // 參見 [ajv 的配置選項(xiàng)](https://ajv.js.org/#options)
    },
    plugins: [
      require('ajv-merge-patch')
      [require('ajv-keywords'), 'instanceof'];
      // 用法: [plugin, pluginOptions] - 插件與選項(xiàng)
      // 用法: plugin - 僅插件
    ]
  }
})

實(shí)例

服務(wù)器方法

服務(wù)器

fastify.server:由 Fastify 的工廠函數(shù) 生成的 Node 原生 server 對象。

after

當(dāng)前插件及在其中注冊的所有插件加載完畢后調(diào)用??傇?nbsp;fastify.ready 之前執(zhí)行。

fastify
  .register((instance, opts, done) => {
    console.log('當(dāng)前插件')
    done()
  })
  .after(err => {
    console.log('當(dāng)前插件之后')
  })
  .register((instance, opts, done) => {
    console.log('下一個(gè)插件')
    done()
  })
  .ready(err => {
    console.log('萬事俱備')
  })

ready

當(dāng)所有插件的加載都完成時(shí)調(diào)用。如有錯(cuò)誤發(fā)生,它會(huì)傳遞一個(gè) error 參數(shù)。

fastify.ready(err => {
  if (err) throw err
})

調(diào)用時(shí)不加參數(shù),它會(huì)返回一個(gè) Promise 對象:

fastify.ready().then(() => {
  console.log('successfully booted!')
}, (err) => {
  console.log('an error happened', err)
})

listen

所有的插件加載完畢、ready 事件觸發(fā)后,在指定的端口啟動(dòng)服務(wù)器。它的回調(diào)函數(shù)與 Node 原生方法的回調(diào)相同。默認(rèn)情況下,服務(wù)器監(jiān)聽 localhost 所決定的地址 (127.0.0.1 或 ::1,取決于操作系統(tǒng))。將地址設(shè)置為 0.0.0.0 可監(jiān)聽所有的 IPV4 地址。設(shè)置為 :: 則可監(jiān)聽所有的 IPV6 地址,在某些系統(tǒng)中,這么做亦可同時(shí)監(jiān)聽所有 IPV4 地址。監(jiān)聽所有的接口要格外謹(jǐn)慎,因?yàn)檫@種方式存在著固有的 安全風(fēng)險(xiǎn)。

fastify.listen(3000, (err, address) => {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
})

指定監(jiān)聽的地址:

fastify.listen(3000, '127.0.0.1', (err, address) => {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
})

指定積壓隊(duì)列 (backlog queue size) 的大小:

fastify.listen(3000, '127.0.0.1', 511, (err, address) => {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
})

沒有提供回調(diào)函數(shù)時(shí),它會(huì)返回一個(gè) Promise 對象:

fastify.listen(3000)
  .then((address) => console.log(`server listening on ${address}`))
  .catch(err => {
    console.log('Error starting server:', err)
    process.exit(1)
  })

你還可以在使用 Promise 的同時(shí)指定地址:

fastify.listen(3000, '127.0.0.1')
  .then((address) => console.log(`server listening on ${address}`))
  .catch(err => {
    console.log('Error starting server:', err)
    process.exit(1)
  })

當(dāng)部署在 Docker 或其它容器上時(shí),明智的做法是監(jiān)聽 0.0.0.0。因?yàn)槟J(rèn)情況下,這些容器并未將映射的端口暴露在 127.0.0.1:

fastify.listen(3000, '0.0.0.0', (err, address) => {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
})

假如未設(shè)置 port (或設(shè)為 0),則會(huì)自動(dòng)選擇一個(gè)隨機(jī)可用的端口 (之后可通過 fastify.server.address().port 獲知)。

route

將路由添加到服務(wù)器的方法,支持簡寫。請看這里。

close

fastify.close(callback):調(diào)用這個(gè)函數(shù)來關(guān)閉服務(wù)器實(shí)例,并觸發(fā) 'onClose' 鉤子。服務(wù)器會(huì)向所有新的請求發(fā)送 503 錯(cuò)誤,并銷毀它們。 要改變這一行為,請見 return503OnClosing

如果無參調(diào)用,它會(huì)返回一個(gè) Promise:

fastify.close().then(() => {
  console.log('successfully closed!')
}, (err) => {
  console.log('an error happened', err)
})

decorate*

向 Fastify 實(shí)例、響應(yīng)或請求添加裝飾器函數(shù)。參閱這里了解更多。

register

Fastify 允許用戶通過插件擴(kuò)展功能。插件可以是一組路由、裝飾器或其他。請看這里。

use

向 Fastify 添加中間件,請看這里

addHook

向 Fastify 添加特定的生命周期鉤子函數(shù),請看這里。

prefix

添加在路由前的完整路徑。

示例:

fastify.register(function (instance, opts, done) {
  instance.get('/foo', function (request, reply) {
    // 輸出:"prefix: /v1"
    request.log.info('prefix: %s', instance.prefix)
    reply.send({prefix: instance.prefix})
  })

  instance.register(function (instance, opts, done) {
    instance.get('/bar', function (request, reply) {
      // 輸出:"prefix: /v1/v2"
      request.log.info('prefix: %s', instance.prefix)
      reply.send({prefix: instance.prefix})
    })

    done()
  }, { prefix: '/v2' })

  done()
}, { prefix: '/v1' })

pluginName

當(dāng)前插件的名稱。有三種定義插件名稱的方式(按順序)。

  1. 如果插件使用 fastify-plugin,那么名稱為元數(shù)據(jù) (metadata) 中的 name。
  2. 如果插件通過 module.exports 導(dǎo)出,使用文件名。
  3. 如果插件通過常規(guī)的 函數(shù)定義,則使用函數(shù)名。

回退方案:插件函數(shù)的頭兩行將作為插件名,并使用 -- 替代換行符。這有助于在處理涉及許多插件的問題時(shí),找到根源。

重點(diǎn):如果你要處理一些通過 fastify-plugin 包裝的嵌套的異名插件,由于沒有生成新的定義域,因此不會(huì)去覆蓋上下文數(shù)據(jù),而是將各插件名加入一個(gè)數(shù)組。在這種情況下,會(huì)按涉及到的插件的啟動(dòng)順序,以 plugin-A -> plugin-B 的格式來展示插件名稱。

log

日志的實(shí)例,詳見這里。

inject

偽造 http 注入 (作為測試之用) 。請看更多內(nèi)容。

addSchema

fastify.addSchema(schemaObj),向 Fastify 實(shí)例添加可共用的 schema,用于驗(yàn)證數(shù)據(jù)。你可以通過該 schema 的 id 在應(yīng)用的任意位置使用它。請看驗(yàn)證和序列化一文中的范例

setReplySerializer

作用于未設(shè)置 Reply.serializer(func) 的所有路由的默認(rèn)序列化方法。這個(gè)處理函數(shù)是完全封裝的,因此,不同的插件允許有不同的錯(cuò)誤處理函數(shù)。 注:僅當(dāng)狀態(tài)碼為 2xx 時(shí)才被調(diào)用。關(guān)于錯(cuò)誤處理,請看 setErrorHandler。

fastify.setReplySerializer(function (payload, statusCode){
  // 使用同步函數(shù)序列化 payload
  return `my serialized ${statusCode} content: ${payload}`
})

setSchemaCompiler

為所有的路由設(shè)置 schema 編譯器 (schema compiler),請看這里了解更多信息。

setSchemaResolver

為所有的路由設(shè)置 schema $ref 解析器 (schema $ref resolver),請看這里了解更多信息。

schemaCompiler

setSchemaCompiler 方法的簡寫。用于設(shè)置 schema 編譯器函數(shù),也可用于返回全部路由的 schema 編譯器。

setNotFoundHandler

fastify.setNotFoundHandler(handler(request, reply)):為 404 狀態(tài) (not found) 設(shè)置處理函數(shù) (handler)。向 fastify.register() 傳遞不同的 prefix 選項(xiàng),就可以為不同的插件設(shè)置不同的處理函數(shù)。這些處理函數(shù)被視為常規(guī)的路由處理函數(shù),因此它們的請求會(huì)經(jīng)歷一個(gè)完整的  Fastify 生命周期。

你也可以為 404 處理函數(shù)注冊一個(gè) preValidation 或 preHandler 鉤子。

fastify.setNotFoundHandler({
  preValidation: (req, reply, done) => {
    // 你的代碼
    done()
  } ,
  preHandler: (req, reply, done) => {
    // 你的代碼
    done()
  }  
}, function (request, reply) {
    // 設(shè)置了 preValidation 與 preHandler 鉤子的默認(rèn) not found 處理函數(shù)
})

fastify.register(function (instance, options, done) {
  instance.setNotFoundHandler(function (request, reply) {
    // '/v1' 開頭的 URL 的 not found 處理函數(shù),
    // 未設(shè)置 preValidation 與 preHandler 鉤子
  })
  done()
}, { prefix: '/v1' })

setErrorHandler

fastify.setErrorHandler(handler(error, request, reply)):設(shè)置任意時(shí)刻的錯(cuò)誤處理函數(shù)。錯(cuò)誤處理函數(shù)是完全封裝 (fully encapsulated) 的,因此不同插件的處理函數(shù)可以不同。支持 async-await 語法。注:假如錯(cuò)誤的 statusCode 小于 400,在處理錯(cuò)誤前 Fastify 將會(huì)自動(dòng)將其設(shè)為 500。

fastify.setErrorHandler(function (error, request, reply) {
  // 記錄錯(cuò)誤
  // 發(fā)送錯(cuò)誤響應(yīng)
})

當(dāng)沒有設(shè)置錯(cuò)誤處理函數(shù)時(shí),F(xiàn)astify 會(huì)調(diào)用一個(gè)默認(rèn)函數(shù),并根據(jù) statusCode 相應(yīng)地記錄日志:

var statusCode = error.statusCode
if (statusCode >= 500) {
  log.error(error)
} else if (statusCode >= 400) {
  log.info(error)
} else {
  log.error(error)
}

printRoutes

fastify.printRoutes():打印路由的基數(shù)樹 (radix tree),可作調(diào)試之用。記得在 ready 函數(shù)的內(nèi)部或之后調(diào)用它。

fastify.get('/test', () => {})
fastify.get('/test/hello', () => {})
fastify.get('/hello/world', () => {})

fastify.ready(() => {
  console.log(fastify.printRoutes())
  // └── /
  //   ├── test (GET)
  //   │   └── /hello (GET)
  //   └── hello/world (GET)
})


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

掃描二維碼

下載編程獅App

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

編程獅公眾號(hào)