Skip to main content

Content-Type 解析器

Content-Type 解析器

¥Content-Type Parser

Fastify 原生支持 'application/json''text/plain' 内容类型,默认字符集为 utf-8。这些默认解析器可以更改或删除。

¥Fastify natively supports 'application/json' and 'text/plain' content types with a default charset of utf-8. These default parsers can be changed or removed.

不支持的内容类型将引发 FST_ERR_CTP_INVALID_MEDIA_TYPE 错误。

¥Unsupported content types will throw an FST_ERR_CTP_INVALID_MEDIA_TYPE error.

要支持其他内容类型,请使用 addContentTypeParser API 或现有的 plugin

¥To support other content types, use the addContentTypeParser API or an existing plugin.

与其他 API 一样,addContentTypeParser 封装在声明它的范围中。如果在根作用域中声明,则它在任何地方都可用;如果在插件中声明,则它仅在该范围及其子范围内可用。

¥As with other APIs, addContentTypeParser is encapsulated in the scope in which it is declared. If declared in the root scope, it is available everywhere; if declared in a plugin, it is available only in that scope and its children.

Fastify 会自动将解析后的请求负载添加到 Fastify 请求 对象,可通过 request.body 访问。

¥Fastify automatically adds the parsed request payload to the Fastify request object, accessible via request.body.

请注意,对于 GETHEAD 请求,有效负载永远不会被解析。对于 OPTIONSDELETE 请求,仅当提供有效的 content-type 标头时才会解析有效负载。与 POSTPUTPATCH 不同,catch-all 解析器不会被执行,并且负载不会被解析。

¥Note that for GET and HEAD requests, the payload is never parsed. For OPTIONS and DELETE requests, the payload is parsed only if a valid content-type header is provided. Unlike POST, PUT, and PATCH, the catch-all parser is not executed, and the payload is simply not parsed.

⚠ 警告:使用正则表达式检测 Content-Type 时,确保正确检测非常重要。例如,要匹配 application/*,请使用 /^application\/([\w-]+);?/ 仅匹配 本质 MIME 类型

¥⚠ Warning: When using regular expressions to detect Content-Type, it is important to ensure proper detection. For example, to match application/*, use /^application\/([\w-]+);?/ to match the essence MIME type only.

用法

¥Usage

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) {
const 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 first tries to match a content-type parser with a string value before trying to find a matching RegExp. For overlapping content types, it starts with the last one configured and ends with the first (last in, first out). To specify a general content type more precisely, first specify the general type, then the specific one, as shown below.

// 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

¥Using addContentTypeParser with fastify.register

当将 addContentTypeParserfastify.register 一起使用时,注册路由时请避免使用 await。使用 await 使路由注册异步,可能在设置 addContentTypeParser 之前注册路由。

¥When using addContentTypeParser with fastify.register, avoid await when registering routes. Using await makes route registration asynchronous, potentially registering routes before addContentTypeParser is set.

正确用法

¥Correct Usage

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) => {});
});

除了 addContentTypeParserhasContentTypeParserremoveContentTypeParserremoveAllContentTypeParsers API 也可用。

¥In addition to addContentTypeParser, the hasContentTypeParser, removeContentTypeParser, and removeAllContentTypeParsers APIs are available.

hasContentTypeParser

使用 hasContentTypeParser API 检查是否存在特定内容类型解析器。

¥Use the hasContentTypeParser API to check if a specific content type parser exists.

if (!fastify.hasContentTypeParser('application/jsoff')){
fastify.addContentTypeParser('application/jsoff', function (request, payload, done) {
jsoffParser(payload, function (err, body) {
done(err, body)
})
})
}

removeContentTypeParser

removeContentTypeParser 可以删除单个内容类型或内容类型数组,同时支持 stringRegExp

¥removeContentTypeParser can remove a single content type or an array of content types, supporting both string and 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

removeAllContentTypeParsers API 删除了所有现有的内容类型解析器,无需单独指定每个解析器。此 API 支持封装,可用于注册应该为每种内容类型执行的 包罗万象的内容类型解析器,忽略内置解析器。

¥The removeAllContentTypeParsers API removes all existing content type parsers eliminating the need to specify each one individually. This API supports encapsulation and is useful for registering a catch-all content type parser that should be executed for every content type, ignoring built-in parsers.

fastify.removeAllContentTypeParsers()

fastify.addContentTypeParser('text/xml', function (request, payload, done) {
xmlParser(payload, function (err, body) {
done(err, body)
})
})

🛈 注意:function(req, done)async function(req) 仍受支持,但已弃用。

¥🛈 Note: function(req, done) and async function(req) are still supported but deprecated.

正文解析器

¥Body Parser

请求主体可以通过两种方式解析。首先,添加自定义内容类型解析器并处理请求流。或者其次,使用 addContentTypeParser API 中的 parseAs 选项,指定 'string''buffer'。Fastify 将处理流,检查主体的 最大尺寸 和内容长度。如果超出限制,则不会调用自定义解析器。

¥The request body can be parsed in two ways. First, add a custom content type parser and handle the request stream. Or second, use the parseAs option in the addContentTypeParser API, specifying 'string' or 'buffer'. Fastify will handle the stream, check the maximum size of the body, and the content length. If the limit is exceeded, the custom parser will not be invoked.

fastify.addContentTypeParser('application/json', { parseAs: 'string' }, function (req, body, done) {
try {
const json = JSON.parse(body)
done(null, json)
} catch (err) {
err.statusCode = 400
done(err, undefined)
}
})

请参阅 example/parser.js 了解示例。

¥See example/parser.js for an example.

自定义解析器选项

¥Custom Parser Options

  • parseAs(字符串):'string''buffer' 指定应如何收集传入数据。默认:'buffer'

    ¥parseAs (string): 'string' or 'buffer' to designate how the incoming data should be collected. Default: 'buffer'.

  • bodyLimit(数字):自定义解析器将接受的最大有效负载大小(以字节为单位)。默认为传递给 Fastify factory function 的全局主体限制。

    ¥bodyLimit (number): The maximum payload size, in bytes, that the custom parser will accept. Defaults to the global body limit passed to the Fastify factory function.

Catch-All

要捕获所有请求(无论内容类型如何),请使用 '*' 内容类型:

¥To catch all requests regardless of content type, use the '*' content type:

fastify.addContentTypeParser('*', function (request, payload, done) {
let data = ''
payload.on('data', chunk => { data += chunk })
payload.on('end', () => {
done(null, data)
})
})

此函数将处理所有没有相应内容类型解析器的请求。

¥All requests without a corresponding content type parser will be handled by this function.

这对于管道请求流也很有用。定义内容解析器,例如:

¥This is also useful for piping the request stream. Define a content parser like:

fastify.addContentTypeParser('*', function (request, payload, done) {
done()
})

然后直接访问核心 HTTP 请求进行管道:

¥And then access the core HTTP request directly for piping:

app.post('/hello', (request, reply) => {
reply.send(request.raw)
})

这是一个记录传入 json 行 对象的完整示例:

¥Here is a complete example that logs incoming json line objects:

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

对于管道文件上传,请查看 @fastify/multipart

¥For piping file uploads, check out @fastify/multipart.

要对所有内容类型执行内容类型解析器,请先调用 removeAllContentTypeParsers

¥To execute the content type parser on all content types, call removeAllContentTypeParsers first.

// 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) {
const data = ''
payload.on('data', chunk => { data += chunk })
payload.on('end', () => {
done(null, data)
})
})