HTTP協議做爲網絡傳輸的基本協議,有着普遍的應用。HTTP協議的完整內容不少,可是其核心知識卻又簡單精煉。學習者應該掌握其基本結構,而且可以觸類旁通。這篇文章所列的,就是在實際開發中必須知道必須掌握的HTTP知識。html
HTTP消息(有的文章稱之爲報文)分爲請求消息和響應消息兩種基本分類。其中請求消息是客戶端發送給服務器的用於請求服務和資源的消息,響應消息是服務器對請求消息的應答。通常來講,一個響應對應一個請求,很少也很多。json
HTTP協議被人總結爲無鏈接、無狀態的特色:瀏覽器
無鏈接:無鏈接的含義是限制每次鏈接只處理一個請求。服務器處理完客戶的請求,並收到客戶的應答後,即斷開鏈接。採用這種方式能夠節省傳輸時間。緩存
無狀態:HTTP協議是無狀態協議。無狀態是指協議對於事務處理沒有記憶能力。缺乏狀態意味着若是後續處理須要前面的信息,則它必須重傳,這樣可能致使每次鏈接傳送的數據量增大。另外一方面,在服務器不須要先前信息時它的應答就較快。服務器
HTTP協議的請求消息和響應消息的格式及其類似。提煉出它們的共性,能夠指出,HTTP消息分爲三個部分:網絡
首行app
頭部(Header)學習
正文(Body)this
其中,頭部用來指出HTTP消息的一些屬性,它們有固定的格式;正文部分是傳輸的實際內容,它們的格式是任意的,一般用Content-Type頭來指定。首行在請求消息和響應消息中具體格式略有區別,它們表示的按理說應該是HTTP消息最基本的部分。不管是HTTP請求仍是HTTP響應,首行都是有的,不然會出現不可饒恕的解析錯誤;然而頭部和正文是可選的,不過實際過程當中,多多少少都要包含一些基本的頭。編碼
HTTP消息主要是基於ASCII編碼的消息實體。主要的意思是指首行和頭部都是以ASCII編碼,而正文部分的編碼就顯得任意了。在實際的開發中,發送的文本消息時常會碰到亂碼的問題。一種解決辦法是,對於文本消息,約定以UTF-8格式進行編碼和解碼。
知道的人也許知道,HTTP消息是基於TCP協議的上層應用協議。TCP協議是網絡流協議的一種。抽象地講,就是從一臺主機一個字節一個字節有序地傳輸到另外一臺主機。對於HTTP協議來講,天然保持了這種有序性,即按照首行、頭部、正文的順序進行傳輸。首行和頭部都是ASCII文本流,正文部分是字節流。一個特殊的控制結構CRLF用來控制每一個部分的結束。
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> ----- 空白行表示頭部的結束 ----- 接下來的內容是正文部分
這是一個簡單的HTTP請求消息。我在其中作了一些必要的刪減,以便每一個頭足夠短都能在一行中顯示。記住首行和頭部是ASCII流,正文部分是字節流,它們在消息實體中是連續的片斷,並不像代碼中所示那樣有換行的結構。換句話說,原始的消息應該是以下形式:
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標誌其結束;接下來是頭部,含有多個消息頭,每行定義一個消息頭,以CRLF標誌其結束;一個單獨的CRLF(緊接着上一個CRLF)表示整個頭部的結束,接下來是正文部分。在這個示例中,正文部分爲空。
另外,能夠看到每一個消息頭的格式都是一致的,即Key:Value
的形式。其中Key表示消息頭的鍵,Value表示消息頭的值。
接下來具體講講HTTP的請求消息。誠心而論,光是寫上面這麼點內容就花費了我很久。往往想到寫博客耗費的精力和時間,都會影響到我寫博客的動力。
以前已經說過,HTTP請求消息也分爲三個部分:
請求行
請求頭部
請求正文
其中請求頭部的格式咱們已經見過。請求行的基本格式爲:
方法 路徑 版本
例以下面的例子:
GET /simple.html HTTP/1.1
就有對應關係:
方法:GET
路徑:/simple.html
版本:HTTP/1.1
請求行是HTTP請求消息的最基本要素。版本是用來聲明HTTP消息的解析規則,不一樣的版本在某些地方的表現是不一樣的,這裏不做過多拆解了。如今實際應用中最新的HTTP協議版本就是HTTP/1.1。路徑能夠理解成該請求消息發往服務器的入口,通常來說,同一個路徑應該表明同一個資源實體。方法表示對該資源實體進行的操做,例如上述的GET方法,其含義就是請求獲取該資源的內容。這些都是一般的解釋,但不是必然的要求。實際上,服務器會解析到方法和路徑,根據方法和路徑作出本身相應的響應。這種響應的規則,能夠遵循某些規範,也能夠徹底不考慮這些規範,是任意的。市面上已經存在一些約定俗成的規範了,好比Restful。Restful是很是優秀的基於HTTP協議的WEB API設計理念,很值得講,但在這裏就不講了。
首先列出最經常使用的HTTP方法:
GET
POST
PUT
PATCH
DELETE
HEAD
OPTIONS
以前說過,服務器對於方法的處理,是沒有強制的規範的。這句話說得並不全對。其實每一個HTTP方法,都是有一些HTTP協議要求的。好比說GET方法請求的資源,瀏覽器端通常都會有緩存,下次請求的時候可能從緩存中去取就夠了,服務器不用再重複發送相同的資源了;可是服務器若是將獲取資源的接口的方法定義爲POST,那麼瀏覽器端就不會再對資源進行緩存了,即便每次取到的都是一樣地內容,都會請求服務器從新發送一遍。因此說,將請求資源的接口的方法定義爲POST而不是GET,就是一種不合理的設計。
再好比,GET方法的請求消息是不能定義消息體的,HEAD方法的請求其響應消息是不包含消息體的,這些都是HTTP協議對於HTTP方法的約束。
方法和路徑的組合構成WEB API的入口,路徑也是很關鍵的。路徑的基本格式通常是:
basic-path[?query-string]
其中[]
中的內容表示可選的。在上例中,basic-path就是/simple.html,但不包含query-string的內容。basic-path形式很像UNIX中絕對路徑的樣式,要以/
打頭。單獨的/
表示一種路徑,/a
、/a/b
、/a/b/c
都是合理的路徑表示。不推薦使用/a/
、/a/b/
、/a/b/c/
這樣/
後面不跟任何其餘內容的形式(/
除外)。優秀的API設計者會利用不一樣的路徑層級來合理地組織資源。
問號後面的部分就是query-string。它的格式是任意的,只要客戶端和服務器約定好必定的形式便可。這個部分通常是請求參數的附加。以前說過,GET方法是不包含請求體的,因此GET方法的HTTP請求想要附加參數只能使用這種方式。固然其餘方法也是可使用這種方式附加參數,只要服務器贊成就能夠了。query-string的格式任意,但在客戶端和服務器之間也有預先定好的約定,即鍵值對的形式。query-string能夠表示成一系列鍵值對的集合,用如下方式表示:
k1=v1&k2=v2&k3=&k4
在這裏,&
分隔不一樣的鍵值對,=
表示鍵和值得關係。能夠看到一共有四個鍵值對關係,它們是:
k1: v1
k2: v2
k3: 空字符串
k4: 起碼該鍵被定義了
通常來講,鍵值對要寫成k=v
的形式,可是k=
和僅僅一個k
都是容許的,前者表示鍵k
的值是空字符串,後者表示鍵k
被定義了,可是其值是什麼並不關心。
從上面的例子中發現,在query-string中&
和=
被用於特殊的用途了,咱們不能再在其中從容地使用這兩個符號了。若是咱們要在值中包含這兩個符號,那咋辦呢?方法就是,編碼。
在實際的HTTP請求中,對於以下的鍵值關係
k1: & k2: =
具體的query-string要寫成:
k1=%26&k2=%3D
這是由於在ASCII編碼中,&
的16進製表示是26
,=
的16進製表示是3D
。對於須要的編碼,就要表示成其實際編碼的16進製表示,每一個字節都用一個%XX
三個字符進行表示。這樣,%
自己也就要進行編碼了,它的編碼是%25
。除了這些控制字符的編碼,還能夠進行中文等非英語語言的編碼。
對於編碼部分,我推薦阮一峯的一篇博文
雖然看了也未必懂了,可是最起碼知道編碼不是一件簡單的事情。
HTTP請求頭格式與以前所說的消息頭格式沒什麼兩樣,就是以冒號分隔的鍵值對。HTTP請求頭中,既包含預約義的頭(如Content-Type、Content-Length等),也支持自定義頭。本來打算多列出幾個常見的請求頭的,但限於精力,不打算這樣作了。我只說說我最經常使用的Content-Type頭吧。
Content-Type頭,既可用於請求消息,也可用於響應消息,是規定請求正文內容格式的頭部。例如利用這個頭部,咱們能夠規定正文的格式爲純文本格式、表單格式、XML格式、JSON格式、圖像格式等。例如Content-Type: application/json
就表示JSON文本格式。
在小節的末尾,我良心地給出一個關於HTTP預約義頭的參考網址:
HTTP響應消息的基本格式也是同樣的,包含三個部分:
響應行
響應頭部
響應正文
響應頭部和響應正文我以爲不須要再多說了。響應行的基本格式是:
版本號 狀態碼 狀態文本
例以下面的響應行:
HTTP/1.1 200 OK
其對應關係爲:
版本號:HTTP/1.1
狀態碼:200
狀態文本:OK
HTTP狀態碼主要表示應答的狀態。狀態碼是由3個數字表示,其中第一個數字表示一個大狀態,後面兩個數字表示該大狀態的一個子狀態。200就表示操做成功,還有其餘常見的如404表示對象未找到,500表示服務器錯誤,403表示不能瀏覽目錄等等。
狀態碼一共分爲五個大狀態,它們是:
1xx
2xx:請求成功處理
3xx
4xx:客戶端出錯
5xx:服務器出錯
接下來的全部示例中,咱們將代碼都寫成前面的一行一行的模式,但略去
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錯誤的響應示例:
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>