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 应用时会自动创建。

config/bodyparser.ts
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 方法应该解析其请求体。默认情况下,只处理 POSTPUTPATCHDELETE 请求。GET 请求被排除在外,因为它们通常不包含请求体。

全局解析选项

所有解析器都可用两个全局选项:convertEmptyStringsToNulltrimWhitespaces。这些选项有助于在请求数据到达你的应用逻辑之前对其进行规范化。

convertEmptyStringsToNull

convertEmptyStringsToNull 选项将请求体中的所有空字符串转换为 null 值。这个选项解决了 HTML 表单的一个常见问题。

当 HTML 表单输入字段没有值时,浏览器会在请求体中发送一个空字符串,而不是完全省略该字段。这种行为给数据库规范化带来了挑战,尤其是可空列。

考虑一个带有可选 "country" 字段的用户注册表单。你的数据库有一个可空的 country 列,并且你希望在用户未选择国家时存储 null。然而,HTML 表单发送的是一个空字符串,这意味着你会向数据库中插入一个空字符串,而不是让该列保持 null

启用 convertEmptyStringsToNull 会自动处理这种不一致。body parser 会在你的验证或数据库逻辑运行之前将所有空字符串转换为 null

config/bodyparser.ts
json: {
  convertEmptyStringsToNull: true,
}
trimWhitespaces

trimWhitespaces 选项会移除请求体所有字符串值首尾的空白字符。这有助于消除用户在提交表单时可能附带的多余空白。

无需在控制器或验证器中手动修剪值,你可以启用这个选项,让 body parser 在全局范围内处理空白移除。

config/bodyparser.ts
form: {
  trimWhitespaces: true,
}

JSON 解析器

JSON 解析器处理带有 JSON 编码请求体的请求。默认情况下,它会处理多种内容类型,包括 application/jsonapplication/json-patch+jsonapplication/vnd.api+jsonapplication/csp-report

encoding

encoding 选项指定将请求体 Buffer 转换为字符串时使用的字符编码。默认是 utf-8,可处理大多数用例。你可以使用 iconv-lite 包支持的任何编码。

config/bodyparser.ts
json: {
  encoding: 'utf-8',
}
limit

limit 选项设置解析器接受的请求体数据的最大大小。超过此限制的请求将收到 413 Payload Too Large 错误响应。

config/bodyparser.ts
json: {
  limit: '1mb',
}
strict

strict 选项控制解析器是否只接受对象和数组作为顶层的 JSON 值。启用后,解析器会拒绝根层级的原始值,如字符串、数字或布尔值。

config/bodyparser.ts
json: {
  strict: true,
}
types

types 数组定义了 JSON 解析器应处理哪些内容类型。如果你的应用接收到带有非标准内容类型请求头的 JSON 数据,可以添加自定义内容类型。

config/bodyparser.ts
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 包支持的任何编码。

config/bodyparser.ts
form: {
  encoding: 'utf-8',
}
limit

limit 选项设置解析器接受的请求体数据的最大大小。超过此限制的请求将收到 413 Payload Too Large 错误响应。

config/bodyparser.ts
form: {
  limit: '1mb',
}
queryString

queryString 选项允许你配置 URL 编码字符串如何解析为对象。这些选项会直接传递给负责解析的 qs 包。

config/bodyparser.ts
form: {
  queryString: {
    depth: 5,
    parameterLimit: 1000,
  },
}

另请参阅: qs 文档 了解所有可用选项。

types

types 数组定义了 form 解析器应处理哪些内容类型。默认情况下,它处理 application/x-www-form-urlencoded 请求。

config/bodyparser.ts
form: {
  types: ['application/x-www-form-urlencoded'],
}

Multipart 解析器

multipart 解析器处理文件上传和 multipart 表单数据。它处理 multipart/form-data 内容类型的请求,这是浏览器在提交包含文件输入的表单时使用的类型。

autoProcess

autoProcess 选项控制上传的文件是否自动移动到你操作系统的临时目录。启用后,解析器会在处理请求时将文件流式传输到磁盘。

自动处理后,你可以在控制器中使用 request.file() 访问上传的文件、验证它们,并将它们移动到永久位置或云存储服务。

config/bodyparser.ts
multipart: {
  autoProcess: true,
}

你可以指定一个路由模式数组,仅为特定的路由启用自动处理。这些值必须是路由模式,而不是 URL。

config/bodyparser.ts
multipart: {
  autoProcess: [
    '/uploads',
    '/posts/:id/images',
  ],
}
processManually

processManually 数组允许你为选定的路由禁用自动文件处理,同时在全局范围内保持启用。当你有少数几个路由需要自定义文件处理,但又希望在其他地方享受自动处理的便利时,这很有用。

这些值必须是路由模式,而不是 URL。

config/bodyparser.ts
multipart: {
  autoProcess: true,
  processManually: [
    '/file-manager',
    '/projects/:id/assets',
  ],
}
encoding

encoding 选项指定将 multipart 请求中的文本字段转换为字符串时使用的字符编码。默认是 utf-8,可处理大多数用例。你可以使用 iconv-lite 包支持的任何编码。

config/bodyparser.ts
multipart: {
  encoding: 'utf-8',
}
limit

limit 选项设置单个请求中所有上传文件的最大总大小。超过此限制的请求将收到 413 Payload Too Large 错误响应。

config/bodyparser.ts
multipart: {
  limit: '20mb',
}
fieldsLimit

fieldsLimit 选项设置 multipart 请求中所有表单字段(非文件)的最大总大小。这可以防止通过极大的文本字段提交进行滥用。超过此限制的请求将收到 413 Payload Too Large 错误响应。

config/bodyparser.ts
multipart: {
  fieldsLimit: '2mb',
}
tmpFileName

tmpFileName 选项接受一个为临时文件生成自定义名称的函数。默认情况下,解析器会生成随机文件名。

config/bodyparser.ts
multipart: {
  tmpFileName: () => {
    return `upload_${Date.now()}_${Math.random().toString(36)}`
  },
}
types

types 数组定义了 multipart 解析器应处理哪些内容类型。默认情况下,它处理 multipart/form-data 请求。

config/bodyparser.ts
multipart: {
  types: ['multipart/form-data'],
}

用于自定义内容类型的 Raw 解析器

body parser 包含一个 raw 解析器,可以处理默认解析器不支持的内容类型。raw 解析器将请求体作为字符串提供,你可以随后使用自定义中间件对其进行处理。

当你的应用接收到 XML、YAML 或其他没有内置解析器的专门内容类型的数据时,这很有用。

config/bodyparser.ts
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 解析器后,创建自定义中间件将字符串数据解析为可用的格式。

另请参阅: 中间件文档

app/middleware/xml_parser_middleware.ts
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>