查询字符串

查询字符串（query string）是统一资源定位符 (URL) 的一部分，用于将值分配给指定的参数。查询字符串通常包括由Web浏览器或其他客户端应用程序添加到基本URL的字段，例如作为HTML文档的一部分、选择页面的外观或跳转到多媒体内容中的位置。

<image>

Web服务器可以通过基于URL路径从其文件系统读取文件或使用特定于资源类型的逻辑处理请求来处理超文本传输协议 (HTTP) 请求。在调用特殊逻辑的情况下，查询字符串以及URL的路径组件将可供该逻辑在其处理中使用。

## 结构

包含查询字符串的典型URL如下：`https://example.com/over/there?name=ferret`

当服务器收到对此类页面的请求时，它可以运行一个程序，将查询字符串（在本例中为`name=ferret`）传递给该程序，不做任何更改。问号用作分隔符，不是查询字符串的一部分。

Web框架可能提供用于解析查询字符串中的多个参数的方法，这些参数由某些分隔符分隔。在下面的示例URL中，多个查询参数由与号“`&amp;`”分隔：

查询字符串的确切结构尚未标准化。用于解析查询字符串的方法可能因网站而异。

网页中的链接可能具有包含查询字符串的URL。HTML定义了用户代理生成查询字符串的三种方式：

- 通过form元素生成HTML表单
- 通过元素上的ismap属性和构造的服务器端图像映射
- 通过现已弃用的isindex元素进行索引搜索

### 网页表单

最初的用途之一是包含HTML表单（也称为Web表单）的内容。特别地，当提交包含字段`field1`, `field2`, `field3`的表单时，字段的内容被编码为查询字符串，如下所示：

`field1=value1&amp;field2=value2&amp;field3=value3...`

- 查询字符串由一系列“字段-值”对组成。
- 在每对中，字段名称和值由等号“`=`”分隔。
- 该系列的“字段-值”对由与号“`&amp;`”分隔（W3C不再推荐使用分号“`;`”，请参见下文）。

虽然没有明确的标准，但大多数Web框架允许多个值与单个字段关联（例如`field1=value1&field1=value2&field2=value3`）。

对于表单的每个字段，查询字符串包含一对`<var>field</var>=<var>value</var>`。Web 表单可能包含用户不可见的字段；提交表单时，这些字段将包含在查询字符串中。

该约定是W3C的建议。在1999年的建议中，W3C建议所有Web服务器除了&符号分隔符之外还支持分号分隔符，以允许HTML文档的URL中的application/x-www-form-urlencoded查询字符串，而无需对&符号进行实体转义。自2014年起，W3C建议仅使用&符号作为查询分隔符。

当表单提交方法为GET时，表单内容仅编码在URL的查询字符串中。当提交方法为POST时，默认使用相同的编码，但结果作为HTTP request body 提交，而不是包含在修改后的URL 中。

### 索引搜索

在将表单添加到HTML之前，浏览器将isindex元素呈现为单行文本输入控件。输入到此控件中的文本作为查询字符串发送到服务器，附加到对基本URL或action属性指定的另一个URL的GET请求。这样做的目的是允许Web服务器使用提供的文本作为查询条件，以便它们可以返回匹配页面的列表。

当输入到索引搜索控件的文本被提交时，它被编码为查询字符串，如下所示：

`argument1+argument2+argument3...`

- 查询字符串由一系列参数组成，通过将文本解析为该空间处的单词。
- 该系列由加号“`+`”分隔。

尽管isindex元素已被弃用，并且大多数浏览器不再支持或呈现它，但仍然存在索引搜索的一些痕迹。例如，这就是浏览器URL的百分号编码中加号“`+`”特殊处理的来源（如今，随着索引搜索的弃用，`%20`几乎是多余的）。此外，如果查询字符串不包含等号“`=`”（根据CGI 1.1的第4.4节），一些支持CGI的Web服务器（例如Apache）会将查询字符串处理为命令行参数。一些CGI脚本仍然依赖并使用这种历史行为来嵌入HTML中的URL。

## URL编码

有些字符不能是URL的一部分（例如空格），而其他一些字符在URL中具有特殊含义：例如，字符`#`可用于进一步指定文档的小节（或片段）。在HTML表单中，字符`=`用于分隔名称和值。URI通用语法使用URL编码来处理此问题，而HTML表单会进行一些额外的替换，而不是对所有此类字符应用百分比编码。

