跳至主要內容
版本:最新版 (v5.0.x)

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 來存取它。

請注意,對於 GETHEAD 請求,永遠不會解析有效負載。對於 OPTIONSDELETE 請求,只有在內容類型在 content-type 標頭中指定時才會解析有效負載。如果未指定,則不會執行萬用解析器,如同 POSTPUTPATCH,但有效負載只是不會解析。

⚠ 安全注意事項

當使用 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 搭配使用

當將 addContentTypeParserfastify.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 可以使用。這些是 hasContentTypeParserremoveContentTypeParserremoveAllContentTypeParsers

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 可以移除單個或一組內容類型。該方法支援 stringRegExp 內容類型。

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