## 瀏覽器的 HTTP 請求
發起一個 HTTP 請求很容易。比如你說你想用瀏覽器訪問 Reddit 。你所需要做的僅僅是啟動瀏覽器然后在地址欄輸入?[http://www.reddit.com](http://www.reddit.com/),然后你就可以看到:

承載 Reddit 網站的服務器處理你的請求并返回給你的瀏覽器一個響應。你的瀏覽器足夠聰明去處理這個響應,然后將所有的顏色,圖片,文字,樣式給你展示成上圖那樣。
## 用 HTTP 工具發起 HTTP 請求
因為瀏覽器給我們展示的是處理過的服務器響應,我們看不到服務器發給我們的響應的本來面目。怎么樣才能看到原始的 HTTP 響應數據呢?
為此,我們可以使用一個 HTTP 工具,就像用瀏覽器的時候我們輸入網址一樣,我們可以用 HTTP 工具發送一個請求到?[http://www.reddit.com](http://www.reddit.com/)。我們的 HTTP 工具,Paw,不會處理響應數據,這樣能讓我們看到原始的響應數據,大概長這樣:

跟你在瀏覽器里看到的簡直就是天壤之別啊!如果你以前從來沒見過原始的 HTTP 響應數據,這可能讓你有點吃驚。你現在看到的這些,其實就是你瀏覽器接收到的數據,只不過是瀏覽器把這一堆數據處理成了人類友好的格式。
如果你學習 HTTP 的知識是為了成為一個 web 開發者,你需要學著閱讀原始的 HTTP 響應數據。當然你不用在你的腦海里把這些數據解析成一個高清晰度的畫面,但是你應該對這些響應代表著什么有一個整體上的概念。等你有了足夠的經驗,你就可以深入原始的 HTTP 響應數據去做一些 debug 工作和一探其究竟。
## 使用審查器 (Inspector)
所有現代瀏覽器都有查看 HTTP 請求和響應的方法,通常都叫做**審查器**。我們使用?[Chrome 審查器](https://developer.chrome.com/devtools),演示一下如何用它來分析你瀏覽器的 HTTP 通信。
1. 打開 Chrome 瀏覽器,點擊右上角的菜單,選擇工具,然后選擇開發者工具,這樣就打開審查器了。
2. 在地址欄輸入?[http://www.reddit.com](http://www.reddit.com/),來給 Reddit 重新發送一個新的請求。
3. 以上兩個步驟要確保審查器開著啊,然后點擊 Network 標簽:
4. 你應該注意的第一件事是,這里會有很多項。每一項都是一個單獨的請求,也就是說,僅僅是訪問了這一個 URL,你的瀏覽器就發起了多個請求,一個請求對應著一個資源 (圖片,文件等等)。點擊一下對主頁的第一個請求,就是`www.reddit.com`那項:
5. 這里,你就能看到特定的請求頭部,cookies,還有原始的響應數據:在默認的子標簽?_Headers_?里顯示了發給服務器的請求頭部和接收到的服務器返回來的響應頭部。接下來點擊?_Response_?子標簽去看看原始響應數據。這個響應數據看起來應該跟我們前面使用 HTTP 工具得到的差不多。
另一個要注意的事情是,當我們使用審查器的時候,在?_Network_?標簽下,除了第一個請求,還有一堆其他請求的返回:
為什么會出現這些額外的響應,誰發起的這些請求? 事情是這樣的,當我們請求資源的時候,最初的對于`www.reddit.com`的請求,返回了一些 HTML。 這些 HTML 里又引用了其他的資源比如圖片,CSS 文件,javascript 文件等等。你的瀏覽器,很聰明也很給力。它明白,為了展示出一個能夠給人看的網頁,它必須去把這些引用到的資源也一并拿來. 因此瀏覽器就會對得到的初次響應里的每一個資源再一一發起請求。當你在_Network_?標簽里往下滾動頁面,你就能看到所有的相關資源。這些其他的請求保證了這個網頁和其他一些東西能正常良好的顯示在你的屏幕上.整體來看,瀏覽器的審查器對于相關的引用資源給你了一個良好的體驗.另一方面,一個純粹的 HTTP 工具,只會返回**一個**巨大的響應數據,并不會自動拉取引用的資源.一個用`curl`發起的請求可以這樣寫:
~~~
$ curl -X GET "http://www.reddit.com/" -m 30 -v
~~~
然后你看到的應該只是一個請求,一個響應包含著 HTML,但是沒有那些你在瀏覽器里看到的自動發起的額外請求。
## 請求方法
讓我們重溫一下上面第 3 步里的圖,看看?_Network_?標簽里的響應數據。你會發現有這么兩列?**Method**?和?**Status**。
本節我們來看看這幾列里的信息是什么意思。
在?**Method**?這一列里的數據呢,就是常說的 HTTP 請求方法 ( Request Method )。你可以這樣想,把這個 Method 當做是一個動詞,用來告訴服務器你想對某個資源做什么。最常見的兩個 HTTP 請求方法是?`GET`?和`POST`?。當你想要獲取信息的時候,使用`GET`這個最常用的 HTTP 請求方法。在上面那張圖你應該能看到,所有使用?`GET`?的請求,都是為了去拿到那些需要顯示在網頁上的資源。
**Status**?這列顯示了每一個請求的響應狀態碼。在本書的后面章節我們會詳細討論響應。現在你需要理解的是每一個請求都會得到一個響應,哪怕這響應是一個錯誤響應 -- 那它也是個響應嘛。(這句話其實不是百分百正確,因為有些響應會**超時**,但是現階段我們不用在意這些細節。)
## GET 請求
`GET`?請求一般出現在超鏈接或者瀏覽器的地址欄里。當你在你的瀏覽器地址欄里輸入類似`http://www.reddit.com`?這樣的地址的時候,你就是在發起一個 GET 請求。你讓瀏覽器去取這個地址上的資源,這就意味著我們在整本書里一直在使用`GET`請求。在你點擊 web 應用上的超鏈接的時候也會發生同樣的事情。超鏈接的默認行為就是向一個 URL 發送`GET`請求。讓我們用 HTTP 工具向?`http://www.reddit.com`?發起一個簡單的?`GET`?請求。要確保你選擇了?`GET`?這一項,然后輸入網址:
在右邊窗口你就能看到服務器發回來的原始 HTTP 響應數據和一些其他信息。
使用?`curl`?的讀者呢,可以在終端里敲入以下命令:
~~~
curl -X GET "http://www.reddit.com/" -m 30 -v
~~~
我們也可以用 HTTP 工具發送帶查詢字符串的請求.舉個例子,我們發起一個帶查詢字符串的請求去搜索`https://itunes.apple.com/`里所有跟`Michael Jackson`有關的東西.請求使用的 URL 長這樣:
~~~
https://itunes.apple.com/search?term=Michael%20Jackson
~~~
還是那句話,用 Paw 的,發起請求前確保選的是`GET`。

這里我們僅僅是給`https://itunes.apple.com/`的服務器發送了一個帶著參數?`term=Michael%20Jackson`?的 HTTP?`GET`?請求,其中`%20`是空格的 URL 編碼字符。
這個例子所用的?`curl`?命令是這樣的:
~~~
$ curl -X GET "https://itunes.apple.com/search?term=Michael%20Jackson" -m 30 -v
~~~
以上就是你現階段需要知道的所有關于發起 HTTP?`GET`?請求的所有知識。主要的概念有以下幾點:
* `GET`?請求經常用于取得一個資源,而且大部分超鏈接都是?`GET`?請求。
* 一個?`GET`?請求的響應可以是任何東西,但是如果響應是一個 HTML 并且里面引用了其他資源,你的瀏覽器會自動對這些資源發起請求,而一個純粹的 HTTP 工具則不會。
* 使用?`GET`?請求的時候在大小和安全性上有一些限制。
## POST 請求
我們已經看到了怎么使用?`GET`?去從一個服務器取得和請求數據,但是如果我們需要給服務器提交一些數據怎辦呢? 這就要提到另一個非常必要和重要的 HTTP 請求法方法`POST`啦。當你想在服務器上采取一些行動,或者給服務器發送數據的時候,就要用到?`POST`?了。讓我們用 HTTP 工具來舉個例子:

按照慣例給出 curl 命令:
~~~
$ curl -X POST "http://echo.httpkit.com" -m 30 -v
~~~
上面的圖片顯示了對?`http://echo.httpkit.com`?發起的?`POST`請 求和服務器返回的響應。瀏覽器里的典型`POST`?使用案例就是你提交一個表單的時候。`POST`?請求允許我們向服務器發送更大或者敏感的數據,比如圖片或者視頻。舉個例子,比如我們要把我們的用戶名和密碼發送到服務器上去做驗證。我們完全可以使用?`GET`?通過附加查詢字符串把數據發給服務器。這樣做的毛病很明顯: 我們的驗證信息在 URL 上是可見的。這必然不是我們想要的。在表單提交上使用?`POST`?請求能夠解決這個問題。而且?`POST`?請求也能避免你使用?`GET`?請求時的查詢字符串長度限制問題。通過?`POST`?請求,我們可以給服務器發送更大的數據。
我們再來看一個使用?`POST`?請求提交 web 表單的例子。在瀏覽器里我們的示例表單長這樣:

提交表單后,你會被重定向到這樣一個頁面:

現在讓我們切換到我們的 HTTP 工具,來模擬一下我們剛才在瀏覽器里做的事情。我們要發起一個?`POST`?請求到?`http://al-blackjack.herokuapp.com/new_player`?來代替在瀏覽器里填寫表單。下面是我們填寫第一個表單(就是那個我們寫名字的那個)的方法:

或者用 curl:
~~~
$ curl -X POST "http://al-blackjack.herokuapp.com/new_player" -d "player_name=Albert" -m 30 -v
~~~
注意在圖片和 curl 命令里我們都提供了額外的參數:?`player_name=albert`?。 這個跟我們在第一個表單的 “What's your name?” 輸入框里填寫內容并提交是一樣的。
我們可以使用審查器查看內容 (右鍵選擇`審查元素`)。你能看到,我們在?`POST`?請求里發送的參數`player_name`?就是這個表單里面 input 元素的 name 屬性里的內容:

這個的奧秘在于,我們是怎樣不通過 URL,僅僅是提交表單就把數據發送到服務器的呢?答案是 HTTP 的正文 ( Body )。正文包含了正在傳輸的 HTTP 消息,這個內容是可選的。換句話說就是,可以發送一個沒有正文的 HTTP 消息。當你要使用正文的時候,可以包含 HTML,圖片,音頻等等。你可以把正文看成包裹在信封里發出的信件(譯注:URL 就好比是信封上寫的地址,是可見的,而信的內容在信封里是不可見的)。
HTTP 工具和 curl 發起的?`POST`?請求跟你在瀏覽器里填寫表單然后提交是一樣的,緊接著我們會被重定向到下一個頁面。仔細看看 HTTP 工具的圖片,看看里面的原始響應數據,把我們重定向到下一個頁面的關鍵信息在這一行?`Location: http://al-blackjack.herokuapp.com/bet`?。?`Location`?和它對應的數據,就是所謂的 HTTP 響應頭部里的一部分 (是的,你可能也想到了,請求也有頭部,但是在現在這個例子里,這是一個響應頭部)。不要太在意這些細節,在后面章節我們會討論頭部。當你的瀏覽器看到這個響應頭部,然后就會自動向`Location`?頭部里的 URL 發起一個全新的,完全獨立的請求。你看到的那個 "Make a bet" 表單,就是這第二個請求的響應內容。

如果你對上面幾段內容感到困惑,那就再多看兩遍。使用瀏覽器的時候有一點很重要,那就是瀏覽器對你隱藏了大量的 HTTP 請求/響應的細節。你的瀏覽器發起最初的一個?`POST`?請求,得到了一個包含?`Location`?頭部的響應,然后在不用你參與的情況下發起另一個請求,然后把第二個請求得到的響應內容展示給你。再重申一次,如果你使用的是一個純粹的 HTTP 工具,你能看到第一個?`POST`?請求的?`Location`?響應頭部,但是這個工具不會自動發起第二個請求。(有些 HTTP 工具有這個功能,你可以看看 "automatically follow redirects" 選項)
## HTTP 頭部
HTTP 頭部允許客戶端和服務器在請求/響應的 HTTP 周期里發送額外的信息。頭部,通常是以冒號分隔的鍵值對兒,一般是純文本格式的。我們可以使用**審查器**來看看這些頭部。下面這張圖你能看到請求和響應都有頭部:

上圖顯示了在一個請求/響應周期里傳遞的各種頭部。更進一步,在?`Request Headers`?下面,我們能看到請求和響應包含著不同的頭部:

## 請求頭部
請求頭部提供更多關于服務器和要獲取的資源的信息。一些有用的請求頭部是:
| 字段名 | 描述 | 舉例 |
| --- | --- | --- |
| Host | 服務器域名 | Host:[www.reddit.com](http://www.reddit.com/) |
| Accept-Language | 可接受的語言 | Accept-Language: en-US,en;q=0.8 |
| User-Agent | 一個標識客戶端的字符串 | User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/38.0.2125.101 Safari/537.36 |
| Connection | 客戶端連接的類型 | Connection: keep-alive |
沒有必要去記憶任何一個請求頭部,但是一定要知道這是發送到服務器請求里的一部分。在下一章我們會討論響應頭部。
## 小結
本章我們做了一個簡單的 HTTP 請求介紹。讀完本章你應該能熟練應用:
* 使用審查器查看 HTTP 請求
* 使用 HTTP 工具發起 GET/POST 請求
關于 HTTP 請求最終的部分是理解以下內容:
* URL
* 參數
* 頭部
* 消息正文 (在?`POST`?請求里的)
下一章我們將通過學習 HTTP 響應來繼續了解 HTTP 。