本文主要和大家分享HTTP請求頭與請求體詳解,對於HTTP的學習主要包含HTTP 基礎、HTTP 請求頭與請求體、HTTP 回應頭與狀態碼、HTTP 快取這四個部分,而對於HTTP相關的擴展與引申,我們也需要了解HTTPS 理解與實踐 、HTTP/2 基礎、WebSocket 基礎這些部分。本部分知識點同時也歸納於筆者的我的校招準備之路:從Web前端到服務端應用架構這篇綜述。
HTTP Request
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)分為三個部分:請求方法、請求位址和協定及版本,以CRLF(rn)結束。
HTTP/1.1 定義的請求方法有8種:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、TRACE,最常的兩種GET和POST,如果是RESTful介面的話一般會用到GET、 POST、DELETE、PUT。
注意,只有POST、PUT以及PATCH這三個動詞時會包含請求體,而GET、HEAD、DELETE、CONNECT、TRACE、OPTIONS這幾個動詞時不包含請求體。
Header | 解釋 | ##範例|
---|---|---|
指定客戶端能夠接收的內容類型 | Accept: text/plain, text/html,application/json | |
瀏覽器可以接受的字元編碼集。 | Accept-Charset: iso-8859-5 | |
指定瀏覽器可以支援的web伺服器傳回內容壓縮編碼類型。 | Accept-Encoding: compress, gzip | |
瀏覽器可接受的語言 | Accept-Language: en,zh | |
可以請求網頁實體的一個或多個子範圍欄位 | Accept-Ranges: bytes | |
HTTP授權的授權憑證 | Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== | |
指定要求和回應遵循的快取機制 | Cache-Control: no-cache | |
##表示是否需要持久連線。 (HTTP 1.1預設為持久連接) | Connection: close | |
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##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 |
根据应用场景的不同,HTTP请求的请求体有三种不同的形式。
移动开发者常见的,请求体是任意类型,服务器不会解析请求体,请求体的处理需要自己解析,如 POST 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 方式提交数据。如有需要,可以参考这篇文章。
我的博客之前提到过 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 会更灵活方便。
這算是最常見的 POST 提交資料的方式了。瀏覽器的原生 ff9c23ada1bcecdd1a0fb5d5a0f18437 表單,如果不設定 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可以省略。
我們使用表單上傳檔案時,必須讓 ff9c23ada1bcecdd1a0fb5d5a0f18437 表單的 enctyped
等於 multipart/form-data。直接來看一個請求範例:
BASHPOST http://www.example.com HTTP/1.1Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA------WebKitFormBoundaryrGKCBY7qhFd3TrwAContent-Disposition: form-data; name="text"title------WebKitFormBoundaryrGKCBY7qhFd3TrwAContent-Disposition: form-data; name="file"; filename="chrome.png"Content-Type: image/pngPNG ... content of chrome.png ...------WebKitFormBoundaryrGKCBY7qhFd3TrwA--
这个例子稍微复杂点。首先生成了一个 boundary 用于分割不同的字段,为了避免与正文内容重复,boundary 很长很复杂。然后 Content-Type 里指明了数据是以 multipart/form-data 来编码,本次请求的 boundary 是什么内容。消息主体里按照字段个数又分为多个结构类似的部分,每部分都是以 --boundary
开始,紧接着是内容描述信息,然后是回车,最后是字段具体内容(文本或二进制)。如果传输的是文件,还要包含文件名和文件类型信息。消息主体最后以 --boundary--
标示结束。关于 multipart/form-data 的详细定义,请前往 rfc1867 查看。
这种方式一般用来上传文件,各大服务端语言对它也有着良好的支持。
上面提到的这两种 POST 数据的方式,都是浏览器原生支持的,而且现阶段标准中原生 ff9c23ada1bcecdd1a0fb5d5a0f18437 表单也只支持这两种方式(通过 ff9c23ada1bcecdd1a0fb5d5a0f18437 元素的enctype
属性指定,默认为 application/x-www-form-urlencoded
。其实 enctype
还支持 text/plain
,不过用得非常少)。
随着越来越多的 Web 站点,尤其是 WebApp,全部使用 Ajax 进行数据交互之后,我们完全可以定义新的数据提交方式,给开发带来更多便利。
网页中的表单使用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物件的字串物件負責,確保所有字元都會被適當地編碼。
相關推薦:
以上是HTTP請求頭與請求體詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!