编码范围与结构规则
查询串由若干「键=值」对组成,对之间用 & 连接。正确做法是把键与值分别做百分号编码后再拼接,绝不把整条查询串塞进编码函数。数组参数常见两种表示:重复键(同一键出现多次)与带方括号的键名(某些框架可识别),两者在服务端解析行为不同。空值(键存在但值为空)与缺参数(键不出现)语义也不同,接口设计应明确区分。
基本信息
| 编码范围 | 键与值分别编码,分隔符 & 与 = 保持原样 |
|---|---|
| 分隔符 | & 连接参数对,= 连接键与值 |
| 数组表示 | 重复键或键名带方括号,取决于框架 |
| 空值语义 | key= 表示键存在值为空,与缺参数不同 |
| 顺序影响 | 签名与校验场景通常要求固定顺序或排序后拼接 |
| 长度限制 | 过长查询串会被网关截断,大参数应改用请求体 |
典型使用场景
- 筛选条件拼接:多条件参数逐个编码,避免特殊字符破坏结构
- 数组查询:多选筛选项按框架约定使用重复键或方括号
- 分页与游标:游标值可能含特殊字符,必须编码后传递
- 签名与回调:参数顺序与编码规则写进协议,两端严格一致
- 深度链接与分享:链接中的参数值与展示文案需分别处理
拼接与解析示例
拼接与解析示例
# 逐参数编码:值为 a&b=c 也不会破坏结构
python -c "import urllib.parse as u; print(u.urlencode({'q':'a&b=c','page':1}))"
# 数组参数:doseq 保持重复键形式
python -c "import urllib.parse as u; print(u.urlencode({'tag':['x','y']}, doseq=True))" # tag=x&tag=y
# 解析时保留全部同名参数
python -c "import urllib.parse as u; print(u.parse_qs('tag=x&tag=y'))" # {'tag': ['x','y']}
# 空值与缺参数的差别
python -c "import urllib.parse as u; print(u.parse_qs('a=&b'))" # {'a': ['']}
# 签名场景:排序后拼接并统一编码,避免顺序差异导致校验失败参数形态与语义
| 写法 | 含义 | 注意点 |
|---|---|---|
| a=1&a=2 | 重复键,通常解析为数组 | 部分框架只取最后一个值 |
| a[]=1&a[]=2 | 方括号数组写法 | 依赖框架支持,跨语言不通用 |
| a= | 键存在、值为空字符串 | 与缺参数语义不同 |
| (不出现 a) | 参数缺失 | 业务应显式区分两者 |
| a=%E4%B8%AD | 值已编码 | 解码一次即得原文 |
| a=1&b=2 与 b=2&a=1 | 顺序不同、内容相同 | 签名场景可能要求排序 |
拼接查询串时不要手工拼字符串:值里出现 & 或 = 就会破坏结构,出现中文未编码则可能被网关拒绝。使用语言自带的编码函数按参数逐个处理,能一次性避免大部分问题。
参数注入是查询串的常见风险:未编码的 & 能让值逃逸成新参数,覆盖服务端信任字段。服务端应对每个参数独立校验类型与长度,并对敏感参数做白名单,而不是信任客户端发来的完整串。
长度与隐私也要考虑:查询串会进入网关日志、浏览器历史与统计系统,敏感或超长内容应放在请求体中传输,避免被记录与截断。