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¶ms[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¶ms[b]=y |
强 |
📌 核心建议:
- 能用 OpenAPI 3 就不用 2,结构化参数更清晰
- 注意 URL 编码,避免传输失败
- 敏感或大数据量场景,优先考虑请求体方式
- 文档中明确参数格式,减少前后端沟通成本
完整示例代码已发布在 GitHub:
👉 https://github.com/eugenp/tutorials/tree/master/spring-web-modules/spring-rest-http