---
title: "查询参数里的加号怎么变成空格了？从 URLSearchParams 的两种输入查起"
date: 2026-11-13
slug: url-query-plus-and-duplicates
canonical: https://kernelranch.com/blog/2026/11/url-query-plus-and-duplicates
markdown_url: https://kernelranch.com/blog/2026/11/url-query-plus-and-duplicates.md
tags: ["web", "javascript", "url"]
---搜索 `C++` 却得到 `C  ` 时，检查代码是否先拼成查询字符串，再交给 `URLSearchParams` 解析。字符串构造走表单式解码，`+` 会被读成空格。构造新 URL 时用 `set()` 或 `append()` 传值，让序列化器把字面加号编码为 `%2B`。

## 1. 比较参数值、重复键和序列化字节

把以下内容保存为 `query_lab.mjs`，运行 `node query_lab.mjs`。

```javascript
import assert from 'node:assert/strict';

const parsed = new URLSearchParams('q=C++&tag=red&tag=blue');
assert.equal(parsed.get('q'), 'C  ');
assert.deepEqual(parsed.getAll('tag'), ['red', 'blue']);
console.log('parsed q:', JSON.stringify(parsed.get('q')));
console.log('tags:', JSON.stringify(parsed.getAll('tag')));

const built = new URLSearchParams();
built.set('q', 'C++');
built.append('tag', 'red');
built.append('tag', 'blue');
assert.equal(built.toString(), 'q=C%2B%2B&tag=red&tag=blue');
console.log('built:', built.toString());

const url = new URL('https://example.test/?q=a%20b~');
const before = url.search;
url.searchParams.sort();
assert.equal(url.searchParams.get('q'), 'a b~');
assert.equal(before, '?q=a%20b~');
assert.equal(url.search, '?q=a+b%7E');
console.log('serialized:', before, '->', url.search);
```

```text
parsed q: "C  "
tags: ["red","blue"]
built: q=C%2B%2B&tag=red&tag=blue
serialized: ?q=a%20b~ -> ?q=a+b%7E
```

## 2. 接口先约定重复键，再处理签名

允许多个筛选值时，读取 `getAll()` 并保留顺序。约定单值的参数可要求 `getAll(name).length === 1`，把多次出现视为输入错误。`get()` 只返回第一个匹配，`set()` 会替换第一个值并移除后续同名项。调用方和服务端若在同一参数上分别使用首值和末值，可能对同一 URL 得出不同结论。

签名覆盖请求目标的场合，使用签名协议规定的原始字节或规范化算法。样本只有一个参数，排序前后的值相同，序列化仍改写了空格和波浪号。若入口代理会重写路径或查询字符串，要在签名验证接入测试里记录签名方与验证方各自拿到的请求目标，再按协议核对。

## 小结

按值构造查询字符串，显式定义重复键规则。涉及签名时，用字节级样本验证规范化是否一致。

## 数据来源与检索时间

| 来源 | URL | 检索日期 | 对应内容 |
|---|---|---|---|
| WHATWG URL Standard §5 | https://url.spec.whatwg.org/#application/x-www-form-urlencoded | 2026-11-13 | 表单解析与序列化中的加号、空格和百分号编码 |
| WHATWG URLSearchParams | https://url.spec.whatwg.org/#urlsearchparams | 2026-11-13 | get、getAll、set、sort 和 URL 更新行为 |

## 核验说明

- Windows、Node.js 24.19.0 运行标准 URL API 验证三组行为。样本 URL 仅用于本地构造，未访问网站，也未经过浏览器或代理。
