Content-Type
解析器
Content-Type
解析器
¥Content-Type
Parser
Fastify 本身仅支持 'application/json'
和 'text/plain'
内容类型。如果内容类型不是其中之一,则会抛出 FST_ERR_CTP_INVALID_MEDIA_TYPE
错误。通过使用 plugins 支持其他常见内容类型。
¥Natively, Fastify only supports 'application/json'
and 'text/plain'
content
types. If the content type is not one of these, an
FST_ERR_CTP_INVALID_MEDIA_TYPE
error will be thrown.
Other common content types are supported through the use of
plugins.
默认字符集为 utf-8
。如果你需要支持不同的内容类型,可以使用 addContentTypeParser
API。可以更改或删除默认的 JSON 和/或纯文本解析器。
¥The default charset is utf-8
. If you need to support different content types,
you can use the addContentTypeParser
API. The default JSON and/or plain text
parser can be changed or removed.
注意:如果你决定使用 Content-Type
标头指定自己的内容类型,则 UTF-8 将不是默认值。请务必包含 UTF-8,例如 text/html; charset=utf-8
。
¥Note: If you decide to specify your own content type with the Content-Type
header, UTF-8 will not be the default. Be sure to include UTF-8 like this
text/html; charset=utf-8
.
与其他 API 一样,addContentTypeParser
被封装在声明它的范围中。这意味着,如果你在根作用域中声明它,它将在任何地方都可用,而如果你在插件内声明它,它将仅在该作用域及其子作用域中可用。
¥As with the other APIs, addContentTypeParser
is encapsulated in the scope in
which it is declared. This means that if you declare it in the root scope it
will be available everywhere, while if you declare it inside a plugin it will be
available only in that scope and its children.
Fastify 会自动将解析后的请求负载添加到 Fastify 请求 对象中,你可以使用 request.body
访问该对象。
¥Fastify automatically adds the parsed request payload to the Fastify
request object which you can access with request.body
.
请注意,对于 GET
和 HEAD
请求,有效负载永远不会被解析。对于 OPTIONS
和 DELETE
请求,仅当在 content-type 标头中给出内容类型时,才会解析有效负载。如果没有给出,则不会像 POST
、PUT
和 PATCH
那样执行 catch-all 解析器,但有效负载不会被解析。
¥Note that for GET
and HEAD
requests the payload is never parsed. For
OPTIONS
and DELETE
requests the payload is only parsed if the content type
is given in the content-type header. If it is not given, the
catch-all parser is not executed as with POST
, PUT
and
PATCH
, but the payload is simply not parsed.
⚠ 安全须知
¥⚠ Security Notice
当使用 RegExp 检测
Content-Type
时,你应该注意如何正确检测Content-Type
。例如,如果你需要application/*
,则应使用/^application\/([\w-]+);?/
来仅匹配 本质 MIME 类型。¥When using with RegExp to detect
Content-Type
, you should beware of how to properly detect theContent-Type
. For example, if you needapplication/*
, you should 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 会尝试从最后一个传递的内容类型开始并以第一个内容类型结束来查找匹配的内容类型。因此,如果你想更精确地指定一般内容类型,请首先指定一般内容类型,然后指定更具体的内容类型,如下例所示。
¥Fastify first tries to match a content-type parser with a string
value before
trying to find a matching RegExp
. If you provide overlapping content types,
Fastify tries to find a matching content type by starting with the last one
passed and ending with the first one. So if you want to specify a general
content type more precisely, first specify the general content type and then the
more specific one, like in the example 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
当将 addContentTypeParser
与 fastify.register
结合使用时,注册路由时不应使用 await
。使用 await
会导致路由注册异步,并可能导致在设置 addContentTypeParser 之前注册路由。
¥When using addContentTypeParser
in combination with fastify.register
,
await
should not be used when registering routes. Using await
causes
the route registration to be asynchronous and can lead to routes being registered
before the addContentTypeParser has been 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) => {});
});
除了 addContentTypeParser
API 之外,还有其他 API 可以使用。这些是 hasContentTypeParser
、removeContentTypeParser
和 removeAllContentTypeParsers
。
¥Besides the addContentTypeParser
API there are further APIs that can be used.
These are hasContentTypeParser
, removeContentTypeParser
and
removeAllContentTypeParsers
.
hasContentTypeParser
你可以使用 hasContentTypeParser
API 来查找特定内容类型解析器是否已存在。
¥You can use the hasContentTypeParser
API to find if a specific content type
parser already exists.
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
内容类型。
¥With removeContentTypeParser
a single or an array of content types can be
removed. The method supports string
and RegExp
content types.
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 特别有用。
¥In the example from just above, it is noticeable that we need to specify each
content type that we want to remove. To solve this problem Fastify provides the
removeAllContentTypeParsers
API. This can be used to remove all currently
existing content type parsers. In the example below we achieve the same as in
the example above except that we do not need to specify each content type to
delete. Just like removeContentTypeParser
, this API supports encapsulation.
The API is especially useful if you want to register a catch-all content type
parser that should be executed for every content type and the
built-in parsers should be ignored as well.
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('text/xml', function (request, payload, done) {
xmlParser(payload, function (err, body) {
done(err, body)
})
})
注意:解析器的旧语法 function(req, done)
和 async function(req)
仍然受支持,但它们已被弃用。
¥Notice: The old syntaxes function(req, done)
and async function(req)
for
the parser are still supported but they are deprecated.
正文解析器
¥Body Parser
你可以通过两种方式解析请求正文。第一个如上所示:你添加自定义内容类型解析器并处理请求流。在第二个例子中,你应该将 parseAs
选项传递给 addContentTypeParser
API,在那里声明你想要如何获取主体。它可以是 'string'
或 'buffer'
类型。如果你使用 parseAs
选项,Fastify 将在内部处理流并执行一些检查,例如主体的 最大尺寸 和内容长度。如果超出限制,将不会调用自定义解析器。
¥You can parse the body of a request in two ways. The first one is shown above:
you add a custom content type parser and handle the request stream. In the
second one, you should pass a parseAs
option to the addContentTypeParser
API, where you declare how you want to get the body. It could be of type
'string'
or 'buffer'
. If you use the parseAs
option, Fastify will
internally handle the stream and perform some checks, such as 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): Either'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 theFastify factory function
.
Catch-All
在某些情况下,你需要捕获所有请求,无论其内容类型如何。使用 Fastify,你只需使用 '*'
内容类型即可。
¥There are some cases where you need to catch all requests regardless of their
content type. With Fastify, you can just use the '*'
content type.
fastify.addContentTypeParser('*', function (request, payload, done) {
let data = ''
payload.on('data', chunk => { data += chunk })
payload.on('end', () => {
done(null, data)
})
})
使用此功能,所有没有相应内容类型解析器的请求将由指定的函数处理。
¥Using this, all requests that do not have a corresponding content type parser will be handled by the specified function.
这对于管道请求流也很有用。你可以定义一个内容解析器,例如:
¥This is also useful for piping the request stream. You can define a content parser like:
fastify.addContentTypeParser('*', function (request, payload, done) {
done()
})
然后直接访问核心 HTTP 请求,将其传送到你想要的位置:
¥and then access the core HTTP request directly for piping it where you want:
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
}
})
对于管道文件上传,你可能需要查看 这个插件。
¥For piping file uploads you may want to check out this plugin.
如果你希望内容类型解析器在所有内容类型上执行,而不仅仅是在没有特定内容类型的内容类型上执行,你应该首先调用 removeAllContentTypeParsers
方法。
¥If you want the content type parser to be executed on all content types and not
only on those that don't have a specific one, you should call the
removeAllContentTypeParsers
method 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)
})
})