Fastify 系列教程:
- Fastify 系列教程一 (路由和日志)
- Fastify 系列教程二 (中間件、鈎子函數和裝飾器)
- Fastify 系列教程三 (驗證、序列化和生命周期)
- Fastify 系列教程四 (請求對象、響應對象和插件)
響應對象
路由的第二個參數即是響應對象 reply ,它有如下幾個方法:
.code(statusCode)- 設置狀態碼。.header(name, value)- 設置響應頭。.type(value)- 設置Content-Type字段。.redirect([code,] url)- 重定向到特定的地址,code參數是可選的 (默認為302)。.serializer(function)- 為有效荷載設置自定義的序列化器。.send(payload)- 向客戶端發送荷載,可以是文本、JSON、流或者一個錯誤對象。.sent- 一個布爾值,用來判斷send函數是否已經被調用。
示例:
fastify.get('/', options, function (request, reply) {
// Your code
reply
.code(200)
.header('Content-Type', 'application/json')
.send({ hello: 'world' })
})
Code
如果沒有設置 reply.code,默認 statusCode 是 200。
Header設置響應頭
設置自定義的響應頭。
如果沒有設置 'Content-Type' 字段,Fastify 猜測你會使用 'application/json', 除非你要發送一個流, 在這種情況下,Fastify會識別並設置 'Content-Type' 為 'application/octet-stream'。
注意,如果使用的序列化器不是序列化到 JSON,則必須設置自定義的 Content-Type 頭。
Redirect
重定向一個請求到特定的地址,狀態碼是可選的,默認為 302。
reply.redirect('/home')
Type
設置響應頭的 Content-Type字段。
這是 reply.header('Content-Type', 'the/type') 的簡寫。
reply.type('text/html')
Serializer
Fastify 作為一個完整的JSON兼容服務器而誕生,所以可以直接序列化 send() 函數中的有效荷載。如果你設置了輸出 schema,那么會使用 fast-json-stringify 作為格式化JSON的工具包,否則使用 fast-safe-stringify。
如果想使用自定義的序列化器,比如 msgpack5 或者 protobuf,你可以使用 .serializer() ,注意:如果使用自定義的序列化器卻不是序列化成JSON,那么必須得設置自定義的 'Content-Type' 頭。
reply
.header('Content-Type', 'application/x-protobuf')
.serializer(protoBuf.serialize)
Promises
發送原生的 promises 和支持開箱即用的 async-await。
fastify.get('/promises', options, function (request, reply) {
const promise = new Promise(function(){
setTimeout(function(resolve, reject){
resolve({hello: 'promise'})
}, 1000)
})
reply
.code(200)
.send(promise)
})
// 上述代碼將會在服務端延遲 1s 后成功響應 {hello: 'promise'}
fastify.get('/async-await', options, async function (request, reply) {
var res = await new Promise(function (resolve) {
setTimeout(resolve, 200, { hello: 'world' })
})
return res
})
Streams
也可以發送流,內部使用 pump 來避免文件描述符的泄露。如果你要發送流,並且尚未設置 “Content-Type” 頭,則 send 將會以“application/octet-stream” 進行設置。
fastify.get('/streams', function (request, reply) {
const fs = require('fs')
const stream = fs.createReadStream('some-file', 'utf8')
reply.send(stream)
})
Errors
如果你向 send 函數傳入一個 Error 對象的實例,Fastify 將自動創建一個結構如下的錯誤
{
error: String // the http error message
message: String // the user error message
statusCode: Number // the http status code
}
fastify.get('/error', function(request. reply){
reply.send(new Error('error request'))
})
如果你想要擴展錯誤對象,可以使用 extendServerError
如果你傳入錯誤對象並且設置小於 400 的狀態碼,Fastify 將會自動的設置成 500。
請求對象
路由的第一個參數即是請求對象 request,它有如下幾個屬性:
query- 解析后的查詢字符串body- 請求體params- 匹配url的參數,例如: 用/page/5匹配/page/:id=> {id: 5}headers- 請求頭對象req- 原生Node的請求對象log- 日志記錄器的實例
fastify.post('/page/:id', options, function (request, reply) {
console.log(request.body)
console.log(request.query)
console.log(request.params)
console.log(request.headers)
console.log(request.req)
request.log.info('some info')
})
插件
Fastify 允許用戶使用插件擴展其功能。
一個插件可以是一組路由,一個裝飾器等,可以使用 register 函數注冊插件。
默認情況下,register 創建了一個新的作用域,這意味着如果對 Fastify 實例(通過 decorate)進行一些更改,則此更改不會反映到當前上下文的祖先中,而只會反映到其子節點。這個特性允許我們實現插件封裝和繼承,這樣就創建了一個直接非循環圖(DAG),就不會有交叉依賴關系引起的問題。
創建一個插件
創建插件非常簡單,你只要創建一個包含三個參數的方法。第一個是 fastify 實例,第二個是一個配置對象,最后一個是 next 回調。
// plugin.js
module.exports = function (fastify, opts, next) {
fastify.decorate('utility', () => {})
fastify.get('/', handler)
next()
}
使用插件:
var plugin = require('./plugin')
var fastify = require('fastify')()
fastify.register(plugin)
注意,插件是有作用域的:
fastify.register((instance, opts, next) => {
instance.decorate('util', (a, b) => a + b)
console.log(instance.util('that is ', ' awesome'))
next()
})
fastify.register((instance, opts, next) => {
console.log(instance.util('that is ', ' awesome')) // 拋出異常
next()
})
在第二個 register 函數中 instance.util 會拋出異常,因為 util 只存在於第一個 register 函數的上下文中。
注意:封裝只適用於祖先和兄弟作用域,不適用於子作用域。
fastify.register((instance, opts, next) => {
instance.decorate('util', (a, b) => a + b)
console.log(instance.util('that is ', ' awesome'))
fastify.register((instance, opts, next) => {
console.log(instance.util('that is ', ' awesome')) // 不會拋出異常
next()
})
next()
})
fastify.register((instance, opts, next) => {
console.log(instance.util('that is ', ' awesome')) // 會拋出異常
next()
})
如果希望定義的方法在應用的任何地方都可以用,沒有作用域的影響,則可以在根作用域中定義。或者使用 fastify-plugin 來包裝插件。
使用 fastify-plugin 模塊,可以傳遞一個表示 Fastify 的版本范圍的參數作為插件將支持的 Fastify 版本。
const fp = require('fastify-plugin')
const plugin = fp(function (fastify, opts, next) {
fastify.decorate('utility', (a, b) => {
return a + b
})
next()
}, ">=0.30.2")
fastify.register(plugin)
fastify.register(function(fastify, opts, next){
console.log(fastify.utility(1, 3)) // 4
next()
})
在插件中使用路由鈎子
只在某些特定的路由中執行路由鈎子,可以這樣做:
fastify.register((instance, opts, next) => {
instance.decorate('util', (req, key, value) => { req.key = value })
instance.addHook('preHandler', (req, reply, done) => {
instance.util(req, 'timestamp', new Date())
done()
})
instance.get('/plugin1', (req, reply) => {
reply.send(req)
})
next()
})
fastify.get('/plugin2', (req, reply) => {
reply.send(req)
})
/plugin2 將不會應用 preHandler 鈎子。
常用插件
point-of-view 模板引擎
安裝
npm install point-of-view --save
使用方法
fastify.register(require('point-of-view'), {
engine: {
ejs: require('ejs')
},
templates: './views' // 模板存放的目錄
})
fastify.get('/', (req, reply) => {
reply.view('index.ejs', { text: 'text' })
})
fastify-cookie Cookie
安裝
npm install fastify-cookie --save
使用方法
const fastify = require('fastify')()
fastify.register(require('fastify-cookie'), (err) => {
if (err) throw err
})
fastify.get('/', (req, reply) => {
const cookieFoo = req.cookies.foo // 得到發送過來的cookie中的foo字段
reply
.setCookie('foo', 'foo', { // 設置cookie
domain: 'example.com',
path: '/'
})
.send({hello: 'world'})
})
fastify-formbody 支持 application/x-www-form-urlencoded 內容類型
安裝
npm install fastify-formbody --save
使用方式
const fastify = require('fastify')()
fastify.register(require('fastify-formbody'), {}, (err) => {
if (err) throw err
})
fastify.post('/', (req, reply) => {
reply.send(req.body)
})
fastify.listen(8000, (err) => {
if (err) throw err
})
更多插件可以訪問https://www.fastify.io/ecosystem/
Fastify 系列教程到此結束。
Tips:訪問 https://lavyun.gitbooks.io/fastify/content/ 查看我翻譯的 Fastify 中文文檔。
