HTTP請求與請求頭


HTTP 的請求報文分為三個部分 請求行、請求頭和請求體,格式如圖:

一個典型的請求消息頭域,如下所示:

  POST/GET http://download.microtool.de:80/somedata.exe 
  Host: download.microtool.de 
  Accept:*/*   Pragma: no-cache   Cache-Control: no-cache   Referer: http://download.microtool.de/   User-Agent:Mozilla/4.04[en](Win95;I;Nav)   Range:bytes=554554- 

Request Line:請求行

請求行(Request Line)分為三個部分:請求方法、請求地址和協議及版本,以CRLF(rn)結束。HTTP/1.1 定義的請求方法有8種:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、TRACE,最常的兩種GET和POST,如果是RESTful接口的話一般會用到GET、POST、DELETE、PUT。

Request Methods

注意,僅有POST、PUT以及PATCH這三個動詞時會包含請求體,而GET、HEAD、DELETE、CONNECT、TRACE、OPTIONS這幾個動詞時不包含請求體。

Header:請求頭

Header 解釋 示例
Accept 指定客戶端能夠接收的內容類型 Accept: text/plain, text/html,application/json
Accept-Charset 瀏覽器可以接受的字符編碼集。 Accept-Charset: iso-8859-5
Accept-Encoding 指定瀏覽器可以支持的web服務器返回內容壓縮編碼類型。 Accept-Encoding: compress, gzip
Accept-Language 瀏覽器可接受的語言 Accept-Language: en,zh
Accept-Ranges 可以請求網頁實體的一個或者多個子范圍字段 Accept-Ranges: bytes
Authorization HTTP授權的授權證書 Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Cache-Control 指定請求和響應遵循的緩存機制 Cache-Control: no-cache
Connection 表示是否需要持久連接。(HTTP 1.1默認進行持久連接) Connection: close
Cookie HTTP請求發送時,會把保存在該請求域名下的所有cookie值一起發送給web服務器。 Cookie: $Version=1; Skin=new;
Content-Length 請求的內容長度 Content-Length: 348
Content-Type 請求的與實體對應的MIME信息 Content-Type: application/x-www-form-urlencoded
Date 請求發送的日期和時間 Date: Tue, 15 Nov 2010 08:12:31 GMT
Expect 請求的特定的服務器行為 Expect: 100-continue
From 發出請求的用戶的Email From: user@email.com
Host 指定請求的服務器的域名和端口號 Host: www.zcmhi.com
If-Match 只有請求內容與實體相匹配才有效 If-Match: “737060cd8c284d8af7ad3082f209582d”
If-Modified-Since 如果請求的部分在指定時間之后被修改則請求成功,未被修改則返回304代碼 If-Modified-Since: Sat, 29 Oct 2010 19:43:31 GMT
If-None-Match 如果內容未改變返回304代碼,參數為服務器先前發送的Etag,與服務器回應的Etag比較判斷是否改變 If-None-Match: “737060cd8c284d8af7ad3082f209582d”
If-Range 如果實體未改變,服務器發送客戶端丟失的部分,否則發送整個實體。參數也為Etag If-Range: “737060cd8c284d8af7ad3082f209582d”
If-Unmodified-Since 只在實體在指定時間之后未被修改才請求成功 If-Unmodified-Since: Sat, 29 Oct 2010 19:43:31 GMT
Max-Forwards 限制信息通過代理和網關傳送的時間 Max-Forwards: 10
Pragma 用來包含實現特定的指令 Pragma: no-cache
Proxy-Authorization 連接到代理的授權證書 Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Range 只請求實體的一部分,指定范圍 Range: bytes=500-999
Referer 先前網頁的地址,當前請求網頁緊隨其后,即來路 Referer: http://www.zcmhi.com/archives...
TE 客戶端願意接受的傳輸編碼,並通知服務器接受接受尾加頭信息 TE: trailers,deflate;q=0.5
Upgrade 向服務器指定某種傳輸協議以便服務器進行轉換(如果支持) Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11
User-Agent User-Agent的內容包含發出請求的用戶信息 User-Agent: Mozilla/5.0 (Linux; X11)
Via 通知中間網關或代理服務器地址,通信協議 Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)
Warning 關於消息實體的警告信息 Warn: 199 Miscellaneous warning

Request Body:請求體

Types

根據應用場景的不同,HTTP請求的請求體有三種不同的形式。

任意類型

移動開發者常見的,請求體是任意類型,服務器不會解析請求體,請求體的處理需要自己解析,如 POST JSON時候就是這類。

application/json

application/json 這個 Content-Type 作為響應頭大家肯定不陌生。實際上,現在越來越多的人把它作為請求頭,用來告訴服務端消息主體是序列化后的 JSON 字符串。由於 JSON 規范的流行,除了低版本 IE 之外的各大瀏覽器都原生支持 JSON.stringify,服務端語言也都有處理 JSON 的函數,使用 JSON 不會遇上什么麻煩。

JSON 格式支持比鍵值對復雜得多的結構化數據,這一點也很有用。記得我幾年前做一個項目時,需要提交的數據層次非常深,我就是把數據 JSON 序列化之后來提交的。不過當時我是把 JSON 字符串作為 val,仍然放在鍵值對里,以 x-www-form-urlencoded 方式提交。

Google 的 AngularJS 中的 Ajax 功能,默認就是提交 JSON 字符串。例如下面這段代碼:

JSvar data = {'title':'test', 'sub' : [1,2,3]}; $http.post(url, data).success(function(result) { ... }); 

最終發送的請求是:

BASHPOST http://www.example.com HTTP/1.1 Content-Type: application/json;charset=utf-8 {"title":"test","sub":[1,2,3]} 

這種方案,可以方便的提交復雜的結構化數據,特別適合 RESTful 的接口。各大抓包工具如 Chrome 自帶的開發者工具、Firebug、Fiddler,都會以樹形結構展示 JSON 數據,非常友好。但也有些服務端語言還沒有支持這種方式,例如 php 就無法通過 $_POST 對象從上面的請求中獲得內容。這時候,需要自己動手處理下:在請求頭中 Content-Type 為 application/json 時,從 php://input 里獲得原始輸入流,再 json_decode 成對象。一些 php 框架已經開始這么做了。

當然 AngularJS 也可以配置為使用 x-www-form-urlencoded 方式提交數據。如有需要,可以參考這篇文章

text/xml

我的博客之前提到過 XML-RPC(XML Remote Procedure Call)。它是一種使用 HTTP 作為傳輸協議,XML 作為編碼方式的遠程調用規范。典型的 XML-RPC 請求是這樣的:

HTMLPOST http://www.example.com HTTP/1.1 
Content-Type: text/xml

<?xml version="1.0"?> <methodCall> <methodName>examples.getStateName</methodName> <params> <param> <value><i4>41</i4></value> </param> </params> </methodCall> 

XML-RPC 協議簡單、功能夠用,各種語言的實現都有。它的使用也很廣泛,如 WordPress 的 XML-RPC Api,搜索引擎的 ping 服務等等。JavaScript 中,也有現成的庫支持以這種方式進行數據交互,能很好的支持已有的 XML-RPC 服務。不過,我個人覺得 XML 結構還是過於臃腫,一般場景用 JSON 會更靈活方便。

Query String:application/x-www-form-urlencoded

這算是最常見的 POST 提交數據的方式了。瀏覽器的原生 <form> 表單,如果不設置 enctype 屬性,那么最終就會以 application/x-www-form-urlencoded 方式提交數據。請求類似於下面這樣(無關的請求頭在本文中都省略掉了):

POST http://www.example.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8
title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3

首先,Content-Type 被指定為 application/x-www-form-urlencoded;這里的格式要求就是URL中Query String的格式要求:多個鍵值對之間用&連接,鍵與值之前用=連接,且只能用ASCII字符,非ASCII字符需使用UrlEncode編碼。大部分服務端語言都對這種方式有很好的支持。例如 PHP 中,$_POST['title'] 可以獲取到 title 的值,$_POST['sub'] 可以得到 sub 數組。

文件分割

第三種請求體的請求體被分成為多個部分,文件上傳時會被使用,這種格式最先應該是被用於郵件傳輸中,每個字段/文件都被boundary(Content-Type中指定)分成單獨的段,每段以-- 加 boundary開頭,然后是該段的描述頭,描述頭之后空一行接內容,請求結束的標制為boundary后面加--,結構見下圖:

區分是否被當成文件的關鍵是Content-Disposition是否包含filename,因為文件有不同的類型,所以還要使用Content-Type指示文件的類型,如果不知道是什么類型取值可以為application/octet-stream表示該文件是個二進制文件,如果不是文件則Content-Type可以省略。
我們使用表單上傳文件時,必須讓 <form> 表單的 enctyped 等於 multipart/form-data。直接來看一個請求示例:

BASHPOST http://www.example.com HTTP/1.1 Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA ------WebKitFormBoundaryrGKCBY7qhFd3TrwA Content-Disposition: form-data; name="text" title ------WebKitFormBoundaryrGKCBY7qhFd3TrwA Content-Disposition: form-data; name="file"; filename="chrome.png" Content-Type: image/png PNG ... content of chrome.png ... ------WebKitFormBoundaryrGKCBY7qhFd3TrwA-- 

這個例子稍微復雜點。首先生成了一個 boundary 用於分割不同的字段,為了避免與正文內容重復,boundary 很長很復雜。然后 Content-Type 里指明了數據是以 multipart/form-data 來編碼,本次請求的 boundary 是什么內容。消息主體里按照字段個數又分為多個結構類似的部分,每部分都是以 --boundary 開始,緊接着是內容描述信息,然后是回車,最后是字段具體內容(文本或二進制)。如果傳輸的是文件,還要包含文件名和文件類型信息。消息主體最后以 --boundary-- 標示結束。關於 multipart/form-data 的詳細定義,請前往 rfc1867 查看。

這種方式一般用來上傳文件,各大服務端語言對它也有着良好的支持。

上面提到的這兩種 POST 數據的方式,都是瀏覽器原生支持的,而且現階段標准中原生 <form> 表單也只支持這兩種方式(通過 <form> 元素的enctype 屬性指定,默認為 application/x-www-form-urlencoded。其實 enctype 還支持 text/plain,不過用得非常少)。

隨着越來越多的 Web 站點,尤其是 WebApp,全部使用 Ajax 進行數據交互之后,我們完全可以定義新的數據提交方式,給開發帶來更多便利。

Encoding:編碼

網頁中的表單使用POST方法提交時,數據內容的類型是 application/x-www-form-urlencoded,這種類型會:

  1.字符"a"-"z","A"-"Z","0"-"9",".","-","*",和"_" 都不會被編碼;

  2.將空格轉換為加號 (+)

  3.將非文本內容轉換成"%xy"的形式,xy是兩位16進制的數值;

  4.在每個 name=value 對之間放置 & 符號。

web設計者面臨的眾多難題之一便是怎樣處理不同操作系統間的差異性。這些差異性能引起URL方面的問題:例如,一些操作系統允許文件名中含有空格符,有些又不允許。大多數操作系統不會認為文件名中含有符號“#”會有什么特殊含義;但是在一個URL中,符號“#”表示該文件名已經結束,后面會緊跟一個fragment(部分)標識符。其他的特殊字符,非字母數字字符集,它們在URL或另一個操作系統上都有其特殊的含義,表述着相似的問題。為了解決這些問題,我們在URL中使用的字符就必須是一個ASCII字符集的固定字集中的元素,具體如下:

  1.大寫字母A-Z

  2.小寫字母a-z

  3.數字 0-9

  4.標點符 - _ . ! ~ * ' (和 ,)

  諸如字符: / & ? @ # $ + = 和 %也可以被使用,但是它們各有其特殊的用途,如果一個文件名包括了這些字符( / & ? @ # $ + = %),這些字符和所有其他字符就應該被編碼。

  編碼過程非常簡單,任何字符只要不是ASCII碼數字,字母,或者前面提到的標點符,它們都將被轉換成字節形式,每個字節都寫成這種形式:一個“%”后面跟着兩位16進制的數值。空格是一個特殊情況,因為它們太平常了。它除了被編碼成“%20”以外,還能編碼為一個“+”。加號(+)本身被編碼為%2B。當/ # = & 和?作為名字的一部分來使用時,而不是作為URL部分之間的分隔符來使用時,它們都應該被編碼。

  WARNING這種策略在存在大量字符集的異構環境中效果不甚理想。例如:在U.S. Windows 系統中, é 被編碼為 %E9. 在 U.S. Mac中被編碼為%8E。這種不確定性的存在是現存的URI的一個明顯的不足。所以在將來URI的規范當中應該通過國際資源標識符(IRIs)進行改善。

  類URL並不自動執行編碼或解碼工作。你能生成一個URL對象,它可以包括非法的ASCII和非ASCII字符和/或%xx。當用方法getPath() 和toExternalForm( ) 作為輸出方法時,這種字符和轉移符不會自動編碼或解碼。你應對被用來生成一個URL對象的字符串對象負責,確保所有字符都會被恰當地編碼。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM