Body Parser
本指南介绍 AdonisJS 中的 body parser 配置。你将学习如何:
- 为不同的内容类型(JSON、表单数据、multipart)配置解析器
- 设置全局解析选项,如空字符串转换和空格修剪
- 调整文件上传限制和请求大小限制
- 控制特定路由的自动文件处理
- 使用 raw 解析器处理自定义内容类型
概述
body parser 负责在请求体到达你的路由处理器之前解析传入的请求体。它会自动检测每个请求的内容类型,并应用适当的解析器将原始请求数据转换为可用的格式。
AdonisJS 包含三个内置解析器:JSON 解析器 处理 JSON 编码的数据,form 解析器 处理 URL 编码的表单提交,multipart 解析器 处理文件上传和 multipart 表单数据。每个解析器都可以通过 config/bodyparser.ts 文件独立配置。
你不会在应用代码中直接与 body parser 交互。相反,你通过 Request 类使用诸如 request.all()、request.body() 或 request.file() 等方法来访问解析后的数据。body parser 作为中间件运行,在你的路由处理器执行之前自动处理请求体。
另请参阅: Request 类文档 了解如何访问解析后的请求数据。
配置
body parser 在 config/bodyparser.ts 文件中配置。该配置文件在你创建新的 AdonisJS 应用时会自动创建。
import { defineConfig } from '@adonisjs/core/bodyparser'
const bodyParserConfig = defineConfig({
allowedMethods: ['POST', 'PUT', 'PATCH', 'DELETE'],
form: {
convertEmptyStringsToNull: true,
trimWhitespaces: true,
types: ['application/x-www-form-urlencoded'],
},
json: {
convertEmptyStringsToNull: true,
trimWhitespaces: true,
types: [
'application/json',
'application/json-patch+json',
'application/vnd.api+json',
'application/csp-report',
],
},
multipart: {
autoProcess: true,
convertEmptyStringsToNull: true,
trimWhitespaces: true,
processManually: [],
limit: '20mb',
types: ['multipart/form-data'],
},
})
export default bodyParserConfig
allowedMethods
allowedMethods 数组定义了哪些 HTTP 方法应该解析其请求体。默认情况下,只处理 POST、PUT、PATCH 和 DELETE 请求。GET 请求被排除在外,因为它们通常不包含请求体。
全局解析选项
所有解析器都可用两个全局选项:convertEmptyStringsToNull 和 trimWhitespaces。这些选项有助于在请求数据到达你的应用逻辑之前对其进行规范化。
convertEmptyStringsToNull
convertEmptyStringsToNull 选项将请求体中的所有空字符串转换为 null 值。这个选项解决了 HTML 表单的一个常见问题。
当 HTML 表单输入字段没有值时,浏览器会在请求体中发送一个空字符串,而不是完全省略该字段。这种行为给数据库规范化带来了挑战,尤其是可空列。
考虑一个带有可选 "country" 字段的用户注册表单。你的数据库有一个可空的 country 列,并且你希望在用户未选择国家时存储 null。然而,HTML 表单发送的是一个空字符串,这意味着你会向数据库中插入一个空字符串,而不是让该列保持 null。
启用 convertEmptyStringsToNull 会自动处理这种不一致。body parser 会在你的验证或数据库逻辑运行之前将所有空字符串转换为 null。
json: {
convertEmptyStringsToNull: true,
}
trimWhitespaces
trimWhitespaces 选项会移除请求体所有字符串值首尾的空白字符。这有助于消除用户在提交表单时可能附带的多余空白。
无需在控制器或验证器中手动修剪值,你可以启用这个选项,让 body parser 在全局范围内处理空白移除。
form: {
trimWhitespaces: true,
}
JSON 解析器
JSON 解析器处理带有 JSON 编码请求体的请求。默认情况下,它会处理多种内容类型,包括 application/json、application/json-patch+json、application/vnd.api+json 和 application/csp-report。
encoding
encoding 选项指定将请求体 Buffer 转换为字符串时使用的字符编码。默认是 utf-8,可处理大多数用例。你可以使用
iconv-lite
包支持的任何编码。
json: {
encoding: 'utf-8',
}
limit
limit 选项设置解析器接受的请求体数据的最大大小。超过此限制的请求将收到 413 Payload Too Large 错误响应。
json: {
limit: '1mb',
}
strict
strict 选项控制解析器是否只接受对象和数组作为顶层的 JSON 值。启用后,解析器会拒绝根层级的原始值,如字符串、数字或布尔值。
json: {
strict: true,
}
types
types 数组定义了 JSON 解析器应处理哪些内容类型。如果你的应用接收到带有非标准内容类型请求头的 JSON 数据,可以添加自定义内容类型。
json: {
types: [
'application/json',
'application/json-patch+json',
'application/vnd.api+json',
'application/csp-report',
'application/custom+json',
]
}
Form 解析器
form 解析器处理 URL 编码的表单数据,通常来自 application/x-www-form-urlencoded 内容类型的 HTML 表单。
encoding
encoding 选项指定将请求体 Buffer 转换为字符串时使用的字符编码。默认是 utf-8,可处理大多数用例。你可以使用
iconv-lite
包支持的任何编码。
form: {
encoding: 'utf-8',
}
limit
limit 选项设置解析器接受的请求体数据的最大大小。超过此限制的请求将收到 413 Payload Too Large 错误响应。
form: {
limit: '1mb',
}
queryString
queryString 选项允许你配置 URL 编码字符串如何解析为对象。这些选项会直接传递给负责解析的
qs
包。
form: {
queryString: {
depth: 5,
parameterLimit: 1000,
},
}
另请参阅: qs 文档 了解所有可用选项。
types
types 数组定义了 form 解析器应处理哪些内容类型。默认情况下,它处理 application/x-www-form-urlencoded 请求。
form: {
types: ['application/x-www-form-urlencoded'],
}
Multipart 解析器
multipart 解析器处理文件上传和 multipart 表单数据。它处理 multipart/form-data 内容类型的请求,这是浏览器在提交包含文件输入的表单时使用的类型。
autoProcess
autoProcess 选项控制上传的文件是否自动移动到你操作系统的临时目录。启用后,解析器会在处理请求时将文件流式传输到磁盘。
自动处理后,你可以在控制器中使用 request.file() 访问上传的文件、验证它们,并将它们移动到永久位置或云存储服务。
multipart: {
autoProcess: true,
}
你可以指定一个路由模式数组,仅为特定的路由启用自动处理。这些值必须是路由模式,而不是 URL。
multipart: {
autoProcess: [
'/uploads',
'/posts/:id/images',
],
}
processManually
processManually 数组允许你为选定的路由禁用自动文件处理,同时在全局范围内保持启用。当你有少数几个路由需要自定义文件处理,但又希望在其他地方享受自动处理的便利时,这很有用。
这些值必须是路由模式,而不是 URL。
multipart: {
autoProcess: true,
processManually: [
'/file-manager',
'/projects/:id/assets',
],
}
encoding
encoding 选项指定将 multipart 请求中的文本字段转换为字符串时使用的字符编码。默认是 utf-8,可处理大多数用例。你可以使用
iconv-lite
包支持的任何编码。
multipart: {
encoding: 'utf-8',
}
limit
limit 选项设置单个请求中所有上传文件的最大总大小。超过此限制的请求将收到 413 Payload Too Large 错误响应。
multipart: {
limit: '20mb',
}
fieldsLimit
fieldsLimit 选项设置 multipart 请求中所有表单字段(非文件)的最大总大小。这可以防止通过极大的文本字段提交进行滥用。超过此限制的请求将收到 413 Payload Too Large 错误响应。
multipart: {
fieldsLimit: '2mb',
}
tmpFileName
tmpFileName 选项接受一个为临时文件生成自定义名称的函数。默认情况下,解析器会生成随机文件名。
multipart: {
tmpFileName: () => {
return `upload_${Date.now()}_${Math.random().toString(36)}`
},
}
types
types 数组定义了 multipart 解析器应处理哪些内容类型。默认情况下,它处理 multipart/form-data 请求。
multipart: {
types: ['multipart/form-data'],
}
用于自定义内容类型的 Raw 解析器
body parser 包含一个 raw 解析器,可以处理默认解析器不支持的内容类型。raw 解析器将请求体作为字符串提供,你可以随后使用自定义中间件对其进行处理。
当你的应用接收到 XML、YAML 或其他没有内置解析器的专门内容类型的数据时,这很有用。
const bodyParserConfig = defineConfig({
allowedMethods: ['POST', 'PUT', 'PATCH', 'DELETE'],
raw: {
types: ['application/xml', 'text/xml'],
limit: '1mb',
encoding: 'utf-8',
},
form: {
// ... form config
},
json: {
// ... json config
},
multipart: {
// ... multipart config
},
})
export default bodyParserConfig
为特定内容类型启用 raw 解析器后,创建自定义中间件将字符串数据解析为可用的格式。
另请参阅: 中间件文档
import type { HttpContext } from '@adonisjs/core/http'
import type { NextFn } from '@adonisjs/core/types/http'
import xml2js from 'xml2js'
export default class XmlParserMiddleware {
async handle({ request }: HttpContext, next: NextFn) {
if (request.header('content-type')?.includes('xml')) {
const rawBody = request.raw()
const parser = new xml2js.Parser()
const parsed = await parser.parseStringPromise(rawBody)
request.updateBody(parsed)
}
await next()
}
}
表单方法伪装
HTML 表单只支持 GET 和 POST 方法。方法伪装(method spoofing)允许你通过查询参数指定其他 HTTP 方法(PUT、PATCH、DELETE),从而使用标准 HTML 表单实现完整的 RESTful 路由。
// title: config/app.ts
import env from '#start/env'
import { defineConfig } from '@adonisjs/core/http'
export const http = defineConfig({
/**
* 为 HTML 表单启用方法伪装。
* 这允许表单通过在表单 action 中添加 ?_method=PUT
* 来使用 PUT、PATCH 和 DELETE 方法。
*/
allowMethodSpoofing: true
})
启用方法伪装后,你可以在表单中使用 _method 查询参数:
<!-- 表单将被作为 PUT 请求处理 -->
<form method="POST" action="/posts/1?_method=PUT">
<input type="text" name="title" />
<button type="submit">Update Post</button>
</form>
<!-- 表单将被作为 DELETE 请求处理 -->
<form method="POST" action="/posts/1?_method=DELETE">
<button type="submit">Delete Post</button>
</form>