1. 概述

本文将介绍如何在 OpenAPI 中将 JSON 对象作为查询参数传递。这在构建灵活、可扩展的 RESTful 接口时非常实用,尤其适用于复杂过滤、排序或配置场景。

虽然看似简单,但不同版本的 OpenAPI 支持程度差异较大,稍不注意就会踩坑。下面我们从 OpenAPI 2 和 3 两个版本分别讲解,并附上关键注意事项。

2. OpenAPI 2 中的查询参数

OpenAPI 2 不支持对象类型的查询参数,只允许基本类型(如 string、number)或基本类型的数组。

因此,如果我们想传一个 JSON 对象,必须将其定义为 string 类型的参数,然后在后端手动解析。

示例如下:

swagger: "2.0"
info:
  title: "Ticket API"
  version: "1.0.0"
paths:
  /tickets:
    get:
      tags:
        - tickets
      summary: "Send a JSON Object as a query param"
      parameters:
        - name: "params"
          in: "query"
          description: "{\"type\":\"foo\",\"color\":\"green\"}"
          required: true
          type: "string"

⚠️ 注意:in: "path" 是原文笔误,应为 in: "query",否则参数会作为路径变量处理。

此时,客户端请求应为:

GET http://localhost:8080/api/tickets?params={"type":"foo","color":"green"}

而不是传统的扁平化参数:

GET http://localhost:8080/api/tickets?type=foo&color=green

这种方式简单粗暴,兼容性好,但失去了结构化校验能力,完全依赖后端解析逻辑。

3. OpenAPI 3 中的查询参数

OpenAPI 3 开始原生支持对象作为查询参数,这是重大改进。

我们可以通过 schema 定义参数结构,并利用 content 或直接在 schema 中描述对象。

正确写法如下:

openapi: 3.0.1
info:
  title: "Ticket API"
  version: "1.0.0"
paths:
  /tickets:
    get:
      tags:
        - tickets
      summary: "Send a JSON Object as a query param"
      parameters:
        - name: params
          in: query
          description: '{"type":"foo","color":"green"}'
          required: true
          schema:
            type: object
            properties:
              type:
                type: string
              color:
                type: string

此时,客户端可以使用两种格式发送请求:

方式一:带括号的嵌套语法(推荐)

GET http://localhost:8080/api/tickets?params[type]=foo&params[color]=green

✅ 优点:

  • OpenAPI 工具链可进行参数校验
  • 更清晰地表达结构化数据
  • 符合 OpenAPI 3 规范推荐格式

方式二:JSON 字符串形式(兼容 OpenAPI 2)

GET http://localhost:8080/api/tickets?params={"type":"foo","color":"green"}

✅ 优点:

  • 后端控制更灵活
  • 可与旧系统兼容

❌ 缺点:

  • 无法在 API 文档中做深度校验
  • 需自行处理解析异常

📌 提示:Swagger UI 默认生成第一种格式,更推荐使用。

4. URL 编码问题

⚠️ 所有包含特殊字符(如 {, }, ", :)的 JSON 参数都必须进行 URL 编码,否则会导致请求失败或被拦截。

例如原始请求:

GET /tickets?params={"type":"foo","color":"green"}

实际发送时应编码为:

GET /tickets?params=%7B%22type%22%3A%22foo%22%2C%22color%22%3A%22green%22%7D

📌 常见编码对照:

  • {%7B
  • }%7D
  • "%22
  • :%3A
  • ,%2C

✅ 建议前端使用 encodeURIComponent() 处理整个 JSON 字符串:

const params = { type: "foo", color: "green" };
const encoded = encodeURIComponent(JSON.stringify(params));
fetch(`/tickets?params=${encoded}`);

5. 使用限制与注意事项

虽然技术上可行,但将 JSON 对象放在查询参数中有几个明显短板,务必权衡:

❌ 安全性风险

  • 查询参数会明文出现在:
    • 浏览器地址栏
    • 服务器访问日志
    • 反向代理、CDN 日志
  • 若 JSON 中包含敏感信息(如 token、用户 ID),极易泄露

❌ 长度限制

  • 大多数浏览器和服务器对 URL 长度限制为 2048 字符以内
  • 经过 URL 编码后,实际可用 payload 约 1000 字符左右
  • 超长 JSON 会被截断或拒绝

✅ 替代方案:使用请求体(Request Body)

对于复杂或较大的 JSON 数据,建议改用 GET 请求带 body 或直接使用 POST:

POST /tickets/search
Content-Type: application/json

{
  "filters": {
    "type": "foo",
    "color": "green"
  },
  "sort": "createdAt"
}

或(非主流但合法):

GET /tickets
Content-Type: application/json

{ "type": "foo", "color": "green" }

📌 注意事项:

  • ✅ GET + body 符合 HTTP 规范,RESTful 语义清晰
  • ❌ 但部分前端库(如早期 jQuery、某些 Fetch 封装)不支持 GET 请求携带 body
  • ⚠️ 代理服务器或 CDN 可能忽略或丢弃 GET 请求体

💡 建议:优先使用 POST + body 处理复杂查询,保持兼容性和可维护性。

6. 总结

本文梳理了在 OpenAPI 中使用 JSON 对象作为查询参数的两种方式:

版本 是否支持对象 推荐格式 校验能力
OpenAPI 2 ❌ 否 ?params={"a":"b"}
OpenAPI 3 ✅ 是 ?params[a]=x&params[b]=y

📌 核心建议:

  1. 能用 OpenAPI 3 就不用 2,结构化参数更清晰
  2. 注意 URL 编码,避免传输失败
  3. 敏感或大数据量场景,优先考虑请求体方式
  4. 文档中明确参数格式,减少前后端沟通成本

完整示例代码已发布在 GitHub:

👉 https://github.com/eugenp/tutorials/tree/master/spring-web-modules/spring-rest-http


原始标题:OpenAPI JSON Objects as Query Parameters