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
请求,仅当内容类型在内容类型标头中给出时才会解析有效负载。如果未给出,则不会执行通配解析器,就像 POST
、PUT
和 PATCH
一样,但有效负载根本不会被解析。
⚠ 安全注意事项
在使用正则表达式检测
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。这可以用来删除所有当前存在的内容类型解析器。在下面的示例中,我们实现了与上面示例相同的目的,除了我们不需要指定要删除的每个内容类型。就像 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)
})
})