ContentTypeParser
Content-Type
解析器
Fastify 原生僅支援 'application/json'
和 'text/plain'
內容類型。如果內容類型不是其中之一,則會拋出 FST_ERR_CTP_INVALID_MEDIA_TYPE
錯誤。其他常見的內容類型則透過使用外掛來支援。
預設字元集為 utf-8
。如果您需要支援不同的內容類型,可以使用 addContentTypeParser
API。*預設的 JSON 和/或純文字解析器可以變更或移除。*
注意:如果您決定使用 Content-Type
標頭指定自己的內容類型,則 UTF-8 將不會是預設值。請務必像這樣包含 UTF-8:text/html; charset=utf-8
。
與其他 API 一樣,addContentTypeParser
會封裝在其宣告的範圍內。這表示如果您在根範圍中宣告它,它將在任何地方都可用,而如果您在外掛內宣告它,它將僅在該範圍及其子範圍中可用。
Fastify 會自動將解析後的請求有效負載新增至 Fastify 請求物件,您可以使用 request.body
來存取它。
請注意,對於 GET
和 HEAD
請求,永遠不會解析有效負載。對於 OPTIONS
和 DELETE
請求,只有在內容類型在 content-type 標頭中指定時才會解析有效負載。如果未指定,則不會執行萬用解析器,如同 POST
、PUT
和 PATCH
,但有效負載只是不會解析。
⚠ 安全注意事項
當使用 RegExp 來偵測
Content-Type
時,您應該注意如何正確偵測Content-Type
。例如,如果您需要application/*
,您應該使用/^application\/([\w-]+);?/
來僅比對本質 MIME 類型。
用法
fastify.addContentTypeParser('application/jsoff', function (request, payload, done) {
jsoffParser(payload, function (err, body) {
done(err, body)
})
})
// Handle multiple content types with the same function
fastify.addContentTypeParser(['text/xml', 'application/xml'], function (request, payload, done) {
xmlParser(payload, function (err, body) {
done(err, body)
})
})
// Async is also supported in Node versions >= 8.0.0
fastify.addContentTypeParser('application/jsoff', async function (request, payload) {
var res = await jsoffParserAsync(payload)
return res
})
// Handle all content types that matches RegExp
fastify.addContentTypeParser(/^image\/([\w-]+);?/, function (request, payload, done) {
imageParser(payload, function (err, body) {
done(err, body)
})
})
// Can use default JSON/Text parser for different content Types
fastify.addContentTypeParser('text/json', { parseAs: 'string' }, fastify.getDefaultJsonParser('ignore', 'ignore'))
Fastify 會先嘗試比對具有 string
值的內容類型解析器,然後再嘗試尋找符合的 RegExp
。如果您提供重疊的內容類型,Fastify 會嘗試從最後一個傳遞的內容類型開始,到第一個結束,來尋找符合的內容類型。因此,如果您想要更精確地指定一般的內容類型,請先指定一般的內容類型,然後再指定更具體的內容類型,如下例所示。
// Here only the second content type parser is called because its value also matches the first one
fastify.addContentTypeParser('application/vnd.custom+xml', (request, body, done) => {} )
fastify.addContentTypeParser('application/vnd.custom', (request, body, done) => {} )
// Here the desired behavior is achieved because fastify first tries to match the
// `application/vnd.custom+xml` content type parser
fastify.addContentTypeParser('application/vnd.custom', (request, body, done) => {} )
fastify.addContentTypeParser('application/vnd.custom+xml', (request, body, done) => {} )
將 addContentTypeParser 與 fastify.register 搭配使用
當將 addContentTypeParser
與 fastify.register
結合使用時,註冊路由時不應使用 await
。使用 await
會導致路由註冊變成非同步,並可能導致在設定 addContentTypeParser 之前就註冊了路由。
正確用法
const fastify = require('fastify')();
fastify.register((fastify, opts) => {
fastify.addContentTypeParser('application/json', function (request, payload, done) {
jsonParser(payload, function (err, body) {
done(err, body)
})
})
fastify.get('/hello', async (req, res) => {});
});
除了 addContentTypeParser
API 之外,還有其他 API 可以使用。這些是 hasContentTypeParser
、removeContentTypeParser
和 removeAllContentTypeParsers
。
hasContentTypeParser
您可以使用 hasContentTypeParser
API 來尋找是否已存在特定的內容類型解析器。
if (!fastify.hasContentTypeParser('application/jsoff')){
fastify.addContentTypeParser('application/jsoff', function (request, payload, done) {
jsoffParser(payload, function (err, body) {
done(err, body)
})
})
}
removeContentTypeParser
使用 removeContentTypeParser
可以移除單個或一組內容類型。該方法支援 string
和 RegExp
內容類型。
fastify.addContentTypeParser('text/xml', function (request, payload, done) {
xmlParser(payload, function (err, body) {
done(err, body)
})
})
// Removes the both built-in content type parsers so that only the content type parser for text/html is available
fastify.removeContentTypeParser(['application/json', 'text/plain'])
removeAllContentTypeParsers
在上面的範例中,可以注意到我們需要指定每個要移除的內容類型。為了要解決這個問題,Fastify 提供了 removeAllContentTypeParsers
API。此 API 可用於移除所有目前存在的內容類型解析器。在下面的範例中,我們實現了與上面範例相同的效果,但我們不需要指定每個要刪除的內容類型。與 removeContentTypeParser
一樣,此 API 支援封裝。如果您想要註冊一個萬用內容類型解析器,該解析器應該為每個內容類型執行,並且也應忽略內建的解析器,則此 API 特別有用。
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('text/xml', function (request, payload, done) {
xmlParser(payload, function (err, body) {
done(err, body)
})
})
注意:舊的解析器語法 function(req, done)
和 async function(req)
仍然支援,但它們已過時。
主體解析器
您可以使用兩種方式來解析請求的主體。第一種方式如上所示:您新增一個自訂內容類型解析器並處理請求串流。在第二種方式中,您應該將 parseAs
選項傳遞給 addContentTypeParser
API,您可以在其中宣告您想要如何取得主體。它可以是 'string'
或 'buffer'
類型。如果您使用 parseAs
選項,Fastify 將在內部處理串流並執行一些檢查,例如主體的最大大小和內容長度。如果超過限制,將不會叫用自訂解析器。
fastify.addContentTypeParser('application/json', { parseAs: 'string' }, function (req, body, done) {
try {
var json = JSON.parse(body)
done(null, json)
} catch (err) {
err.statusCode = 400
done(err, undefined)
}
})
請參閱 example/parser.js
以取得範例。
自訂解析器選項
parseAs
(字串):'string'
或'buffer'
,用於指定應如何收集傳入的資料。預設值:'buffer'
。bodyLimit
(數字):自訂解析器將接受的最大有效負載大小 (以位元組為單位)。預設為傳遞給Fastify 工廠函數
的全域主體限制。
萬用
在某些情況下,您需要捕獲所有請求,無論它們的內容類型為何。使用 Fastify,您可以使用 '*'
內容類型。
fastify.addContentTypeParser('*', function (request, payload, done) {
var data = ''
payload.on('data', chunk => { data += chunk })
payload.on('end', () => {
done(null, data)
})
})
使用此方法,所有沒有對應內容類型解析器的請求都將由指定的函數處理。
這也適用於傳輸請求串流。您可以定義如下的內容解析器
fastify.addContentTypeParser('*', function (request, payload, done) {
done()
})
然後直接存取核心 HTTP 請求以將其傳輸到您想要的位置
app.post('/hello', (request, reply) => {
reply.send(request.raw)
})
以下是一個完整的範例,記錄傳入的 json 行物件
const split2 = require('split2')
const pump = require('pump')
fastify.addContentTypeParser('*', (request, payload, done) => {
done(null, pump(payload, split2(JSON.parse)))
})
fastify.route({
method: 'POST',
url: '/api/log/jsons',
handler: (req, res) => {
req.body.on('data', d => console.log(d)) // log every incoming object
}
})
對於傳輸檔案上傳,您可能需要查看此外掛。
如果您希望內容類型解析器在所有內容類型上執行,而不僅是在沒有特定內容類型解析器的類型上執行,您應該先呼叫 removeAllContentTypeParsers
方法。
// Without this call, the request body with the content type application/json would be processed by the built-in JSON parser
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('*', function (request, payload, done) {
var data = ''
payload.on('data', chunk => { data += chunk })
payload.on('end', () => {
done(null, data)
})
})