作者:starok
原文地址:http://www.cnblogs.com/starstone/p/4890409.html
HTTP協(xié)議作為網(wǎng)絡(luò)傳輸?shù)幕緟f(xié)議踱蛀,有著廣泛的應(yīng)用。HTTP協(xié)議的完整內(nèi)容很多拳缠,但是其核心知識卻又簡單精煉。學(xué)習(xí)者應(yīng)該掌握其基本結(jié)構(gòu)牍白,并且能夠舉一反三脊凰。這篇文章所列的,就是在實際開發(fā)中必須知道必須掌握的HTTP知識茂腥。
HTTP協(xié)議
HTTP協(xié)議:消息的分類
HTTP消息(有的文章稱之為報文)分為請求消息和響應(yīng)消息兩種基本分類狸涌。其中請求消息是客戶端發(fā)送給服務(wù)器的用于請求服務(wù)和資源的消息,響應(yīng)消息是服務(wù)器對請求消息的應(yīng)答最岗。一般來說帕胆,一個響應(yīng)對應(yīng)一個請求,不多也不少般渡。
HTTP協(xié)議:特點
HTTP協(xié)議被人總結(jié)為無連接
懒豹、無狀態(tài)
的特點:
- 無連接:無連接的含義是限制每次連接只處理一個請求。服務(wù)器處理完客戶的請求驯用,并收到客戶的應(yīng)答后脸秽,即斷開連接。采用這種方式可以節(jié)省傳輸時間蝴乔。
- 無狀態(tài):HTTP協(xié)議是無狀態(tài)協(xié)議记餐。無狀態(tài)是指協(xié)議對于事務(wù)處理沒有記憶能力。缺少狀態(tài)意味著如果后續(xù)處理需要前面的信息薇正,則它必須重傳片酝,這樣可能導(dǎo)致每次連接傳送的數(shù)據(jù)量增大。另一方面挖腰,在服務(wù)器不需要先前信息時它的應(yīng)答就較快雕沿。
HTTP協(xié)議:消息的基本格式
HTTP協(xié)議的請求消息和響應(yīng)消息的格式及其相似。提煉出它們的共性猴仑,可以指出审轮,HTTP消息分為三個部分:
- 首行
- 頭部(Header)
- 正文(Body)
其中,頭部用來指出HTTP消息的一些屬性,它們有固定的格式断国;正文部分是傳輸?shù)膶嶋H內(nèi)容贤姆,它們的格式是任意的,通常用Content-Type頭來指定稳衬。首行在請求消息和響應(yīng)消息中具體格式略有區(qū)別霞捡,它們表示的按理說應(yīng)該是HTTP消息最基本的部分。不論是HTTP請求還是HTTP響應(yīng)薄疚,首行都是有的碧信,否則會出現(xiàn)不可饒恕的解析錯誤;然而頭部和正文是可選的街夭,不過實際過程中砰碴,多多少少都要包含一些基本的頭。
HTTP消息主要是基于ASCII編碼的消息實體板丽。主要的意思是指首行和頭部都是以ASCII編碼呈枉,而正文部分的編碼就顯得任意了。在實際的開發(fā)中埃碱,發(fā)送的文本消息時常會碰到亂碼的問題猖辫。一種解決辦法是,對于文本消息砚殿,約定以UTF-8格式進行編碼和解碼啃憎。
知道的人也許知道,HTTP消息是基于TCP協(xié)議的上層應(yīng)用協(xié)議辛萍。TCP協(xié)議是網(wǎng)絡(luò)流協(xié)議的一種。抽象地講羡藐,就是從一臺主機一個字節(jié)一個字節(jié)有序地傳輸?shù)搅硪慌_主機。對于HTTP協(xié)議來說仆嗦,自然保持了這種有序性,即按照首行
欧啤、頭部
启上、正文
的順序進行傳輸。首行和頭部都是ASCII文本流冈在,正文部分是字節(jié)流倒慧。一個特殊的控制結(jié)構(gòu)CRLF用來控制每個部分的結(jié)束。
CRLF是回車符和換行符的意思,它們是兩個特殊的ASCII字符纫谅。CR是回車符(\r)炫贤,在ASCII中的編碼是13;LF是換行符(\n)付秕,在ASCII中的編碼是10.
下面通過一個例子來解釋CRLF在HTTP消息中的控制兰珍。
GET /simple.html HTTP/1.1<CRLF> ----- 首行
Accept: text/html<CRLF> --|
Accept-Language: zh-cn<CRLF> |
Accept-Encoding: gzip, deflate<CRLF> |-- 頭部
User-Agent: Mozilla/4.0<CRLF> |
Host: localhost:8080<CRLF> |
Connection: Keep-Alive<CRLF> --|
<CRLF> ----- 空白行表示頭部的結(jié)束
----- 接下來的內(nèi)容是正文部分
這是一個簡單的HTTP請求消息。我在其中做了一些必要的刪減询吴,以便每個頭足夠短都能在一行中顯示掠河。記住首行和頭部是ASCII流,正文部分是字節(jié)流猛计,它們在消息實體中是連續(xù)的片段唠摹,并不像代碼中所示那樣有換行的結(jié)構(gòu)。換句話說奉瘤,原始的消息應(yīng)該是如下形式:
GET /simple.html HTTP/1.1<CRLF>Accept: text/html<CRLF>Accept-Language: zh-cn<CRLF>Accept-Encoding: gzip, deflate<CRLF>User-Agent: Mozilla/4.0<CRLF>Host: localhost:8080<CRLF>Connection: Keep-Alive<CRLF><CRLF>
回到之前有換行符的代碼例子中去勾拉。將每個CRLF單獨列為一行是便于觀察組織〉廖拢可以清楚地看到藕赞,第一行是首行,以CRLF標(biāo)志其結(jié)束肌访;接下來是頭部找默,含有多個消息頭,每行定義一個消息頭吼驶,以CRLF標(biāo)志其結(jié)束惩激;一個單獨的CRLF(緊接著上一個CRLF)表示整個頭部的結(jié)束,接下來是正文部分蟹演。在這個示例中风钻,正文部分為空。
另外酒请,可以看到每個消息頭的格式都是一致的骡技,即Key:Value
的形式。其中Key表示消息頭的鍵羞反,Value表示消息頭的值布朦。
HTTP請求
接下來具體講講HTTP的請求消息。誠心而論昼窗,光是寫上面這么點內(nèi)容就花費了我好久是趴。每每想到寫博客耗費的精力和時間,都會影響到我寫博客的動力澄惊。
之前已經(jīng)說過富雅,HTTP請求消息也分為三個部分:
- 請求行
- 請求頭部
- 請求正文
其中請求頭部的格式我們已經(jīng)見過没佑。請求行的基本格式為:
方法 路徑 版本
例如下面的例子:
GET /simple.html HTTP/1.1
就有對應(yīng)關(guān)系:
- 方法:GET
- 路徑:/simple.html
- 版本:HTTP/1.1
請求行是HTTP請求消息的最基本要素温赔。版本是用來聲明HTTP消息的解析規(guī)則,不同的版本在某些地方的表現(xiàn)是不同的远剩,這里不作過多拆解了『希現(xiàn)在實際應(yīng)用中最新的HTTP協(xié)議版本就是HTTP/1.1。路徑可以理解成該請求消息發(fā)往服務(wù)器的入口痢掠,一般來講嘲恍,同一個路徑應(yīng)該代表同一個資源實體。方法表示對該資源實體進行的操作淹辞,例如上述的GET方法俘侠,其含義就是請求獲取該資源的內(nèi)容。這些都是通常的解釋央星,但不是必然的要求惫东。
實際上,服務(wù)器會解析到方法和路徑颓遏,根據(jù)方法和路徑做出自己相應(yīng)的響應(yīng)滞时。這種響應(yīng)的規(guī)則,可以遵循某些規(guī)范遥皂,也可以完全不考慮這些規(guī)范刽漂,是任意的。市面上已經(jīng)存在一些約定俗成的規(guī)范了样悟,比如Restful
庭猩。Restful
是非常優(yōu)秀的基于HTTP協(xié)議的WEB API設(shè)計理念,很值得講震糖,但在這里就不講了趴腋。
HTTP請求:方法
首先列出最常用的HTTP方法:
- GET
- POST
- PUT
- PATCH
- DELETE
- HEAD
- OPTIONS
之前說過,服務(wù)器對于方法的處理颁井,是沒有強制的規(guī)范的蠢护。這句話說得并不全對。其實每個HTTP方法眉抬,都是有一些HTTP協(xié)議要求的贬芥。比如說GET
方法請求的資源,瀏覽器端一般都會有緩存昏苏,下次請求的時候可能從緩存中去取就夠了威沫,服務(wù)器不用再重復(fù)發(fā)送相同的資源了;但是服務(wù)器如果將獲取資源的接口的方法定義為POST
棒掠,那么瀏覽器端就不會再對資源進行緩存了,即使每次取到的都是同樣地內(nèi)容颈墅,都會請求服務(wù)器重新發(fā)送一遍。所以說恤筛,將請求資源的接口的方法定義為POST
而不是GET
,就是一種不合理的設(shè)計望伦。
再比如煎殷,GET
方法的請求消息是不能定義消息體的,HEAD
方法的請求其響應(yīng)消息是不包含消息體的劣摇,這些都是HTTP協(xié)議對于HTTP方法的約束弓乙。
HTTP請求:路徑
方法和路徑的組合構(gòu)成WEB API的入口,路徑也是很關(guān)鍵的滑潘。路徑的基本格式一般是:
basic-path[?query-string]
其中[]
中的內(nèi)容表示可選的锨咙。在上例中,basic-path就是/simple.html
粹舵,但不包含query-string的內(nèi)容骂倘。basic-path形式很像UNIX中絕對路徑的樣式,要以/
打頭历涝。單獨的/
表示一種路徑/a
、/a/b
堰塌、/a/b/c
都是合理的路徑表示分衫。不推薦使用/a/
场刑、/a/b/
蚪战、/a/b/c/
這樣/后面不跟任何其他內(nèi)容的形式(/
除外)铐懊。優(yōu)秀的API設(shè)計者會利用不同的路徑層級來合理地組織資源瞎疼。
問號后面的部分就是query-string。它的格式是任意的丑慎,只要客戶端和服務(wù)器約定好一定的形式即可瓤摧。這個部分一般是請求參數(shù)的附加。之前說過腻异,GET
方法是不包含請求體的这揣,所以GET
方法的HTTP請求想要附加參數(shù)只能使用這種方式。當(dāng)然其他方法也是可以使用這種方式附加參數(shù)机打,只要服務(wù)器同意就可以了片迅。query-string的格式任意,但在客戶端和服務(wù)器之間也有預(yù)先定好的約定芥挣,即鍵值對的形式耻台。query-string可以表示成一系列鍵值對的集合,用以下方式表示:
k1=v1&k2=v2&k3=&k4
在這里蹋砚,&
分隔不同的鍵值對都弹,=
表示鍵和值得關(guān)系匙姜。可以看到一共有四個鍵值對關(guān)系框杜,它們是:
- k1: v1
- k2: v2
- k3: 空字符串
- k4: 起碼該鍵被定義了
一般來說,鍵值對要寫成k=v
的形式咪辱,但是k=
和僅僅一個k
都是允許的油狂,前者表示鍵k
的值是空字符串,后者表示鍵k
被定義了专筷,但是其值是什么并不關(guān)心磷蛹。
從上面的例子中發(fā)現(xiàn),在query-string中&
和=
被用于特殊的用途了庇勃,我們不能再在其中從容地使用這兩個符號了槽驶。如果我們要在值中包含這兩個符號,那咋辦呢再层?方法就是堡纬,編碼
烤镐。
在實際的HTTP請求中,對于如下的鍵值關(guān)系:
k1: &
k2: =
具體的query-string要寫成:
k1=%26&k2=%3D
這是因為在ASCII編碼中碗旅,&
的16進制表示是26
镜悉,=
的16
進制表示是3D
。對于需要的編碼旧困,就要表示成其實際編碼的16進制表示吼具,每個字節(jié)都用一個%XX
三個字符進行表示。這樣拗盒,%
本身也就要進行編碼了陡蝇,它的編碼是%25
。除了這些控制字符的編碼登夫,還可以進行中文等非英語語言的編碼悼嫉。
對于編碼部分戏蔑,我推薦阮一峰的一篇博文:關(guān)于URL編碼
雖然看了也未必懂了鲁纠,但是最起碼知道編碼不是一件簡單的事情。
HTTP請求頭
HTTP請求頭格式與之前所說的消息頭格式?jīng)]什么兩樣情龄,就是以冒號分隔的鍵值對捍壤。HTTP請求頭中,既包含預(yù)定義的頭(如Content-Type专酗、Content-Length等)盗扇,也支持自定義頭疗隶。原本打算多列出幾個常見的請求頭的,但限于精力蒋纬,不打算這樣做了。我只說說我最常用的Content-Type頭吧法牲。
Content-Type頭琼掠,既可用于請求消息,也可用于響應(yīng)消息悼瓮,是規(guī)定請求正文內(nèi)容格式的頭部横堡。例如利用這個頭部冠桃,我們可以規(guī)定正文的格式為純文本格式食听、表單格式、XML格式葬项、JSON格式民珍、圖像格式等盗飒。例如Content-Type: application/json
就表示JSON文本格式。
在小節(jié)的末尾津肛,我良心地給出一個關(guān)于HTTP預(yù)定義頭的參考網(wǎng)址:HTTP消息頭大全
HTTP響應(yīng)
HTTP響應(yīng)消息的基本格式也是一樣的汗贫,包含三個部分:
- 響應(yīng)行
- 響應(yīng)頭部
- 響應(yīng)正文
響應(yīng)頭部和響應(yīng)正文我覺得不需要再多說了落包。響應(yīng)行的基本格式是:
版本號 狀態(tài)碼 狀態(tài)文本
例如下面的響應(yīng)行:
HTTP/1.1 200 OK
其對應(yīng)關(guān)系為:
- 版本號:HTTP/1.1
- 狀態(tài)碼:200
- 狀態(tài)文本:OK
HTTP狀態(tài)碼主要表示應(yīng)答的狀態(tài)咐蝇。狀態(tài)碼是由3個數(shù)字表示,其中第一個數(shù)字表示一個大狀態(tài),后面兩個數(shù)字表示該大狀態(tài)的一個子狀態(tài)岛请。200就表示操作成功警绩,還有其他常見的如404表示對象未找到,500表示服務(wù)器錯誤后室,403表示不能瀏覽目錄等等。
狀態(tài)碼一共分為五個大狀態(tài)岸霹,它們是:
- 1xx
- 2xx:請求成功處理
- 3xx
- 4xx:客戶端出錯
- 5xx:服務(wù)器出錯
HTTP協(xié)議示例:
接下來的所有示例中贡避,我們將代碼都寫成前面的一行一行的模式,但略去*
*. 這時只要記住每行的結(jié)尾都暗含一個CRLF控制就可以了刮吧。例如:
GET /simple.html?bg=white HTTP/1.1
Accept: text/html
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0
Host: localhost:8080
Connection: Keep-Alive
GET請求沒有請求正文皇筛,但可以包含query-string.
POST請求可以包含請求正文,例如下面帶JSON格式正文的POST請求:
POST /test/demo_form.asp HTTP/1.1
Host: w3schools.com
Content-Type: application/json
Content-Length: 38
{"name1": "value1", "name2": "value2"}
一個返回404錯誤的響應(yīng)示例:
HTTP/1.1 404 Not Found
Date: Mon, 06 Mar 2006 09:03:14 GMT
Server: Apache/2.0.55 (Unix) PHP/5.0.5
Content-Length: 291
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /notexist was not found on this server.</p>
<hr>
<address>Apache/2.0.55 (Unix) PHP/5.0.5 Server at localhost Port 8080</address>
</body></html>