fastify.route(options)
示例:
fastify.route({
method: 'GET',
url: '/',
schema: {
querystring: {
name: { type: 'string' },
excitement: { type: 'integer' }
},
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
},
handler: function (request, reply) {
reply.send({ hello: 'world' })
}
})
上文的路由定義帶有 Hapi 的風格。要是偏好 Express/Restify 的寫法,F(xiàn)astify 也是支持的:fastify.get(path, [options], handler)fastify.head(path, [options], handler)fastify.post(path, [options], handler)fastify.put(path, [options], handler)fastify.delete(path, [options], handler)fastify.options(path, [options], handler)fastify.patch(path, [options], handler)
示例:
const opts = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
}
}
fastify.get('/', opts, (request, reply) => {
reply.send({ hello: 'world' })
})
fastify.all(path, [options], handler) 會給所有支持的 HTTP 方法添加相同的處理函數(shù)。
處理函數(shù)還可以寫到 options 對象里:
const opts = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
},
handler (request, reply) {
reply.send({ hello: 'world' })
}
}
fastify.get('/', opts)
注:假如同時在 options 和簡寫方法的第三個參數(shù)里指明了處理函數(shù),將會拋出重復的 handler 錯誤。
Fastify 同時支持靜態(tài)與動態(tài)的 url。要注冊一個參數(shù)命名的路徑,請在參數(shù)名前加上冒號。星號表示*通配符**。 *注意,靜態(tài)路由總是在參數(shù)路由和通配符之前進行匹配。
// 參數(shù)路由
fastify.get('/example/:userId', (request, reply) => {}))
fastify.get('/example/:userId/:secretToken', (request, reply) => {}))
// 通配符
fastify.get('/example/*', (request, reply) => {}))
正則表達式路由亦被支持。但要注意,正則表達式會嚴重拖累性能!
// 正則表達的參數(shù)路由
fastify.get('/example/:file(^\\d+).png', (request, reply) => {}))
你還可以在同一組斜杠 ("/") 里定義多個參數(shù)。就像這樣:
fastify.get('/example/near/:lat-:lng/radius/:r', (request, reply) => {}))
使用短橫線 ("-") 來分隔參數(shù)。
最后,同時使用多參數(shù)和正則表達式也是允許的。
fastify.get('/example/at/:hour(^\\d{2})h:minute(^\\d{2})m', (request, reply) => {}))
在這個例子里,任何未被正則匹配的符號均可作為參數(shù)的分隔符。
多參數(shù)的路由會影響性能,所以應該盡量使用單參數(shù),對于高頻訪問的路由來說更是如此。 如果你對路由的底層感興趣,可以查看find-my-way。
你是 async/await 的使用者嗎?我們?yōu)槟憧紤]了一切!
fastify.get('/', options, async function (request, reply) {
var data = await getData()
var processed = await processData(data)
return processed
})
如你所見,我們不再使用 reply.send 向用戶發(fā)送數(shù)據(jù),只需返回消息主體就可以了!
當然,需要的話你還是可以使用 reply.send 發(fā)送數(shù)據(jù)。
fastify.get('/', options, async function (request, reply) {
var data = await getData()
var processed = await processData(data)
reply.send(processed)
})
假如在路由中,reply.send() 脫離了 promise 鏈,在一個基于回調(diào)的 API 中被調(diào)用,你可以使用 await reply:
fastify.get('/', options, async function (request, reply) {
setImmediate(() => {
reply.send({ hello: 'world' })
})
await reply
})
返回回復也是可行的:
fastify.get('/', options, async function (request, reply) {
setImmediate(() => {
reply.send({ hello: 'world' })
})
return reply
})
警告:
假如你的處理函數(shù)是一個 async 函數(shù),或返回了一個 promise,請注意一種必須支持回調(diào)函數(shù)和 promise 控制流的特殊情況:如果 promise 被 resolve 為 undefined,請求會被掛起,并觸發(fā)一個錯誤日志。
通過這一方法,我們便可以最小代價同時支持 回調(diào)函數(shù)風格 以及 async-await。盡管這么做十分自由,我們還是強烈建議僅使用其中的一種,因為應用的錯誤處理方式應當保持一致。
注意:每個 async 函數(shù)各自返回一個 promise 對象。
有時你需要維護同一 api 的多個不同版本。一般的做法是在所有的路由之前加上版本號,例如 /v1/user。 Fastify 提供了一個快捷且智能的方法來解決上述問題,無需手動更改全部路由。這就是路由前綴。讓我們來看下吧:
// server.js
const fastify = require('fastify')()
fastify.register(require('./routes/v1/users'), { prefix: '/v1' })
fastify.register(require('./routes/v2/users'), { prefix: '/v2' })
fastify.listen(3000)
// routes/v1/users.js
module.exports = function (fastify, opts, done) {
fastify.get('/user', handler_v1)
done()
}
// routes/v2/users.js
module.exports = function (fastify, opts, done) {
fastify.get('/user', handler_v2)
done()
}
在編譯時 Fastify 自動處理了前綴,因此兩個不同路由使用相同的路徑名并不會產(chǎn)生問題。(這也意味著性能一點兒也不受影響!)。
現(xiàn)在,你的客戶端就可以訪問下列路由了:
根據(jù)需要,你可以多次設置路由前綴,它也支持嵌套的 register 以及路由參數(shù)。 請注意,當使用了 fastify-plugin 時,這一選項是無效的。
根據(jù)前綴是否以 / 結(jié)束,路徑為 / 的路由的匹配模式有所不同。舉例來說,前綴為 /something/ 的 / 路由只會匹配 something,而前綴為 /something 則會匹配 /something 和 /something/。
要改變這一行為,請見上文 prefixTrailingSlash 選項。
在 Fastify 中為路由里設置不同的日志級別是十分容易的。你只需在插件或路由的選項里設置 logLevel 為相應的值即可。
要注意的是,如果在插件層面上設置了 logLevel,那么 setNotFoundHandler 和 setErrorHandler 也會受到影響。
// server.js
const fastify = require('fastify')({ logger: true })
fastify.register(require('./routes/user'), { logLevel: 'warn' })
fastify.register(require('./routes/events'), { logLevel: 'debug' })
fastify.listen(3000)
你也可以直接將其傳給路由:
fastify.get('/', { logLevel: 'warn' }, (request, reply) => {
reply.send({ hello: 'world' })
})
自定義的日志級別僅對路由生效,通過 fastify.log 訪問的全局日志并不會受到影響。
在某些上下文里,你也許需要記錄一個大型對象,但這在其他路由中是個負擔。這時,你可以定義一些序列化器 (serializer),并將它們設置在正確的上下文之上!
const fastify = require('fastify')({ logger: true })
fastify.register(require('./routes/user'), {
logSerializers: {
user: (value) => `My serializer one - ${value.name}`
}
})
fastify.register(require('./routes/events'), {
logSerializers: {
user: (value) => `My serializer two - ${value.name} ${value.surname}`
}
})
fastify.listen(3000)
你可以通過上下文來繼承序列化器:
const fastify = Fastify({
logger: {
level: 'info',
serializers: {
user (req) {
return {
method: req.method,
url: req.url,
headers: req.headers,
hostname: req.hostname,
remoteAddress: req.ip,
remotePort: req.connection.remotePort
}
}
}
}
})
fastify.register(context1, {
logSerializers: {
user: value => `My serializer father - ${value}`
}
})
async function context1 (fastify, opts) {
fastify.get('/', (req, reply) => {
req.log.info({ user: 'call father serializer', key: 'another key' })
// 打印結(jié)果: { user: 'My serializer father - call father serializer', key: 'another key' }
reply.send({})
})
}
fastify.listen(3000)
注冊一個新的處理函數(shù),你可以向其傳遞一個配置對象,并在其中使用它。
// server.js
const fastify = require('fastify')()
function handler (req, reply) {
reply.send(reply.context.config.output)
}
fastify.get('/en', { config: { output: 'hello world!' } }, handler)
fastify.get('/it', { config: { output: 'ciao mondo!' } }, handler)
fastify.listen(3000)
需要的話,你可以提供一個版本選項,它允許你為同一個路由聲明不同的版本。版本號請遵循 semver 規(guī)范。Fastify 會自動檢測 Accept-Version header,并將請求分配給相應的路由 (當前尚不支持 semver 規(guī)范中的 advanced ranges 與 pre-releases 語法)。請注意,這一特性會降低路由的性能。
fastify.route({
method: 'GET',
url: '/',
version: '1.2.0',
handler: function (request, reply) {
reply.send({ hello: 'world' })
}
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'Accept-Version': '1.x' // 也可以是 '1.2.0' 或 '1.2.x'
}
}, (err, res) => {
// { hello: 'world' }
})
如果你聲明了多個擁有相同主版本或次版本號的版本,F(xiàn)astify 總是會根據(jù) Accept-Version header 的值選擇最兼容的版本。假如請求未帶有 Accept-Version header,那么將返回一個 404 錯誤。
新建實例時,可以通過設置 versioning 來自定義版本號邏輯。
更多建議: