目錄
一、HTTP報(bào)文格式長(zhǎng)什么樣?是如何分割的?
請(qǐng)求行
狀態(tài)行
頭部字段
常用頭字段
小結(jié)
二、HTTP提供了哪些方法?GET和POST的區(qū)別是什么?
1.HTTP有哪些方法
2.GET/HEAD
3.PSOT/PUT
小結(jié)
三、URI和URL
URI 的格式
URI 的基本組成
URI 的查詢參數(shù)
小結(jié)
四、HTTP有哪些狀態(tài)碼?分別代表什么意思?
狀態(tài)碼
1××
2××
3××
4××
5××
小結(jié)
一、HTTP報(bào)文格式長(zhǎng)什么樣?是如何分割的?
HTTP 協(xié)議的請(qǐng)求報(bào)文和響應(yīng)報(bào)文的結(jié)構(gòu)基本相同,由三大部分組成:
- 起始行(start line):描述請(qǐng)求或響應(yīng)的基本信息;
- 頭部字段集合(header):使用 key-value 形式更詳細(xì)地說明報(bào)文;
- 消息正文(entity):實(shí)際傳輸?shù)臄?shù)據(jù),它不一定是純文本,可以是圖片、視頻等二進(jìn)制數(shù)據(jù)。
這其中前兩部分起始行和頭部字段經(jīng)常又合稱為“請(qǐng)求頭”或“響應(yīng)頭”,消息正文又稱為“實(shí)體”,但與“header”對(duì)應(yīng),很多時(shí)候就直接稱為“body”。
HTTP 協(xié)議規(guī)定報(bào)文必須有 header,但可以沒有 body,而且在 header 之后必須要有一個(gè)“空行”,也就是“CRLF”,十六進(jìn)制的“0D0A”。
所以,一個(gè)完整的 HTTP 報(bào)文就像是下圖的這個(gè)樣子,注意在 header 和 body 之間有一個(gè)“空行”。
看一下我們之前用 Wireshark 抓的包吧。
在這個(gè)瀏覽器發(fā)出的請(qǐng)求報(bào)文里,第一行“GET / HTTP/1.1”就是請(qǐng)求行,而后面的“Host”“Connection”等等都屬于 header,報(bào)文的最后是一個(gè)空白行結(jié)束,沒有 body。
在很多時(shí)候,特別是瀏覽器發(fā)送 GET 請(qǐng)求的時(shí)候都是這樣,HTTP 報(bào)文經(jīng)常是只有 header 而沒 body,相當(dāng)于只發(fā)了一個(gè)超級(jí)“大頭”過來,你可以想象的出來:每時(shí)每刻網(wǎng)絡(luò)上都會(huì)有數(shù)不清的“大頭兒子”在跑來跑去。
不過這個(gè)“大頭”也不能太大,雖然 HTTP 協(xié)議對(duì) header 的大小沒有做限制,但各個(gè) Web 服務(wù)器都不允許過大的請(qǐng)求頭,因?yàn)轭^部太大可能會(huì)占用大量的服務(wù)器資源,影響運(yùn)行效率。
請(qǐng)求行
了解了 HTTP 報(bào)文的基本結(jié)構(gòu)后,我們來看看請(qǐng)求報(bào)文里的起始行也就是請(qǐng)求行(request line),它簡(jiǎn)要地描述了客戶端想要如何操作服務(wù)器端的資源。
請(qǐng)求行由三部分構(gòu)成:
- 請(qǐng)求方法:是一個(gè)動(dòng)詞,如 GET/POST,表示對(duì)資源的操作;
- 請(qǐng)求目標(biāo):通常是一個(gè) URI,標(biāo)記了請(qǐng)求方法要操作的資源;
- 版本號(hào):表示報(bào)文使用的 HTTP 協(xié)議版本。
這三個(gè)部分通常使用空格(space)來分隔,最后要用 CRLF 換行表示結(jié)束。
還是用 Wireshark 抓包的數(shù)據(jù)來舉例:
GET / HTTP/1.1
在這個(gè)請(qǐng)求行里,“GET”是請(qǐng)求方法,“/”是請(qǐng)求目標(biāo),“HTTP/1.1”是版本號(hào),把這三部分連起來,意思就是“服務(wù)器你好,我想獲取網(wǎng)站根目錄下的默認(rèn)文件,我用的協(xié)議版本號(hào)是 1.1,請(qǐng)不要用 1.0 或者 2.0 回復(fù)我?!?/p>
狀態(tài)行
看完了請(qǐng)求行,我們?cè)倏错憫?yīng)報(bào)文里的起始行,在這里它不叫“響應(yīng)行”,而是叫“狀態(tài)行”(status line),意思是服務(wù)器響應(yīng)的狀態(tài)。
比起請(qǐng)求行來說,狀態(tài)行要簡(jiǎn)單一些,同樣也是由三部分構(gòu)成:
- 版本號(hào):表示報(bào)文使用的 HTTP 協(xié)議版本;
- 狀態(tài)碼:一個(gè)三位數(shù),用代碼的形式表示處理的結(jié)果,比如 200 是成功,500 是服務(wù)器錯(cuò)誤;
- 原因:作為數(shù)字狀態(tài)碼補(bǔ)充,是更詳細(xì)的解釋文字,幫助人理解原因。
頭部字段
請(qǐng)求行或狀態(tài)行再加上頭部字段集合就構(gòu)成了 HTTP 報(bào)文里完整的請(qǐng)求頭或響應(yīng)頭。
請(qǐng)求頭和響應(yīng)頭的結(jié)構(gòu)是基本一樣的,唯一的區(qū)別是起始行,所以我把請(qǐng)求頭和響應(yīng)頭里的字段放在一起介紹。
頭部字段是 key-value 的形式,key 和 value 之間用“:”分隔,最后用 CRLF 換行表示字段結(jié)束。比如在“Host: 127.0.0.1”這一行里 key 就是“Host”,value 就是“127.0.0.1”。
HTTP 頭字段非常靈活,不僅可以使用標(biāo)準(zhǔn)里的 Host、Connection 等已有頭,也可以任意添加自定義頭,這就給 HTTP 協(xié)議帶來了無限的擴(kuò)展可能。
不過使用頭字段需要注意下面幾點(diǎn):
- 字段名不區(qū)分大小寫,例如“Host”也可以寫成“host”,但首字母大寫的可讀性更好;
- 字段名里不允許出現(xiàn)空格,可以使用連字符“-”,但不能使用下劃線“_”。例如,“test-name”是合法的字段名,而“test name”“test_name”是不正確的字段名;
- 字段名后面必須緊接著“:”,不能有空格,而“:”后的字段值前可以有多個(gè)空格;
- 字段的順序是沒有意義的,可以任意排列不影響語義;
- 字段原則上不能重復(fù),除非這個(gè)字段本身的語義允許,例如 Set-Cookie。
常用頭字段
HTTP 協(xié)議規(guī)定了非常多的頭部字段,實(shí)現(xiàn)各種各樣的功能,但基本上可以分為四大類:
- 通用字段:在請(qǐng)求頭和響應(yīng)頭里都可以出現(xiàn);
- 請(qǐng)求字段:僅能出現(xiàn)在請(qǐng)求頭里,進(jìn)一步說明請(qǐng)求信息或者額外的附加條件;
- 響應(yīng)字段:僅能出現(xiàn)在響應(yīng)頭里,補(bǔ)充說明響應(yīng)報(bào)文的信息;
- 實(shí)體字段:它實(shí)際上屬于通用字段,但專門描述 body 的額外信息。
對(duì) HTTP 報(bào)文的解析和處理實(shí)際上主要就是對(duì)頭字段的處理,理解了頭字段也就理解了 HTTP 報(bào)文。
首先要說的是Host字段,它屬于請(qǐng)求字段,只能出現(xiàn)在請(qǐng)求頭里,它同時(shí)也是唯一一個(gè) HTTP/1.1 規(guī)范里要求必須出現(xiàn)的字段,也就是說,如果請(qǐng)求頭里沒有 Host,那這就是一個(gè)錯(cuò)誤的報(bào)文。
Host 字段告訴服務(wù)器這個(gè)請(qǐng)求應(yīng)該由哪個(gè)主機(jī)來處理,當(dāng)一臺(tái)計(jì)算機(jī)上托管了多個(gè)虛擬主機(jī)的時(shí)候,服務(wù)器端就需要用 Host 字段來選擇,有點(diǎn)像是一個(gè)簡(jiǎn)單的“路由重定向”。
例如我們的試驗(yàn)環(huán)境,在 127.0.0.1 上有三個(gè)虛擬主機(jī):“www.chrono.com”“www.metroid.net”和“origin.io”。那么當(dāng)使用域名的方式訪問時(shí),就必須要用 Host 字段來區(qū)分這三個(gè) IP 相同但域名不同的網(wǎng)站,否則服務(wù)器就會(huì)找不到合適的虛擬主機(jī),無法處理。
User-Agent是請(qǐng)求字段,只出現(xiàn)在請(qǐng)求頭里。它使用一個(gè)字符串來描述發(fā)起 HTTP 請(qǐng)求的客戶端,服務(wù)器可以依據(jù)它來返回最合適此瀏覽器顯示的頁面。
但由于歷史的原因,User-Agent 非常混亂,每個(gè)瀏覽器都自稱是“Mozilla”“Chrome”“Safari”,企圖使用這個(gè)字段來互相“偽裝”,導(dǎo)致 User-Agent 變得越來越長(zhǎng),最終變得毫無意義。
不過有的比較“誠(chéng)實(shí)”的爬蟲會(huì)在 User-Agent 里用“spider”標(biāo)明自己是爬蟲,所以可以利用這個(gè)字段實(shí)現(xiàn)簡(jiǎn)單的反爬蟲策略。
Date字段是一個(gè)通用字段,但通常出現(xiàn)在響應(yīng)頭里,表示 HTTP 報(bào)文創(chuàng)建的時(shí)間,客戶端可以使用這個(gè)時(shí)間再搭配其他字段決定緩存策略。
Server字段是響應(yīng)字段,只能出現(xiàn)在響應(yīng)頭里。它告訴客戶端當(dāng)前正在提供 Web 服務(wù)的軟件名稱和版本號(hào),例如在我們的實(shí)驗(yàn)環(huán)境里它就是“Server: openresty/1.15.8.1”,即使用的是 OpenResty 1.15.8.1。
Server 字段也不是必須要出現(xiàn)的,因?yàn)檫@會(huì)把服務(wù)器的一部分信息暴露給外界,如果這個(gè)版本恰好存在 bug,那么黑客就有可能利用 bug 攻陷服務(wù)器。所以,有的網(wǎng)站響應(yīng)頭里要么沒有這個(gè)字段,要么就給出一個(gè)完全無關(guān)的描述信息。
小結(jié)
- HTTP 報(bào)文結(jié)構(gòu)就像是“大頭兒子”,由“起始行 + 頭部 + 空行 + 實(shí)體”組成,簡(jiǎn)單地說就是“header+body”;
- HTTP 報(bào)文可以沒有 body,但必須要有 header,而且 header 后也必須要有空行,形象地說就是“大頭”必須要帶著“脖子”;
- 請(qǐng)求頭由“請(qǐng)求行 + 頭部字段”構(gòu)成,響應(yīng)頭由“狀態(tài)行 + 頭部字段”構(gòu)成;
- 請(qǐng)求行有三部分:請(qǐng)求方法,請(qǐng)求目標(biāo)和版本號(hào);
- 狀態(tài)行也有三部分:版本號(hào),狀態(tài)碼和原因字符串;
- 頭部字段是 key-value 的形式,用“:”分隔,不區(qū)分大小寫,順序任意,除了規(guī)定的標(biāo)準(zhǔn)頭,也可以任意添加自定義字段,實(shí)現(xiàn)功能擴(kuò)展;
- HTTP/1.1 里唯一要求必須提供的頭字段是 Host,它必須出現(xiàn)在請(qǐng)求頭里,標(biāo)記虛擬主機(jī)名。
二、HTTP提供了哪些方法?GET和POST的區(qū)別是什么?
1.HTTP有哪些方法
目前 HTTP/1.1 規(guī)定了八種方法,單詞都必須是大寫的形式,我先簡(jiǎn)單地列把它們列出來,后面再詳細(xì)講解。
- GET:獲取資源,可以理解為讀取或者下載數(shù)據(jù);
- HEAD:獲取資源的元信息;
- POST:向資源提交數(shù)據(jù),相當(dāng)于寫入或上傳數(shù)據(jù);
- PUT:類似 POST;
- DELETE:刪除資源;
- CONNECT:建立特殊的連接隧道;
- OPTIONS:列出可對(duì)資源實(shí)行的方法;
- TRACE:追蹤請(qǐng)求 - 響應(yīng)的傳輸路徑。
2.GET/HEAD
GET方法應(yīng)該是 HTTP 協(xié)議里最知名的請(qǐng)求方法了,也應(yīng)該是用的最多的,自 0.9 版出現(xiàn)并一直被保留至今,是名副其實(shí)的“元老”。
它的含義是請(qǐng)求從服務(wù)器獲取資源,這個(gè)資源既可以是靜態(tài)的文本、頁面、圖片、視頻,也可以是由 PHP、Java 動(dòng)態(tài)生成的頁面或者其他格式的數(shù)據(jù)。
GET 方法雖然基本動(dòng)作比較簡(jiǎn)單,但搭配 URI 和其他頭字段就能實(shí)現(xiàn)對(duì)資源更精細(xì)的操作。
例如,在 URI 后使用“#”,就可以在獲取頁面后直接定位到某個(gè)標(biāo)簽所在的位置;使用 If-Modified-Since 字段就變成了“有條件的請(qǐng)求”,僅當(dāng)資源被修改時(shí)才會(huì)執(zhí)行獲取動(dòng)作;使用 Range 字段就是“范圍請(qǐng)求”,只獲取資源的一部分?jǐn)?shù)據(jù)。
HEAD方法與 GET 方法類似,也是請(qǐng)求從服務(wù)器獲取資源,服務(wù)器的處理機(jī)制也是一樣的,但服務(wù)器不會(huì)返回請(qǐng)求的實(shí)體數(shù)據(jù),只會(huì)傳回響應(yīng)頭,也就是資源的“元信息”。
HEAD 方法可以看做是 GET 方法的一個(gè)“簡(jiǎn)化版”或者“輕量版”。因?yàn)樗捻憫?yīng)頭與 GET 完全相同,所以可以用在很多并不真正需要資源的場(chǎng)合,避免傳輸 body 數(shù)據(jù)的浪費(fèi)。
比如,想要檢查一個(gè)文件是否存在,只要發(fā)個(gè) HEAD 請(qǐng)求就可以了,沒有必要用 GET 把整個(gè)文件都取下來。再比如,要檢查文件是否有最新版本,同樣也應(yīng)該用 HEAD,服務(wù)器會(huì)在響應(yīng)頭里把文件的修改時(shí)間傳回來。
3.PSOT/PUT
GET 和 HEAD 方法是從服務(wù)器獲取數(shù)據(jù),而 POST 和 PUT 方法則是相反操作,向 URI 指定的資源提交數(shù)據(jù),數(shù)據(jù)就放在報(bào)文的 body 里。
POST 也是一個(gè)經(jīng)常用到的請(qǐng)求方法,使用頻率應(yīng)該是僅次于 GET,應(yīng)用的場(chǎng)景也非常多,只要向服務(wù)器發(fā)送數(shù)據(jù),用的大多數(shù)都是 POST。
比如,你上論壇灌水,敲了一堆字后點(diǎn)擊“發(fā)帖”按鈕,瀏覽器就執(zhí)行了一次 POST 請(qǐng)求,把你的文字放進(jìn)報(bào)文的 body 里,然后拼好 POST 請(qǐng)求頭,通過 TCP 協(xié)議發(fā)給服務(wù)器。
又比如,你上購(gòu)物網(wǎng)站,看到了一件心儀的商品,點(diǎn)擊“加入購(gòu)物車”,這時(shí)也會(huì)有 POST 請(qǐng)求,瀏覽器會(huì)把商品 ID 發(fā)給服務(wù)器,服務(wù)器再把 ID 寫入你的購(gòu)物車相關(guān)的數(shù)據(jù)庫記錄。
PUT 的作用與 POST 類似,也可以向服務(wù)器提交數(shù)據(jù),但與 POST 存在微妙的不同,通常 POST 表示的是“新建”“create”的含義,而 PUT 則是“修改”“update”的含義。
在實(shí)際應(yīng)用中,PUT 用到的比較少。而且,因?yàn)樗c POST 的語義、功能太過近似,有的服務(wù)器甚至就直接禁止使用 PUT 方法,只用 POST 方法上傳數(shù)據(jù)。
小結(jié)
- 請(qǐng)求方法是客戶端發(fā)出的、要求服務(wù)器執(zhí)行的、對(duì)資源的一種操作;
- 請(qǐng)求方法是對(duì)服務(wù)器的“指示”,真正應(yīng)如何處理由服務(wù)器來決定;
- 最常用的請(qǐng)求方法是 GET 和 POST,分別是獲取數(shù)據(jù)和發(fā)送數(shù)據(jù);
- HEAD 方法是輕量級(jí)的 GET,用來獲取資源的元信息;
- PUT 基本上是 POST 的同義詞,多用于更新數(shù)據(jù);
- “安全”與“冪等”是描述請(qǐng)求方法的兩個(gè)重要屬性,具有理論指導(dǎo)意義,可以幫助我們?cè)O(shè)計(jì)系統(tǒng)。
三、URI和URL
URI,也就是統(tǒng)一資源標(biāo)識(shí)符(Uniform?Resource?Identifier)。因?yàn)樗?jīng)常出現(xiàn)在瀏覽器的地址欄里,所以俗稱為“網(wǎng)絡(luò)地址”,簡(jiǎn)稱“網(wǎng)址”。
嚴(yán)格地說,URI 不完全等同于網(wǎng)址,它包含有 URL 和 URN 兩個(gè)部分,在 HTTP 世界里用的網(wǎng)址實(shí)際上是 URL——統(tǒng)一資源定位符(Uniform?Resource?Locator)。但因?yàn)?URL 實(shí)在是太普及了,所以常常把這兩者簡(jiǎn)單地視為相等。
URI 的格式
URI 本質(zhì)上是一個(gè)字符串,這個(gè)字符串的作用是唯一地標(biāo)記資源的位置或者名字。
這里我要提醒你注意,它不僅能夠標(biāo)記萬維網(wǎng)的資源,也可以標(biāo)記其他的,如郵件系統(tǒng)、本地文件系統(tǒng)等任意資源。而“資源”既可以是存在磁盤上的靜態(tài)文本、頁面數(shù)據(jù),也可以是由 Java、PHP 提供的動(dòng)態(tài)服務(wù)。
下面的這張圖顯示了 URI 最常用的形式,由 scheme、host:port、path 和 query 四個(gè)部分組成,但有的部分可以視情況省略。
URI 的基本組成
URI 第一個(gè)組成部分叫scheme,翻譯成中文叫“方案名”或者“協(xié)議名”,表示資源應(yīng)該使用哪種協(xié)議來訪問。
最常見的當(dāng)然就是“http”了,表示使用 HTTP 協(xié)議。另外還有“https”,表示使用經(jīng)過加密、安全的 HTTPS 協(xié)議。此外還有其他不是很常見的 scheme,例如 ftp、ldap、file、news 等。
瀏覽器或者你的應(yīng)用程序看到 URI 里的 scheme,就知道下一步該怎么走了,會(huì)調(diào)用相應(yīng)的 HTTP 或者 HTTPS 下層 API。顯然,如果一個(gè) URI 沒有提供 scheme,即使后面的地址再完善,也是無法處理的。
在 scheme 之后,必須是三個(gè)特定的字符“://”,它把 scheme 和后面的部分分離開。
實(shí)話實(shí)說,這個(gè)設(shè)計(jì)非常的怪異,我最早上網(wǎng)的時(shí)候看見地址欄里的“://”就覺得很別扭,直到現(xiàn)在也還是沒有太適應(yīng)。URI 的創(chuàng)造者蒂姆·伯納斯 - 李也曾經(jīng)私下承認(rèn)“://”并非必要,當(dāng)初有些“過于草率”了。
不過這個(gè)設(shè)計(jì)已經(jīng)有了三十年的歷史,不管我們?cè)敢獠辉敢?,只能接受?/p>
在“://”之后,是被稱為“authority”的部分,表示資源所在的主機(jī)名,通常的形式是“host:port”,即主機(jī)名加端口號(hào)。
主機(jī)名可以是 IP 地址或者域名的形式,必須要有,否則瀏覽器就會(huì)找不到服務(wù)器。但端口號(hào)有時(shí)可以省略,瀏覽器等客戶端會(huì)依據(jù) scheme 使用默認(rèn)的端口號(hào),例如 HTTP 的默認(rèn)端口號(hào)是 80,HTTPS 的默認(rèn)端口號(hào)是 443。
有了協(xié)議名和主機(jī)地址、端口號(hào),再加上后面標(biāo)記資源所在位置的path,瀏覽器就可以連接服務(wù)器訪問資源了。
URI 里 path 采用了類似文件系統(tǒng)“目錄”“路徑”的表示方式,因?yàn)樵缙诨ヂ?lián)網(wǎng)上的計(jì)算機(jī)多是 UNIX 系統(tǒng),所以采用了 UNIX 的“/”風(fēng)格。其實(shí)也比較好理解,它與 scheme 后面的“://”是一致的。
這里我也要再次提醒你注意,URI 的 path 部分必須以“/”開始,也就是必須包含“/”,不要把“/”誤認(rèn)為屬于前面 authority。
第一個(gè) URI 算是最簡(jiǎn)單的了,協(xié)議名是“http”,主機(jī)名是“nginx.org”,端口號(hào)省略,所以是默認(rèn)的 80,而路徑部分也被省略了,默認(rèn)就是一個(gè)“/”,表示根目錄。
第二個(gè) URI 是在實(shí)驗(yàn)環(huán)境里這次課程的專用 URI,主機(jī)名是“www.chrono.com”,端口號(hào)是 8080,后面的路徑是“/11-1”。
第三個(gè)是 HTTP 協(xié)議標(biāo)準(zhǔn)文檔 RFC7230 的 URI,主機(jī)名是“tools.ietf.org”,路徑是“/html/rfc7230”。
最后一個(gè) URI 要注意了,它的協(xié)議名不是“http”,而是“file”,表示這是本地文件,而后面居然有三個(gè)斜杠,這是怎么回事?
如果你剛才仔細(xì)聽了 scheme 的介紹就能明白,這三個(gè)斜杠里的前兩個(gè)屬于 URI 特殊分隔符“://”,然后后面的“/D:/http_study/www/”是路徑,而中間的主機(jī)名被“省略”了。這實(shí)際上是 file 類型 URI 的“特例”,它允許省略主機(jī)名,默認(rèn)是本機(jī) localhost。
URI 的查詢參數(shù)
使用“協(xié)議名 + 主機(jī)名 + 路徑”的方式,已經(jīng)可以精確定位網(wǎng)絡(luò)上的任何資源了。但這還不夠,很多時(shí)候我們還想在操作資源的時(shí)候附加一些額外的修飾參數(shù)。
舉幾個(gè)例子:獲取商品圖片,但想要一個(gè) 32×32 的縮略圖版本;獲取商品列表,但要按某種規(guī)則做分頁和排序;跳轉(zhuǎn)頁面,但想要標(biāo)記跳轉(zhuǎn)前的原始頁面。
僅用“協(xié)議名 + 主機(jī)名 + 路徑”的方式是無法適應(yīng)這些場(chǎng)景的,所以 URI 后面還有一個(gè)“query”部分,它在 path 之后,用一個(gè)“?”開始,但不包含“?”,表示對(duì)資源附加的額外要求。這是個(gè)很形象的符號(hào),比“://”要好的多,很明顯地表示了“查詢”的含義。
查詢參數(shù) query 有一套自己的格式,是多個(gè)“key=value”的字符串,這些 KV 值用字符“&”連接,瀏覽器和客戶端都可以按照這個(gè)格式把長(zhǎng)串的查詢參數(shù)解析成可理解的字典或關(guān)聯(lián)數(shù)組形式。
小結(jié)
- URI 是用來唯一標(biāo)記服務(wù)器上資源的一個(gè)字符串,通常也稱為 URL;
- URI 通常由 scheme、host:port、path 和 query 四個(gè)部分組成,有的可以省略;
- scheme 叫“方案名”或者“協(xié)議名”,表示資源應(yīng)該使用哪種協(xié)議來訪問;
- “host:port”表示資源所在的主機(jī)名和端口號(hào);
- path 標(biāo)記資源所在的位置;
- query 表示對(duì)資源附加的額外要求;
- 在 URI 里對(duì)“@&/”等特殊字符和漢字必須要做編碼,否則服務(wù)器收到 HTTP 報(bào)文后會(huì)無法正確處理。
四、HTTP有哪些狀態(tài)碼?分別代表什么意思?
?HTTP 報(bào)文里請(qǐng)求行的組成部分,包括請(qǐng)求方法和 URI。有了請(qǐng)求行,加上后面的頭字段就形成了請(qǐng)求頭,可以通過 TCP/IP 協(xié)議發(fā)送給服務(wù)器。
服務(wù)器收到請(qǐng)求報(bào)文,解析后需要進(jìn)行處理,具體的業(yè)務(wù)邏輯多種多樣,但最后必定是拼出一個(gè)響應(yīng)報(bào)文發(fā)回客戶端。
響應(yīng)報(bào)文由響應(yīng)頭加響應(yīng)體數(shù)據(jù)組成,響應(yīng)頭又由狀態(tài)行和頭字段構(gòu)成。
我們先來復(fù)習(xí)一下狀態(tài)行的結(jié)構(gòu),有三部分:
開頭的 Version 部分是 HTTP 協(xié)議的版本號(hào),通常是 HTTP/1.1,用處不是很大。
后面的 Reason 部分是原因短語,是狀態(tài)碼的簡(jiǎn)短文字描述,例如“OK”“Not Found”等等,也可以自定義。但它只是為了兼容早期的文本客戶端而存在,提供的信息很有限,目前的大多數(shù)客戶端都會(huì)忽略它。
所以,狀態(tài)行里有用的就只剩下中間的狀態(tài)碼(Status Code)了。它是一個(gè)十進(jìn)制數(shù)字,以代碼的形式表示服務(wù)器對(duì)請(qǐng)求的處理結(jié)果,就像我們通常編寫程序時(shí)函數(shù)返回的錯(cuò)誤碼一樣。
不過你要注意,它的名字是“狀態(tài)碼”而不是“錯(cuò)誤碼”。也就是說,它的含義不僅是錯(cuò)誤,更重要的意義在于表達(dá) HTTP 數(shù)據(jù)處理的“狀態(tài)”,客戶端可以依據(jù)代碼適時(shí)轉(zhuǎn)換處理狀態(tài),例如繼續(xù)發(fā)送請(qǐng)求、切換協(xié)議,重定向跳轉(zhuǎn)等,有那么點(diǎn) TCP 狀態(tài)轉(zhuǎn)換的意思。
狀態(tài)碼
目前 RFC 標(biāo)準(zhǔn)里規(guī)定的狀態(tài)碼是三位數(shù),所以取值范圍就是從 000 到 999。但如果把代碼簡(jiǎn)單地從 000 開始順序編下去就顯得有點(diǎn)太“l(fā)ow”,不靈活、不利于擴(kuò)展,所以狀態(tài)碼也被設(shè)計(jì)成有一定的格式。
RFC 標(biāo)準(zhǔn)把狀態(tài)碼分成了五類,用數(shù)字的第一位表示分類,而 0~99 不用,這樣狀態(tài)碼的實(shí)際可用范圍就大大縮小了,由 000~999 變成了 100~599。
這五類的具體含義是:
- 1××:提示信息,表示目前是協(xié)議處理的中間狀態(tài),還需要后續(xù)的操作;
- 2××:成功,報(bào)文已經(jīng)收到并被正確處理;
- 3××:重定向,資源位置發(fā)生變動(dòng),需要客戶端重新發(fā)送請(qǐng)求;
- 4××:客戶端錯(cuò)誤,請(qǐng)求報(bào)文有誤,服務(wù)器無法處理;
- 5××:服務(wù)器錯(cuò)誤,服務(wù)器在處理請(qǐng)求時(shí)內(nèi)部發(fā)生了錯(cuò)誤。
在 HTTP 協(xié)議中,正確地理解并應(yīng)用這些狀態(tài)碼不是客戶端或服務(wù)器單方的責(zé)任,而是雙方共同的責(zé)任。
1××
1××類狀態(tài)碼屬于提示信息,是協(xié)議處理的中間狀態(tài),實(shí)際能夠用到的時(shí)候很少。
我們偶爾能夠見到的是“101 Switching Protocols”。它的意思是客戶端使用 Upgrade 頭字段,要求在 HTTP 協(xié)議的基礎(chǔ)上改成其他的協(xié)議繼續(xù)通信,比如 WebSocket。而如果服務(wù)器也同意變更協(xié)議,就會(huì)發(fā)送狀態(tài)碼 101,但這之后的數(shù)據(jù)傳輸就不會(huì)再使用 HTTP 了。
2××
2××類狀態(tài)碼表示服務(wù)器收到并成功處理了客戶端的請(qǐng)求,這也是客戶端最愿意看到的狀態(tài)碼。
“200 OK”是最常見的成功狀態(tài)碼,表示一切正常,服務(wù)器如客戶端所期望的那樣返回了處理結(jié)果,如果是非 HEAD 請(qǐng)求,通常在響應(yīng)頭后都會(huì)有 body 數(shù)據(jù)。
“204 No Content”是另一個(gè)很常見的成功狀態(tài)碼,它的含義與“200 OK”基本相同,但響應(yīng)頭后沒有 body 數(shù)據(jù)。所以對(duì)于 Web 服務(wù)器來說,正確地區(qū)分 200 和 204 是很必要的。
“206 Partial Content”是 HTTP 分塊下載或斷點(diǎn)續(xù)傳的基礎(chǔ),在客戶端發(fā)送“范圍請(qǐng)求”、要求獲取資源的部分?jǐn)?shù)據(jù)時(shí)出現(xiàn),它與 200 一樣,也是服務(wù)器成功處理了請(qǐng)求,但 body 里的數(shù)據(jù)不是資源的全部,而是其中的一部分。
狀態(tài)碼 206 通常還會(huì)伴隨著頭字段“Content-Range”,表示響應(yīng)報(bào)文里 body 數(shù)據(jù)的具體范圍,供客戶端確認(rèn),例如“Content-Range: bytes 0-99/2000”,意思是此次獲取的是總計(jì) 2000 個(gè)字節(jié)的前 100 個(gè)字節(jié)。
3××
3××類狀態(tài)碼表示客戶端請(qǐng)求的資源發(fā)生了變動(dòng),客戶端必須用新的 URI 重新發(fā)送請(qǐng)求獲取資源,也就是通常所說的“重定向”,包括著名的 301、302 跳轉(zhuǎn)。
“301 Moved Permanently”俗稱“永久重定向”,含義是此次請(qǐng)求的資源已經(jīng)不存在了,需要改用改用新的 URI 再次訪問。
與它類似的是“302 Found”,曾經(jīng)的描述短語是“Moved Temporarily”,俗稱“臨時(shí)重定向”,意思是請(qǐng)求的資源還在,但需要暫時(shí)用另一個(gè) URI 來訪問。
301 和 302 都會(huì)在響應(yīng)頭里使用字段Location指明后續(xù)要跳轉(zhuǎn)的 URI,最終的效果很相似,瀏覽器都會(huì)重定向到新的 URI。兩者的根本區(qū)別在于語義,一個(gè)是“永久”,一個(gè)是“臨時(shí)”,所以在場(chǎng)景、用法上差距很大。
比如,你的網(wǎng)站升級(jí)到了 HTTPS,原來的 HTTP 不打算用了,這就是“永久”的,所以要配置 301 跳轉(zhuǎn),把所有的 HTTP 流量都切換到 HTTPS。
再比如,今天夜里網(wǎng)站后臺(tái)要系統(tǒng)維護(hù),服務(wù)暫時(shí)不可用,這就屬于“臨時(shí)”的,可以配置成 302 跳轉(zhuǎn),把流量臨時(shí)切換到一個(gè)靜態(tài)通知頁面,瀏覽器看到這個(gè) 302 就知道這只是暫時(shí)的情況,不會(huì)做緩存優(yōu)化,第二天還會(huì)訪問原來的地址。
“304 Not Modified” 是一個(gè)比較有意思的狀態(tài)碼,它用于 If-Modified-Since 等條件請(qǐng)求,表示資源未修改,用于緩存控制。它不具有通常的跳轉(zhuǎn)含義,但可以理解成“重定向已到緩存的文件”(即“緩存重定向”)。
301、302 和 304 分別涉及了 HTTP 協(xié)議里重要的“重定向跳轉(zhuǎn)”和“緩存控制”,在之后的課程中我還會(huì)細(xì)講。
4××
4××類狀態(tài)碼表示客戶端發(fā)送的請(qǐng)求報(bào)文有誤,服務(wù)器無法處理,它就是真正的“錯(cuò)誤碼”含義了。
“400 Bad Request”是一個(gè)通用的錯(cuò)誤碼,表示請(qǐng)求報(bào)文有錯(cuò)誤,但具體是數(shù)據(jù)格式錯(cuò)誤、缺少請(qǐng)求頭還是 URI 超長(zhǎng)它沒有明確說,只是一個(gè)籠統(tǒng)的錯(cuò)誤,客戶端看到 400 只會(huì)是“一頭霧水”“不知所措”。所以,在開發(fā) Web 應(yīng)用時(shí)應(yīng)當(dāng)盡量避免給客戶端返回 400,而是要用其他更有明確含義的狀態(tài)碼。
“403 Forbidden”實(shí)際上不是客戶端的請(qǐng)求出錯(cuò),而是表示服務(wù)器禁止訪問資源。原因可能多種多樣,例如信息敏感、法律禁止等,如果服務(wù)器友好一點(diǎn),可以在 body 里詳細(xì)說明拒絕請(qǐng)求的原因,不過現(xiàn)實(shí)中通常都是直接給一個(gè)“閉門羹”。
“404 Not Found”可能是我們最常看見也是最不愿意看到的一個(gè)狀態(tài)碼,它的原意是資源在本服務(wù)器上未找到,所以無法提供給客戶端。但現(xiàn)在已經(jīng)被“用濫了”,只要服務(wù)器“不高興”就可以給出個(gè) 404,而我們也無從得知后面到底是真的未找到,還是有什么別的原因,某種程度上它比 403 還要令人討厭。
4××里剩下的一些代碼較明確地說明了錯(cuò)誤的原因,都很好理解,開發(fā)中常用的有:
- 405 Method Not Allowed:不允許使用某些方法操作資源,例如不允許 POST 只能 GET;
- 406 Not Acceptable:資源無法滿足客戶端請(qǐng)求的條件,例如請(qǐng)求中文但只有英文;
- 408 Request Timeout:請(qǐng)求超時(shí),服務(wù)器等待了過長(zhǎng)的時(shí)間;
- 409 Conflict:多個(gè)請(qǐng)求發(fā)生了沖突,可以理解為多線程并發(fā)時(shí)的競(jìng)態(tài);
- 413 Request Entity Too Large:請(qǐng)求報(bào)文里的 body 太大;
- 414 Request-URI Too Long:請(qǐng)求行里的 URI 太大;
- 429 Too Many Requests:客戶端發(fā)送了太多的請(qǐng)求,通常是由于服務(wù)器的限連策略;
- 431 Request Header Fields Too Large:請(qǐng)求頭某個(gè)字段或總體太大;
5××
5××類狀態(tài)碼表示客戶端請(qǐng)求報(bào)文正確,但服務(wù)器在處理時(shí)內(nèi)部發(fā)生了錯(cuò)誤,無法返回應(yīng)有的響應(yīng)數(shù)據(jù),是服務(wù)器端的“錯(cuò)誤碼”。
“500 Internal Server Error”與 400 類似,也是一個(gè)通用的錯(cuò)誤碼,服務(wù)器究竟發(fā)生了什么錯(cuò)誤我們是不知道的。不過對(duì)于服務(wù)器來說這應(yīng)該算是好事,通常不應(yīng)該把服務(wù)器內(nèi)部的詳細(xì)信息,例如出錯(cuò)的函數(shù)調(diào)用棧告訴外界。雖然不利于調(diào)試,但能夠防止黑客的窺探或者分析。
“501 Not Implemented”表示客戶端請(qǐng)求的功能還不支持,這個(gè)錯(cuò)誤碼比 500 要“溫和”一些,和“即將開業(yè),敬請(qǐng)期待”的意思差不多,不過具體什么時(shí)候“開業(yè)”就不好說了。
“502 Bad Gateway”通常是服務(wù)器作為網(wǎng)關(guān)或者代理時(shí)返回的錯(cuò)誤碼,表示服務(wù)器自身工作正常,訪問后端服務(wù)器時(shí)發(fā)生了錯(cuò)誤,但具體的錯(cuò)誤原因也是不知道的。
“503 Service Unavailable”表示服務(wù)器當(dāng)前很忙,暫時(shí)無法響應(yīng)服務(wù),我們上網(wǎng)時(shí)有時(shí)候遇到的“網(wǎng)絡(luò)服務(wù)正忙,請(qǐng)稍后重試”的提示信息就是狀態(tài)碼 503。文章來源:http://www.zghlxwxcb.cn/news/detail-856059.html
503 是一個(gè)“臨時(shí)”的狀態(tài),很可能過幾秒鐘后服務(wù)器就不那么忙了,可以繼續(xù)提供服務(wù),所以 503 響應(yīng)報(bào)文里通常還會(huì)有一個(gè)“Retry-After”字段,指示客戶端可以在多久以后再次嘗試發(fā)送請(qǐng)求。文章來源地址http://www.zghlxwxcb.cn/news/detail-856059.html
小結(jié)
- 狀態(tài)碼在響應(yīng)報(bào)文里表示了服務(wù)器對(duì)請(qǐng)求的處理結(jié)果;
- 狀態(tài)碼后的原因短語是簡(jiǎn)單的文字描述,可以自定義;
- 狀態(tài)碼是十進(jìn)制的三位數(shù),分為五類,從 100 到 599;
- 2××類狀態(tài)碼表示成功,常用的有 200、204、206;
- 3××類狀態(tài)碼表示重定向,常用的有 301、302、304;
- 4××類狀態(tài)碼表示客戶端錯(cuò)誤,常用的有 400、403、404;
- 5××類狀態(tài)碼表示服務(wù)器錯(cuò)誤,常用的有 500、501、502、503。
到了這里,關(guān)于計(jì)算機(jī)網(wǎng)絡(luò)學(xué)習(xí)day02|HTTP協(xié)議的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!