Skip to Content

Content-Type 解析器

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

不支持的内容类型将抛出 FST_ERR_CTP_INVALID_MEDIA_TYPE 错误。

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

与其他 API 一样,addContentTypeParser 在其声明的作用域内被封装。如果在根作用域中声明,则在整个应用范围内可用;如果在一个插件中声明,则仅在此插件及其子插件中可用。

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

请注意,对于 GETHEAD 请求,永远不会解析负载。对于 OPTIONSDELETE 请求,只有在提供有效的 content-type 头的情况下才会解析负载。与 POST, PUTPATCH 不同,通配符 解析器不会执行,并且负载将不被解析。

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

原始正文中的锚点链接:

## `Content-Type` Parser <a id="catch-all"></a>

翻译后的正文中的锚点链接:

## `Content-Type` 解析器 <a id="通配符"></a>

使用方法

fastify.addContentTypeParser('application/jsoff', function (request, payload, done) { jsoffParser(payload, function (err, body) { done(err, body) }) }) // 处理多个相同函数的内容类型 fastify.addContentTypeParser(['text/xml', 'application/xml'], function (request, payload, done) { xmlParser(payload, function (err, body) { done(err, body) }) }) // 在 Node 版本 >= 8.0.0 中也支持异步函数 fastify.addContentTypeParser('application/jsoff', async function (request, payload) { const res = await jsoffParserAsync(payload) return res }) // 处理匹配正则表达式的所有内容类型 fastify.addContentTypeParser(/^image\/([\w-]+);?/, function (request, payload, done) { imageParser(payload, function (err, body) { done(err, body) }) }) // 可以为不同的内容类型使用默认的 JSON/Text 解析器 fastify.addContentTypeParser('text/json', { parseAs: 'string' }, fastify.getDefaultJsonParser('ignore', 'ignore'))

Fastify 首先尝试用 string 值匹配内容类型解析器,如果未找到,则继续查找匹配的 RegExp。对于重叠的内容类型,它从最后一个配置的开始,并以第一个结束(后进先出)。 为了更精确地指定通用内容类型,请首先指定一般类型,然后指定具体类型,如下所示。

// 在这里只有第二个内容类型解析器被调用,因为它的值也匹配第一个 fastify.addContentTypeParser('application/vnd.custom+xml', (request, body, done) => {} ) fastify.addContentTypeParser('application/vnd.custom', (request, body, done) => {} ) // 在这里实现了期望的行为,因为 Fastify 首先尝试匹配 `application/vnd.custom+xml` 内容类型解析器 fastify.addContentTypeParser('application/vnd.custom', (request, body, done) => {} ) fastify.addContentTypeParser('application/vnd.custom+xml', (request, body, done) => {} )

使用 fastify.register 时的 addContentTypeParser

在使用 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 可用: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) }) }) // 移除所有内置的内容类型解析器,使得只有 text/html 的内容类型解析器可用 fastify.removeContentTypeParser(['application/json', 'text/plain'])

removeAllContentTypeParsers

removeAllContentTypeParsers 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) 仍然受支持但已弃用。

请求体解析

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

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 获取示例。

自定义解析器选项
  • parseAs(字符串):指定传入数据应如何收集,可以是 'string''buffer'。默认值为 'buffer'
  • bodyLimit(数字):自定义解析器接受的最大负载大小(以字节为单位)。默认值为传递给Fastify 工厂函数的全局体限制。
// 示例代码保持不变,无需翻译

捕获所有请求

为了捕获所有类型的请求,可以使用 '*' 内容类型:

fastify.addContentTypeParser('*', function (request, payload, done) { let 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)) // 记录每个传入的对象 } })

对于管道传输文件上传,请参阅 @fastify/multipart

要在所有内容类型上执行内容类型解析器,首先调用 removeAllContentTypeParsers

// 不进行此调用的话,带有 application/json 内容类型的请求体将由内置的 JSON 解析器处理 fastify.removeAllContentTypeParsers() fastify.addContentTypeParser('*', function (request, payload, done) { const data = '' payload.on('data', chunk => { data += chunk }) payload.on('end', () => { done(null, data) }) })