小村軟體有限公司
第 1 章

導入 — 閱讀瀏覽器與伺服器的 HTTP 對話

確認 HTTP 在輸入 URL 之後所發生的事(DNS → TCP → TLS → HTTP)中的位置,掌握請求與回應原始文字的骨架,以及無狀態(stateless)的意義。

打開了 Network 分頁,卻不知道該看哪裡

頁面顯示不出來、API 不照預期運作 — 向前輩求助,得到的回答是「看一下開發者工具的 Network 分頁」。打開一看,一排排的行與數字並列著,不知道該從哪裡讀起。剛開始開發的時候,每個人都會經歷這個場面。

其實,那個畫面上映出的東西,本質非常樸素。瀏覽器與伺服器只是在一來一往地交換用文字寫成的信件而已。瀏覽器送出「請給我這個」的信(請求),伺服器回覆「好的,結果是這樣」的信(回應)。只要知道這往返書信的書寫格式,Network 分頁也好、curl 的輸出也好,都能用同一副骨架讀懂。

在本課程中,不管看什麼輸出,都確認相同的 3 點。

1. 請求了什麼
方法與路徑 — 閱讀請求行(本章與第 2 章)
2. 結果如何
狀態碼 — 閱讀回應第 1 行的 3 位數(第 3 章)
3. 條件與補充是什麼
標頭 — 閱讀格式・數量・條件等附註(第 4〜5 章)

在 HTTP 開始之前 — 輸入 URL 後發生的事

在瀏覽器輸入 https://example.com/ 並按下 Enter 之後,在 HTTP 的信件被送出之前,會層層疊上幾項準備。

Step 1 — DNS:查地址
example.com 這個名稱查出伺服器的 IP 位址。不知道收件地址就寄不出信
Step 2 — TCP:接通道路
與那個 IP 位址之間,建立能不遺漏地搬運位元組序列的連線。是把信件確實送達的郵政網
Step 3 — TLS:封上信封(https 的情況)
為了不在路徑上被讀取或竄改,建立加密的通訊通道。若是 http:// 則省略這一段,信件以明文流動
Step 4 — HTTP:寫信(本課程的主角)
在準備好的通訊通道上,終於送出「請給我這個」的文字信件

重要的是各層的分工。DNS 是地址組,TCP 是運輸組,TLS 是信封組,而 HTTP 就是信件的本文。即使變成 HTTPS,信件的書寫格式(本課程所學的一切)也不會改變 — 差別只在要不要裝進信封。

顯示請求(請求行・標頭・空行・內文)與回應(狀態行・標頭・空行・內文)擁有相同骨架的 HTTP 訊息結構圖

請求與回應只是方向相反,骨架同樣是「第 1 行 + 標頭 + 空行 + 內文」。只有第 1 行會依角色改變形態 — 提出請求的一方是請求行,回答的一方是狀態行。

試著閱讀原始文字

把實際的一來一往,不加工地直接看一看。首先是瀏覽器送出的請求。

GET /docs/index.html HTTP/1.1
Host: example.com
Accept: text/html

第 1 行是請求行。以空白分隔並列 GET(想做什麼 = 方法)、/docs/index.html(對哪一個對象 = 路徑)、HTTP/1.1(用哪個版本交談 = 版本)這 3 項。從第 2 行起是 名稱: 值 形式的標頭,而空行是「抬頭到此為止」的信號。GET 的請求只是提出請託,所以空行之後的內文是空的。

來自伺服器的回應,也以相同的骨架回來。

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 1256

<!DOCTYPE html>
<html>...

第 1 行是狀態行200 是給機器的 3 位數結果代碼,OK 是給人看的補充短語。接下來的標頭附上「是 HTML・共 1256 位元組」的補充,空行之後,被請求的東西(HTML 本文)就作為內文送達。

這裡是整個課程的地基:請求與回應都是「第 1 行 + 標頭 + 空行 + 內文」。讀第 1 行就知道「請求了什麼」「結果如何」,讀標頭就知道「條件與補充」。

練習 1-1 — 從輸入 URL 到 HTTP 的路程

確認 HTTP 開始之前發生了什麼,以及請求第 1 行的讀法。

Q1. 在瀏覽器輸入 https://example.com/ 之後,到 HTTP 請求被送出為止,發生的事情順序正確的是哪一個?

Q2. 請求的第 1 行 GET /docs/index.html HTTP/1.1 的正確讀法是哪一個?

Q3. 在 HTTP 的請求與回應中,把標頭的結尾與內文的開頭分隔開來的是哪一個?

無狀態 — 伺服器不記得「剛才的後續」

這套往返書信有一個最讓初學者驚訝的性質:伺服器原則上不記得前一個請求。就算第 1 封信看了商品頁、第 2 封信把商品放進購物車,對伺服器來說第 2 封仍是「來自陌生人的新信件」。這稱為無狀態(stateless,不持有狀態)。

看起來不方便,但這是設計上的抉擇。正因為每個請求各自完結,伺服器才能一封一封獨立處理,並且能分派給任意多台機器。世界級規模的服務得以成立,靠的就是這個取捨。

那麼,登入狀態這種「連續劇」要怎麼實現?答案是在每一封信上附上名牌 — 伺服器發行的識別標記(Cookie),由瀏覽器在之後的每個請求上寫上去。機制會在第 4 章閱讀,這裡請先帶著「在無狀態的往返書信之上,用附註創造狀態」這個構圖前進。

練習 1-2 — 回應的讀法與無狀態

確認回覆這一側的第 1 行,以及 HTTP 的重要性質「無狀態(stateless)」。

Q4. 回應的第 1 行 HTTP/1.1 200 OK 中的 200OK,正確的說明是哪一個?

Q5. HTTP 常被說是「無狀態(stateless)」。正確的說明是哪一個?

本章帶回家的重點

  • HTTP 是在 DNS → TCP → TLS 的準備之上交換的文字往返書信
  • 請求與回應的骨架相同:「第 1 行 + 標頭 + 空行 + 內文」。第 1 行是請求行(方法・路徑・版本)或狀態行(版本・3 位數代碼・短語)
  • HTTP 是無狀態的 — 每個請求都是獨立的一封信。「連續劇」要用 Cookie 等附註來製作
  • 讀法永遠是 3 點組合 — 請求了什麼・結果如何・條件與補充是什麼

下一章將深入 3 點組合的第 1 點「請求了什麼」。GET 與 POST 有什麼不同、哪些請託「可以再送一次」— 一起來閱讀方法的約定。