查詢字符串

查詢字符串（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) 的字節數。