Fastify 服務器方法

2020-02-06 15:37 更新

工廠函數(shù)

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

http2

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

  • 默認值:false

https

用于配置服務器的 TLS socket 的對象。其選項與 Node.js 原生的 createServer 方法一致。 當值為 null 時,socket 連接將不會配置 TLS。

當 http2 選項設置時,https 選項也會被應用。

  • 默認值:null

ignoreTrailingSlash

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

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

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

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

maxParamLength

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

bodyLimit

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

  • 默認值:1048576 (1MiB)

onProtoPoisoning

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

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

  • 默認值:'error'

onConstructorPoisoning

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

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

  • 默認值:'ignore'

logger

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

屬性可用的值為:

  • 默認: false。禁用日志。所有記錄日志的方法將會指向一個空日志工具 abstract-logging 的實例。
  • pinoInstance: 一個已被實例化的 Pino 實例。內(nèi)建的日志工具將指向這個實例。
  • object: 標準的 Pino 選項對象。 它會被直接傳遞進 Pino 的構造函數(shù)。如果下列屬性未在該對象中定義,它們將被相應地添加:genReqId: 一個同步函數(shù),用于生成請求的標識符。默認生成按次序排列的標識符。level: 最低的日志級別。若未被設置,則默認為 'info'。serializers: 序列化函數(shù)的哈希。默認情況下,序列化函數(shù)應用在 req (來訪的請求對象)、res (發(fā)送的響應對象) 以及 err (標準的 Error 對象) 之上。當一個日志方法接收到含有上述任意屬性的對象時,對應的序列化器將會作用于該屬性。舉例如下: fastify.get('/foo', function (req, res) { req.log.info({req}) // 日志輸出經(jīng)過序列化的請求對象 res.send('foo') })用戶提供的序列化函數(shù)將會覆蓋對應屬性默認的序列化函數(shù)。
  • loggerInstance:自定義日志工具實例。日志工具必須實現(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`
默認情況下當開啟日志時,F(xiàn)astify 會在收到請求與發(fā)送該請求的響應時記錄 `info` 級別的日志。你可以設置該選項為 `true` 來禁用該功能。這時,通過自定義 `onRequest` 和 `onResponse` 鉤子,你能更靈活地記錄一個請求的開始與結束。

+ 默認值:`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 選項,你可以向 Fastify 傳遞一個自定義的 http server。serverFactory 函數(shù)的參數(shù)為 handler 函數(shù)及一個選項對象。handler 函數(shù)的參數(shù)為 request 和 response 對象,選項對象則與你傳遞給 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。因此,如果你使用一個自定義的 server,你必須保證暴露了相同的 API。不這么做的話,你可以在 serverFactory 函數(shù)內(nèi)部 return 語句之前,向 server 實例添加新的屬性。要注意的是,我們也設置了 modifyCoreObjects: false。這是因為在諸如 Google Cloud Functions 等無服務器 (serverless) 環(huán)境下,一些 Node.js 核心的屬性是不可寫的。

caseSensitive

默認值為 true,此時路由對大小寫敏感。這就意味著 /foo 與 /Foo 是兩個不同的路由。當該選項為 false 時,路由大小寫不敏感,/foo、/Foo 以及 /FOO 都是一樣的。

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

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

要注意的是,將該選項設為 false 與 RFC3986 相悖。

requestIdHeader

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

  • 默認值:'request-id'

requestIdLogLabel

定義日志中請求 id 的標簽。

  • 默認值:'reqId'

genReqId

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

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

注意:當設置了 'request-id' header時,genReqId 不會 被調(diào)用。

trustProxy

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

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

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

  • 默認值:10000querystringParser

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

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

versioning

默認情況下,find-my-way 使用 semver 版本號規(guī)范來為路由設置版本號。你也可以使用自定義的版本號策略。更多信息請看 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

  • 默認值:true

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

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

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 等無服務器 (serverless) 環(huán)境下,禁用該選項是有用的。因為在這些環(huán)境中,ip 及 ips 并不可寫。

請注意,我們不建議使用這些屬性。它們將會在 Fastify 的下個主要版本中,與該選項一起去除。作為替代,我們推薦使用 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,服務器會正常處理請求。

  • 默認值:true

ajv

配置 Fastify 使用的 ajv 實例。這使得你無需提供一個自定義的實例。

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

實例

服務器方法

服務器

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

after

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

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

ready

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

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

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

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

listen

所有的插件加載完畢、ready 事件觸發(fā)后,在指定的端口啟動服務器。它的回調(diào)函數(shù)與 Node 原生方法的回調(diào)相同。默認情況下,服務器監(jiān)聽 localhost 所決定的地址 (127.0.0.1 或 ::1,取決于操作系統(tǒng))。將地址設置為 0.0.0.0 可監(jiān)聽所有的 IPV4 地址。設置為 :: 則可監(jiān)聽所有的 IPV6 地址,在某些系統(tǒng)中,這么做亦可同時監(jiān)聽所有 IPV4 地址。監(jiā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)
  }
})

指定積壓隊列 (backlog queue size) 的大?。?/p>

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

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

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

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

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

當部署在 Docker 或其它容器上時,明智的做法是監(jiān)聽 0.0.0.0。因為默認情況下,這些容器并未將映射的端口暴露在 127.0.0.1:

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

假如未設置 port (或設為 0),則會自動選擇一個隨機可用的端口 (之后可通過 fastify.server.address().port 獲知)。

route

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

close

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

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

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

decorate*

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

register

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

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

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

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

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

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

log

日志的實例,詳見這里

inject

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

addSchema

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

setReplySerializer

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

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

setSchemaCompiler

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

setSchemaResolver

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

schemaCompiler

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

setNotFoundHandler

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

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

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

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

setErrorHandler

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

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

當沒有設置錯誤處理函數(shù)時,F(xiàn)astify 會調(diào)用一個默認函數(shù),并根據(jù) statusCode 相應地記錄日志:

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

公眾號
微信公眾號

編程獅公眾號