HTML5指定以下转换，用于使用“GET”方法向Web服务器提交HTML表单。以下是该算法的简要总结：

- 无法转换到正确字符集的字符将替换为HTML字符值引用
- 空格被编码为“`+`”或“`%20`”。
- 字母（`A`–`Z`和`a`–`z`）、数字 (`0`–`9`) 和字符'`~`','`-`','`.`' and '`_`'保持原样
- `+`由%2B编码
- 所有其他字符均编码为`%HH`十六进制表示形式，任何非ASCII字符首先编码为UTF-8（或其他指定的编码）

RFC 3986允许在查询字符串中使用与波浪号（“`~`”）相对应的八位字节，但需要在HTML表单中将其百分比编码为“`%7E`”。

将SPACE编码为“`+`”或者选择“原样”表示该字符，这是HTML5编码与RFC 3986的差异。

## 例子

把如下表单迁入HTML网页:

```html
<form action="/cgi-bin/test.cgi" method="get">
  <input type="text" name="first" />
  <input type="text" name="second" />
  <input type="submit" />
</form>
```

用户在两个文本输入框中写入字符串“this is a field”和“ it clear ?”并按提交按钮，程序`test.cgi`（上例中由form元素的action属性指定的程序）将收到以下查询字符串：`first=this+is+a+field&amp;second=was+it+clear+%28already%29%3F`

如果表单由CGI脚本在服务器上处理，则该脚本通常可以将查询字符串作为名为`QUERY_STRING`的环境变量接收。

## 跟踪用户状态

接收查询字符串的程序可以忽略部分或全部查询字符串。如果请求的URL对应于文件而不是程序，则忽略整个查询字符串。但是，无论是否使用查询字符串，包括它的整个URL都会存储在服务器日志文件中。

这些事实允许使用查询字符串以类似于HTTP cookie提供的方式跟踪用户。为此，每次用户下载页面时，都必须选择一个唯一标识符并将其作为查询字符串添加到该页面包含的所有链接的URL中。一旦用户点击其中一个链接，就会向服务器请求相应的URL。这样，本页的下载就与上一页链接起来了。

例如，当请求包含以下内容的网页时：

```html
 <a href="foo.html">see my page!</a>
 <a href="bar.html">mine is better</a>
```

选择一个唯一的字符串，例如`e0a72cb2a2c7`，并将页面修改如下：

```html
 <a href="foo.html?e0a72cb2a2c7">see my page!</a>
 <a href="bar.html?e0a72cb2a2c7">mine is better</a>
```

添加查询字符串不会改变页面向用户显示的方式。例如，当用户点击第一个链接时，浏览器会向服务器请求页面`foo.html?e0a72cb2a2c7`，而服务器会忽略`?`后面的内容并按预期发送页面`foo.html`，同时将查询字符串添加到其链接中。

这样，来自该用户的任何后续页面请求都将携带相同的查询字符串`e0a72cb2a2c7`，从而可以确定所有这些页面已被同一用户查看。查询字符串通常与网络信标结合使用。

用于跟踪的查询字符串和 HTTP cookie 之间的主要区别是：

- 查询字符串构成URL的一部分，因此如果用户保存URL或将URL发送给其他用户，查询字符串也会包含在内；cookie可以跨浏览会话进行维护，但不会与URL一起保存或发送。
- 如果用户通过两个（或多个）独立路径到达同一个Web服务器，它将被分配两个不同的查询字符串，而存储的cookie是相同的。
- 用户可以禁用cookie，在这种情况下，使用cookie进行跟踪将不起作用。但是，使用查询字符串进行跟踪应该适用于所有情况。
- 不同的页面访问所传递的不同查询字符串将意味着页面永远不会从浏览器（或代理，如果存在）缓存中获取服务，从而增加Web服务器上的负载并降低用户体验。

## 兼容性问题

根据HTTP规范：

在实践中发现了对请求行长度的各种临时限制。建议所有HTTP发送方和接收方至少支持8000个八位字节的请求行长度。

如果URL太长，Web服务器将失败并显示HTTP状态码：414 Request-URI Too Long。

这些问题的常见解决方法是使用POST而不是GET并将参数存储在请求体中。请求体的长度限制通常远高于URL长度的长度限制。例如，默认情况下，POST大小限制在IIS 4.0上为 2 MB，在IIS 5.0上为 128 KB。该限制在Apache2上可使用LimitRequestBody指令进行配置，该指令指定请求体中允许的从 0（表示无限制）到2147483647 (2 GB) 的字节数。