Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
. HTTP請(qǐng)求格式
做過(guò)Socket編程的人都知道,當(dāng)我們?cè)O(shè)計(jì)一個(gè)通信協(xié)議時(shí),“消息頭/消息體”的分割方式是很常用的,消息頭告訴對(duì)方這個(gè)消息是干什么的,消息體告訴對(duì) 方怎么干。HTTP協(xié)議傳輸?shù)南⒁彩沁@樣規(guī)定的,每一個(gè)HTTP包都分為HTTP頭和HTTP體兩部分,消息體是可選的,而消息頭是必須的。每當(dāng)我們打 開(kāi)一個(gè)網(wǎng)頁(yè),在上面點(diǎn)擊右鍵,選擇“查看源文件”,這時(shí)看到的HTML代碼就是HTTP的消息體,那么消息頭可以通過(guò)瀏覽器的開(kāi)發(fā)工具或者插件可以看到, 如果火狐的Firebug,IE的Httpwatch。
客戶端通過(guò)發(fā)送 HTTP 請(qǐng)求向服務(wù)器請(qǐng)求對(duì)資源的訪問(wèn)。 它向服務(wù)器傳遞了一個(gè)數(shù)據(jù)塊,也就是請(qǐng)求信息,HTTP 請(qǐng)求由三部分組成:請(qǐng)求行、 請(qǐng)求頭和請(qǐng)求正文。
請(qǐng)求行:請(qǐng)求方法 URI 協(xié)議/版本
請(qǐng)求頭(Request Header)
請(qǐng)求正文
下面是一個(gè)HTTP請(qǐng)求的數(shù)據(jù):
POST /index.php HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:10.0.2) Gecko/20100101 Firefox/10.0.2
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Referer: http://localhost/
Content-Length:25
Content-Type:application/x-www-form-urlencoded
username=aa&password=1234
1、請(qǐng)求行:請(qǐng)求方法URI協(xié)議/版本
請(qǐng)求的第一行是“方法 URL 協(xié)議/版本”,并以 回車(chē)換行作為結(jié)尾。請(qǐng)求行以空格分隔。格式如下:
POST /index.php HTTP/1.1
以上代碼中“GET”代表請(qǐng)求方法,“//ndex.php”表示URI,“HTTP/1.1代表協(xié)議和協(xié)議的版本。
根據(jù)HTTP標(biāo)準(zhǔn),HTTP請(qǐng)求可以使用多種請(qǐng)求方法。例如:HTTP1.1支持7種請(qǐng)求方法:GET、POST、HEAD、OPTIONS、PUT、DELETE和TARCE。在Internet應(yīng)用中,最常用的方法是GET和POST。
URL完整地指定了要訪問(wèn)的網(wǎng)絡(luò)資源,通常只要給出相對(duì)于服務(wù)器的根目錄的相對(duì)目錄即可,因此總是以“/”開(kāi)頭,最后,協(xié)議版本聲明了通信過(guò)程中使用HTTP的版本。
請(qǐng)求方法
在 HTTP 協(xié)議中,HTTP 請(qǐng)求可以使用多種請(qǐng)求方法,這些方法指明了要以何種方式來(lái)訪問(wèn) Request-URI 所標(biāo)識(shí)的資源。HTTP1.1 支持的請(qǐng)求方法如下表所示:
HTTP1.1 中的請(qǐng)求方式:
方法
作用
GET
請(qǐng)求獲取由 Request-URI 所標(biāo)識(shí)的資源
POST
請(qǐng)求服務(wù)器接收在請(qǐng)求中封裝的實(shí)體,并將其作為由 Request-Line 中的 Request-URI 所標(biāo)識(shí)的資源的一部分
HEAD
請(qǐng)求獲取由 Request-URI 所標(biāo)識(shí)的資源的響應(yīng)消息報(bào)頭
PUT
請(qǐng)求服務(wù)器存儲(chǔ)一個(gè)資源,并用 Request-URI 作為其標(biāo)識(shí)符
DELETE
請(qǐng)求服務(wù)器刪除由 Request-URI 所標(biāo)識(shí)的資源
TRACE
請(qǐng)求服務(wù)器回送到的請(qǐng)求信息,主要用于測(cè)試或診斷
CONNECT
保留將來(lái)使用
OPTIONS
請(qǐng)求查詢服務(wù)器的性能,或者查詢與資源相關(guān)的選項(xiàng)和需求
重點(diǎn)介紹 GET、POST 和 HEAD 三個(gè)方法:
(1)GET
GET 方法用于獲取由 Request-URI 所標(biāo)識(shí)的資源的信息,常見(jiàn)的形式是:
GET Request-URI HTTP/1.1
GET方法是默認(rèn)的HTTP請(qǐng)求方法,例如當(dāng)我們通過(guò)在瀏覽器的地址欄中直接輸入網(wǎng)址的方式去訪問(wèn)網(wǎng)頁(yè)的時(shí)候,瀏覽器采用的就是 GET 方法向服務(wù)器獲取資源。
我們可以使用GET方法來(lái)提交表單數(shù)據(jù),用GET方法提交的表單數(shù)據(jù)只經(jīng)過(guò)了簡(jiǎn)單的編碼,同時(shí)它將作為URL的一部分向服務(wù)器發(fā)送,因此,如果使用GET方法來(lái)提交表單數(shù)據(jù)就存在著安全隱患上。例如:
Http://localhost/login.php?username=aa&password=1234
從上面的URL請(qǐng)求中,很容易就可以辯認(rèn)出表單提交的內(nèi)容。(?之后的內(nèi)容)另外由于GET方法提交的數(shù)據(jù)是作為URL請(qǐng)求的一部分所以提交的數(shù)據(jù)量不能太大。這是因?yàn)闉g覽器對(duì)url的長(zhǎng)度有限制
各種瀏覽器也會(huì)對(duì)url的長(zhǎng)度有所限制,下面是幾種常見(jiàn)瀏覽器的url長(zhǎng)度限制:(單位:字符)
IE : 2803
Firefox:65536
Chrome:8182
Safari:80000
Opera:190000
(2)POST
POST方法是GET方法的一個(gè)替代方法,它主要是向Web服務(wù)器提交表單數(shù)據(jù),尤其是大批量的數(shù)據(jù)。 在請(qǐng)求頭信息結(jié)束之后的兩個(gè)回車(chē)換行之后(實(shí)際是空一行),就是表單提交的數(shù)據(jù)。如上面提到的post表單數(shù)據(jù):
username=aa&password=1234
POST方法克服了GET方法的一些缺點(diǎn)。通過(guò)POST方法提交表單數(shù)據(jù)時(shí),數(shù)據(jù)不是作為URL請(qǐng)求的一部分而是作為標(biāo)準(zhǔn)數(shù)據(jù)傳送給Web服務(wù)器,這就克 服了GET方法中的信息無(wú)法保密和數(shù)據(jù)量太小的缺點(diǎn)。因此,出于安全的考慮以及對(duì)用戶隱私的尊重,通常表單提交時(shí)采用POST方法。
從編程的角度來(lái)講,如果用戶通過(guò)GET方法提交數(shù)據(jù),則數(shù)據(jù)存放在QUERY_STRING環(huán)境變量中,而POST方法提交的數(shù)據(jù)則可以從標(biāo)準(zhǔn)輸入流中獲取。
GET與POST方法有以下區(qū)別:
1、 在客戶端,Get方式在通過(guò)URL提交數(shù)據(jù),數(shù)據(jù)在URL中可以看到;POST方式,數(shù)據(jù)放在HTTP包的body中。
2、 GET方式提交的數(shù)據(jù)大小有限制(因?yàn)闉g覽器對(duì)URL的長(zhǎng)度有限制),而POST則沒(méi)有此限制。
3、安全性問(wèn)題。正如在(1)中提到,使用 Get 的時(shí)候,參數(shù)會(huì)顯示在地址欄上,而 Post 不會(huì)。所以,如果這些數(shù)據(jù)是中文數(shù)據(jù)而且是非敏感數(shù)據(jù),那么使用 get;如果用戶輸入的數(shù)據(jù)不是中文字符而且包含敏感數(shù)據(jù),那么還是使用 post為好。
4.、服務(wù)器取值方式不一樣。GET方式取值,如php可以使用$_GET來(lái)取得變量的值,而POST方式通過(guò)$_POST來(lái)獲取變量的值。
(3)HEAD
HEAD 方法與 GET 方法幾乎是相同的,它們的區(qū)別在于 HEAD 方法只是請(qǐng)求消息報(bào)頭,而不是完整的內(nèi)容。對(duì)于 HEAD 請(qǐng)求的回應(yīng)部分來(lái)說(shuō),它的 HTTP 頭部中包含的信息與通過(guò) GET 請(qǐng)求所得到的信息是相同的。利用這個(gè)方法,不必傳輸整個(gè)資源內(nèi)容,就可以得到 Request-URI 所標(biāo)識(shí)的資源的信息。這個(gè)方法通常被用于測(cè)試超鏈接的有效性,是否可以訪問(wèn),以及最近是否更新。
要注意的是,在 HTML 文檔中,書(shū)寫(xiě) get 和 post,大小寫(xiě)都可以,但在 HTTP 協(xié)議中的 GET 和 POST 只能是大寫(xiě)形式。
2. 請(qǐng)求頭
每個(gè)頭域由一個(gè)域名,冒號(hào)(:)和域值三部分組成。域名是大小寫(xiě)無(wú)關(guān)的,域值前可以添加任何數(shù)量的空格符,頭域可以被擴(kuò)展為多行,在每行開(kāi)始處,使用至少一個(gè)空格或制表符。
HTTP最常見(jiàn)的請(qǐng)求頭如下:
Transport 頭域
Connection:
作用:表示是否需要持久連接。
如果服務(wù)器看到這里的值為“Keep-Alive”,或者看到請(qǐng)求使用的是HTTP 1.1(HTTP 1.1默認(rèn)進(jìn)行持久連接),它就可以利用持久連接的優(yōu)點(diǎn),當(dāng)頁(yè)面包含多個(gè)元素時(shí)(例如Applet,圖片),顯著地減少下載所需要的時(shí)間。要實(shí)現(xiàn)這一點(diǎn),服務(wù)器需要在應(yīng)答中發(fā)送一個(gè)Content-Length頭,最簡(jiǎn)單的實(shí)現(xiàn)方法是:先把內(nèi)容寫(xiě)入 ByteArrayOutputStream,然后在正式寫(xiě)出內(nèi)容之前計(jì)算它的大小;
例如: Connection: keep-alive 當(dāng)一個(gè)網(wǎng)頁(yè)打開(kāi)完成后,客戶端和服務(wù)器之間用于傳輸HTTP數(shù)據(jù)的TCP連接不會(huì)關(guān)閉,如果客戶端再次訪問(wèn)這個(gè)服務(wù)器上的 網(wǎng)頁(yè),會(huì)繼續(xù)使用這一條已經(jīng)建立的連接
例如: Connection: close 代表一個(gè)Request完成后,客戶端和服務(wù)器之間用于傳輸HTTP數(shù)據(jù)的TCP連接會(huì)關(guān)閉, 當(dāng)客戶端再次發(fā)送Request,需要重新建立TCP連接。
Host(發(fā)送請(qǐng)求時(shí),該報(bào)頭域是必需的)
Host請(qǐng)求報(bào)頭域主要用于指定被請(qǐng)求資源的Internet主機(jī)和端口號(hào),它通常從HTTP URL中提取出來(lái)的。
eg:http://;localhost/index.html
瀏覽器發(fā)送的請(qǐng)求消息中,就會(huì)包含Host請(qǐng)求報(bào)頭域,如下:
Host:localhost
此處使用缺省端口號(hào)80,若指定了端口號(hào)8080,則變成:Host:localhost:8080
Client 頭域
Accept:
作用:瀏覽器可以接受的媒體類(lèi)型(MIME類(lèi)型),
例如: Accept: text/html 代表瀏覽器可以接受服務(wù)器回發(fā)的類(lèi)型為 text/html 也就是我們常說(shuō)的html文檔, 如果服務(wù)器無(wú)法返回text/html類(lèi)型的數(shù)據(jù),服務(wù)器應(yīng)該返回一個(gè)406錯(cuò)誤(non acceptable)。
通配符 * 代表任意類(lèi)型。例如 Accept: */* 代表瀏覽器可以處理所有類(lèi)型,(一般瀏覽器發(fā)給服務(wù)器都是發(fā)這個(gè))
Accept-Encoding:
作用: 瀏覽器申明自己接收的編碼方法,通常指定壓縮方法,是否支持壓縮,支持什么壓縮方法(gzip,deflate),(注意:這不是只字符編碼);
例如: Accept-Encoding: gzip, deflate。Server能夠向支持gzip/deflate的瀏覽器返回經(jīng)gzip或者deflate編碼的HTML頁(yè)面。 許多情形下這可以減少5到10倍的下載時(shí)間,也節(jié)省帶寬。
Accept-Language:
作用: 瀏覽器申明自己接收的語(yǔ)言。
語(yǔ)言跟字符集的區(qū)別:中文是語(yǔ)言,中文有多種字符集,比如big5,gb2312,gbk等等;
例如: Accept-Language:zh-cn 。如果請(qǐng)求消息中沒(méi)有設(shè)置這個(gè)報(bào)頭域,服務(wù)器假定客戶端對(duì)各種語(yǔ)言都可以接受。
User-Agent:
作用:告訴HTTP服務(wù)器, 客戶端使用的操作系統(tǒng)和瀏覽器的名稱(chēng)和版本.
我們上網(wǎng)登陸論壇的時(shí)候,往往會(huì)看到一些歡迎信息,其中列出了你的操作系統(tǒng)的名稱(chēng)和版本,你所使用的瀏覽器的名稱(chēng)和版本,這往往讓很多人感到很神 奇,實(shí)際上, 服務(wù)器應(yīng)用程序就是從User-Agent這個(gè)請(qǐng)求報(bào)頭域中獲取到這些信息User-Agent請(qǐng)求報(bào)頭域允許客戶端將它的操作系統(tǒng)、瀏覽 器和其它屬性告訴服務(wù)器。
例如: User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; CIBA; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET4.0C; InfoPath.2; .NET4.0E)
Accept-Charset:
作用:瀏覽器申明自己接收的字符集,這就是本文前面介紹的各種字符集和字符編碼,如gb2312,utf-8(通常我們說(shuō)Charset包括了相應(yīng)的字符編碼方案);
例如:Accept-Charset:iso-8859-1,gb2312.如果在請(qǐng)求消息中沒(méi)有設(shè)置這個(gè)域,缺省是任何字符集都可以接受。
Authorization:授權(quán)信息,通常出現(xiàn)在對(duì)服務(wù)器發(fā)送的WWW-Authenticate頭的應(yīng)答中;
Authorization請(qǐng)求報(bào)頭域主要用于證明客戶端有權(quán)查看某個(gè)資源。當(dāng)瀏覽器訪問(wèn)一個(gè)頁(yè)面時(shí),如果收到服務(wù)器的響應(yīng)代碼為401(未授權(quán)),可以發(fā)送一個(gè)包含Authorization請(qǐng)求報(bào)頭域的請(qǐng)求,要求服務(wù)器對(duì)其進(jìn)行驗(yàn)證。
Cookie/Login 頭域
Cookie:
作用: 最重要的header, 將cookie的值發(fā)送給HTTP 服務(wù)器
Entity頭域
Content-Length
作用:發(fā)送給HTTP服務(wù)器數(shù)據(jù)的長(zhǎng)度。即請(qǐng)求消息正文的長(zhǎng)度;
例如: Content-Length: 38
Content-Type:
作用:
例如:Content-Type: application/x-www-form-urlencoded
Miscellaneous 頭域
Referer:
作用: 提供了Request的上下文信息的服務(wù)器,告訴服務(wù)器我是從哪個(gè)鏈接過(guò)來(lái)的,比如從我主頁(yè)上鏈接到一個(gè)朋友那里, 他的服務(wù)器就能夠從HTTP Referer中統(tǒng)計(jì)出每天有多少用戶點(diǎn)擊我主頁(yè)上的鏈接訪問(wèn) 他的網(wǎng)站。
例如: Referer:http://translate.google.cn/?hl=zh-cn&tab=wT
Cache 頭域
If-Modified-Since:
作用: 把瀏覽器端緩存頁(yè)面的最后修改時(shí)間發(fā)送到服務(wù)器去,服務(wù)器會(huì)把這個(gè)時(shí)間與服務(wù)器上實(shí)際文件的最后修改時(shí)間進(jìn)行對(duì)比。如果時(shí)間一致,那么返回304,客戶端 就直接使用本地緩存文件。如果時(shí)間不一致,就會(huì)返回200和新的文件內(nèi)容。客戶端接到之后,會(huì)丟棄舊文件,把新文件緩存起來(lái),并顯示在瀏覽器中。
例如:If-Modified-Since: Thu, 09 Feb 2012 09:07:57 GMT。
If-None-Match:
作用: If-None-Match和ETag一起工作,工作原理是在HTTP Response中添加ETag信息。 當(dāng)用戶再次請(qǐng)求該資源時(shí),將在HTTP Request 中加入If-None-Match信息(ETag的值)。如果服務(wù)器驗(yàn)證資源的ETag沒(méi)有改變(該資源沒(méi)有更新),將返回一個(gè)304狀態(tài)告訴客戶端使用 本地緩存文件。否則將返回200狀態(tài)和新的資源和Etag. 使用這樣的機(jī)制將提高網(wǎng)站的性能
例如: If-None-Match: "03f2b33c0bfcc1:0"
Pragma:
作用: 防止頁(yè)面被緩存, 在HTTP/1.1版本中,它和Cache-Control:no-cache作用一模一樣
Pargma只有一個(gè)用法, 例如: Pragma: no-cache
注意: 在HTTP/1.0版本中,只實(shí)現(xiàn)了Pragema:no-cache, 沒(méi)有實(shí)現(xiàn)Cache-Control
Cache-Control:
作用: 這個(gè)是非常重要的規(guī)則。 這個(gè)用來(lái)指定Response-Request遵循的緩存機(jī)制。各個(gè)指令含義如下
Cache-Control:Public 可以被任何緩存所緩存()
Cache-Control:Private 內(nèi)容只緩存到私有緩存中
Cache-Control:no-cache 所有內(nèi)容都不會(huì)被緩存
2. HTTP響應(yīng)格式
在接收和解釋請(qǐng)求消息后,服務(wù)器會(huì)返回一個(gè) HTTP 響應(yīng)消息。與 HTTP 請(qǐng)求類(lèi)似,HTTP 響應(yīng)也是由三個(gè)部分組成,分別是:狀態(tài)行、消息報(bào)頭和響應(yīng)正文。如:
HTTP/1.1 200 OK
Date: Sun, 17 Mar 2013 08:12:54 GMT
Server: Apache/2.2.8 (Win32) PHP/5.2.5
X-Powered-By: PHP/5.2.5
Set-Cookie: PHPSESSID=c0huq7pdkmm5gg6osoe3mgjmm3; path=/
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 4393
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html; charset=utf-8
<html>
<head>
<title>HTTP響應(yīng)示例<title>
</head>
<body>
Hello HTTP!
</body>
</html>
1、狀態(tài)行
狀態(tài)行由協(xié)議版本、數(shù)字形式的狀態(tài)代碼,及相應(yīng)的狀態(tài)描述組成,各元素之間以空格分隔,結(jié)尾時(shí)回車(chē)換行符,格式如下:
HTTP-Version Status-Code Reason-Phrase CRLF
HTTP-Version 表示服務(wù)器 HTTP 協(xié)議的版本,Status-Code 表示服務(wù)器發(fā)回的響應(yīng)代碼,Reason-Phrase 表示狀態(tài)代碼的文本描述,CRLF 表示回車(chē)換行。例如:
HTTP/1.1 200 OK (CRLF)
狀態(tài)代碼與狀態(tài)描述
狀態(tài)代碼由 3 位數(shù)字組成, 表示請(qǐng)求是否被理解或被滿足,狀態(tài)描述給出了關(guān)于狀態(tài)碼的簡(jiǎn)短的文字描述。狀態(tài)碼的第一個(gè)數(shù)字定義了響應(yīng)類(lèi)別,后面兩位數(shù)字沒(méi)有具體分類(lèi)。第一個(gè)數(shù)字有 5 種取值,如下所示。
常見(jiàn)狀態(tài)代碼、狀態(tài)描述、說(shuō)明:
200 OK //客戶端請(qǐng)求成功
400 Bad Request //客戶端請(qǐng)求有語(yǔ)法錯(cuò)誤,不能被服務(wù)器所理解
401 Unauthorized //請(qǐng)求未經(jīng)授權(quán),這個(gè)狀態(tài)代碼必須和WWW-Authenticate報(bào)頭域一起使用
403 Forbidden //服務(wù)器收到請(qǐng)求,但是拒絕提供服務(wù)
404 Not Found //請(qǐng)求資源不存在,eg:輸入了錯(cuò)誤的URL
500 Internal Server Error //服務(wù)器發(fā)生不可預(yù)期的錯(cuò)誤
503 Server Unavailable //服務(wù)器當(dāng)前不能處理客戶端的請(qǐng)求,一段時(shí)間后可能恢復(fù)正常
2、響應(yīng)正文
響應(yīng)正文就是服務(wù)器返回的資源的內(nèi)容,響應(yīng)頭和正文之間也必須用空行分隔。如:
3 、響應(yīng)頭信息
HTTP最常見(jiàn)的響應(yīng)頭如下所示:
Cache頭域
Date:
作用:生成消息的具體時(shí)間和日期,即當(dāng)前的GMT時(shí)間。
例如: Date: Sun, 17 Mar 2013 08:12:54 GMT
Expires:
作用: 瀏覽器會(huì)在指定過(guò)期時(shí)間內(nèi)使用本地緩存,指明應(yīng)該在什么時(shí)候認(rèn)為文檔已經(jīng)過(guò)期,從而不再緩存它。
例如: Expires: Thu, 19 Nov 1981 08:52:00 GMT
Vary
作用:
例如: Vary: Accept-Encoding
Cookie/Login 頭域
P3P
作用: 用于跨域設(shè)置Cookie, 這樣可以解決iframe跨域訪問(wèn)cookie的問(wèn)題
例如: P3P: CP=CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR
Set-Cookie
作用: 非常重要的header, 用于把cookie 發(fā)送到客戶端瀏覽器, 每一個(gè)寫(xiě)入cookie都會(huì)生成一個(gè)Set-Cookie.
例如: Set-Cookie: PHPSESSID=c0huq7pdkmm5gg6osoe3mgjmm3; path=/
Entity實(shí)體頭域:
實(shí)體內(nèi)容的屬性,包括實(shí)體信息類(lèi)型,長(zhǎng)度,壓縮方法,最后一次修改時(shí)間,數(shù)據(jù)有效性等。
ETag:
作用: 和If-None-Match 配合使用。 (實(shí)例請(qǐng)看上節(jié)中If-None-Match的實(shí)例)
例如: ETag: "03f2b33c0bfcc1:0"
Last-Modified:
作用: 用于指示資源的最后修改日期和時(shí)間。(實(shí)例請(qǐng)看上節(jié)的If-Modified-Since的實(shí)例)
例如: Last-Modified: Wed, 21 Dec 2011 09:09:10 GMT
Content-Type:
作用:WEB服務(wù)器告訴瀏覽器自己響應(yīng)的對(duì)象的類(lèi)型和字符集,
例如:
Content-Type: text/html; charset=utf-8
Content-Type:text/html;charset=GB2312
Content-Type: image/jpeg
Content-Length:
指明實(shí)體正文的長(zhǎng)度,以字節(jié)方式存儲(chǔ)的十進(jìn)制數(shù)字來(lái)表示。在數(shù)據(jù)下行的過(guò)程中,Content-Length的方式要預(yù)先在服務(wù)器中緩存所有數(shù)據(jù),然后所有數(shù)據(jù)再一股腦兒地發(fā)給客戶端。
例如: Content-Length: 19847
Content-Encoding:
作用:文檔的編碼(Encode)方法。一般是壓縮方式。
WEB服務(wù)器表明自己使用了什么壓縮方法(gzip,deflate)壓縮響應(yīng)中的對(duì)象。利用gzip壓縮文檔能夠顯著地減少HTML文檔的下載時(shí)間。
例如:Content-Encoding:gzip
Content-Language:
作用: WEB服務(wù)器告訴瀏覽器自己響應(yīng)的對(duì)象的語(yǔ)言者
例如: Content-Language:da
Miscellaneous 頭域
Server:
作用:指明HTTP服務(wù)器的軟件信息
例如:Apache/2.2.8 (Win32) PHP/5.2.5
X-Powered-By:
作用:表示網(wǎng)站是用什么技術(shù)開(kāi)發(fā)的
例如: X-Powered-By: PHP/5.2.5
Transport頭域
Connection:
例如: Connection: keep-alive 當(dāng)一個(gè)網(wǎng)頁(yè)打開(kāi)完成后,客戶端和服務(wù)器之間用于傳輸HTTP數(shù)據(jù)的TCP連接不會(huì)關(guān)閉,如果客戶端再次訪問(wèn)這個(gè)服務(wù)器上的網(wǎng)頁(yè),會(huì)繼續(xù)使用這一條已經(jīng)建立的連接
例如: Connection: close 代表一個(gè)Request完成后,客戶端和服務(wù)器之間用于傳輸HTTP數(shù)據(jù)的TCP連接會(huì)關(guān)閉, 當(dāng)客戶端再次發(fā)送Request,需要重新建立TCP連接。
Location頭域
Location:
作用: 用于重定向一個(gè)新的位置, 包含新的URL地址
實(shí)例請(qǐng)看304狀態(tài)實(shí)例
HTTP協(xié)議是無(wú)狀態(tài)的和Connection: keep-alive的區(qū)別
無(wú)狀態(tài)是指協(xié)議對(duì)于事務(wù)處理沒(méi)有記憶能力,服務(wù)器不知道客戶端是什么狀態(tài)。從另一方面講,打開(kāi)一個(gè)服務(wù)器上的網(wǎng)頁(yè)和你之前打開(kāi)這個(gè)服務(wù)器上的網(wǎng)頁(yè)之間沒(méi)有任何聯(lián)系。
HTTP是一個(gè)無(wú)狀態(tài)的面向連接的協(xié)議,無(wú)狀態(tài)不代表HTTP不能保持TCP連接,更不能代表HTTP使用的是UDP協(xié)議(無(wú)連接)。
從HTTP/1.1起,默認(rèn)都開(kāi)啟了Keep-Alive,保持連接特性,簡(jiǎn)單地說(shuō),當(dāng)一個(gè)網(wǎng)頁(yè)打開(kāi)完成后,客戶端和服務(wù)器之間用于傳輸HTTP數(shù)據(jù)的TCP連接不會(huì)關(guān)閉,如果客戶端再次訪問(wèn)這個(gè)服務(wù)器上的網(wǎng)頁(yè),會(huì)繼續(xù)使用這一條已經(jīng)建立的連接。
Keep-Alive不會(huì)永久保持連接,它有一個(gè)保持時(shí)間,可以在不同的服務(wù)器軟件(如Apache)中設(shè)定這個(gè)時(shí)間。
3. 瀏覽器緩存
瀏覽器緩存:包括頁(yè)面html緩存和圖片js,css等資源的緩存。如下圖,瀏覽器緩存是基于把頁(yè)面信息保存到用戶本地電腦硬盤(pán)里。
1、緩存的優(yōu)點(diǎn):
1)服務(wù)器響應(yīng)更快:因?yàn)檎?qǐng)求從緩存服務(wù)器(離客戶端更近)而不是源服務(wù)器被相應(yīng),這個(gè)過(guò)程耗時(shí)更少,讓服務(wù)器看上去響應(yīng)更快。
2)減少網(wǎng)絡(luò)帶寬消耗:當(dāng)副本被重用時(shí)會(huì)減低客戶端的帶寬消耗;客戶可以節(jié)省帶寬費(fèi)用,控制帶寬的需求的增長(zhǎng)并更易于管理。
2、緩存工作原理
頁(yè)面緩存狀態(tài)是由http header決定的,一個(gè)瀏覽器請(qǐng)求信息,一個(gè)是服務(wù)器響應(yīng)信息。主要包括Pragma: no-cache、Cache-Control、 Expires、 Last-Modified、If-Modified-Since。其中Pragma: no-cache由HTTP/1.0規(guī)定,Cache-Control由HTTP/1.1規(guī)定。
工作原理圖:
從圖中我們可以看到原理主要分三步:
與緩存相關(guān)的HTTP擴(kuò)展消息頭
Expires:設(shè)置頁(yè)面過(guò)期時(shí)間,格林威治時(shí)間GMT
Cache-Control:更細(xì)致的控制緩存的內(nèi)容
Last-Modified:請(qǐng)求對(duì)象最后一次的修改時(shí)間 用來(lái)判斷緩存是否過(guò)期 通常由文件的時(shí)間信息產(chǎn)生
ETag:響應(yīng)中資源的校驗(yàn)值,在服務(wù)器上某個(gè)時(shí)段是唯一標(biāo)識(shí)的。ETag是一個(gè)可以 與Web資源關(guān)聯(lián)的記號(hào)(token),和Last-Modified功能才不多,也是一個(gè)標(biāo)識(shí)符,一般和Last-Modified一起使用,加強(qiáng)服務(wù)器判斷的準(zhǔn)確度。
Date:服務(wù)器的時(shí)間
If-Modified-Since:客戶端存取的該資源最后一次修改的時(shí)間,用來(lái)和服務(wù)器端的Last-Modified做比較
If-None-Match:客戶端存取的該資源的檢驗(yàn)值,同ETag。
Cache-Control的主要參數(shù)
Cache-Control: private/public Public 響應(yīng)會(huì)被緩存,并且在多用戶間共享。 Private 響應(yīng)只能夠作為私有的緩存,不能再用戶間共享。
Cache-Control: no-cache:不進(jìn)行緩存
Cache-Control: max-age=x:緩存時(shí)間 以秒為單位
Cache-Control: must-revalidate:如果頁(yè)面是過(guò)期的 則去服務(wù)器進(jìn)行獲取。
2、關(guān)于圖片,css,js,flash的緩存
這個(gè)主要通過(guò)服務(wù)器的配置來(lái)實(shí)現(xiàn)這個(gè)技術(shù),如果使用apache服務(wù)器的話,可以使用mod_expires模塊來(lái)實(shí)現(xiàn):
編譯mod_expires模塊:
Cd /root/httpd-2.2.3/modules/metadata
/usr/local/apache/bin/apxs -i -a -c mod_expires.c //編譯
編輯httpd.conf配置:添加下面內(nèi)容
<IfModule mod_expires.c>
ExpiresActive on
ExpiresDefault "access plus 1 month"
ExpiresByType text/html "access plus 1 months"
ExpiresByType text/css "access plus 1 months"
ExpiresByType image/gif "access plus 1 months"
ExpiresByType image/jpeg "access plus 1 months"
ExpiresByType image/jpg "access plus 1 months"
ExpiresByType image/png "access plus 1 months"
EXpiresByType application/x-shockwave-flash "access plus 1 months"
EXpiresByType application/x-javascript "access plus 1 months"
#ExpiresByType video/x-flv "access plus 1 months"
</IfModule>
解釋?zhuān)旱谝痪?-開(kāi)啟服務(wù)
第二句--默認(rèn)時(shí)間是一個(gè)月
在下面是關(guān)于各種類(lèi)型的資源的緩存時(shí)間設(shè)置
首先我們看圖說(shuō)話
這是在沒(méi)有緩存的情況下,這個(gè)頁(yè)面發(fā)送了很多靜態(tài)資源的請(qǐng)求:
可以看到,靜態(tài)資源占用了整個(gè)頁(yè)面加載用時(shí)的90%以上,而且這個(gè)靜態(tài)資源還是已經(jīng)在我使用了nginx配置壓縮以后的大小,如果沒(méi)有對(duì)這些靜態(tài)資源壓縮的話,那么靜態(tài)資源加載應(yīng)該會(huì)占用這個(gè)頁(yè)面展示99%以上的時(shí)間。聽(tīng)起來(lái)是不是已經(jīng)被嚇到了,但是數(shù)據(jù)已經(jīng)擺在這里了,這可不是危言聳聽(tīng)。
然后再看看使用了nginx緩存之后的效果圖:
看到?jīng)]有,朋友們,整個(gè)頁(yè)面只有請(qǐng)求接口的時(shí)間和從本地磁盤(pán)加載css的時(shí)間。頁(yè)面加載速度直接提升10倍以上!并且由于我這個(gè)頁(yè)面沒(méi)有采用前后端分離的方式,所以html沒(méi)有緩存下來(lái),如果采用了前后端分離架構(gòu)的話,就連html都可以直接緩存,那提升的速度可想而知。當(dāng)然由于瀏覽器或者手機(jī)端對(duì)頁(yè)面加載的優(yōu)化我們并不能很直觀的感受到10倍的提升,實(shí)際上以肉眼觀察的話,差不多減少了一半的時(shí)間,并且由于并沒(méi)有向后端服務(wù)器請(qǐng)求這些靜態(tài)資源,也相當(dāng)于對(duì)后端服務(wù)器做了一層保護(hù)措施。
首先在http模塊加配置:
# 開(kāi)啟gzip gzip on; # 啟用gzip壓縮的最小文件,小于設(shè)置值的文件將不會(huì)壓縮 gzip_min_length 1k; # gzip 壓縮級(jí)別,1-10,數(shù)字越大壓縮的越好,也越占用CPU時(shí)間。一般設(shè)置1和2 gzip_comp_level 2; # 進(jìn)行壓縮的文件類(lèi)型。javascript有多種形式。其中的值可以在 mime.types 文件中找到。 gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png; # 是否在http header中添加Vary: Accept-Encoding,建議開(kāi)啟 gzip_vary on; # 禁用IE 6 gzip gzip_disable "MSIE [1-6]\."; # 設(shè)置緩存路徑并且使用一塊最大100M的共享內(nèi)存,用于硬盤(pán)上的文件索引,包括文件名和請(qǐng)求次數(shù),每個(gè)文件在1天內(nèi)若不活躍(無(wú)請(qǐng)求)則從硬盤(pán)上淘汰,硬盤(pán)緩存最大10G,滿了則根據(jù)LRU算法自動(dòng)清除緩存。 proxy_cache_path /var/cache/nginx/cache levels=1:2 keys_zone=imgcache:100m inactive=1d max_size=10g;
關(guān)于模塊以及nginx配置信息在上一篇文章有說(shuō)明。
可以看到在http模塊中主要是使用gzip壓縮,最后一個(gè)配置才是和緩存有關(guān)的配置。
然后是server中加上location配置
location ~* ^.+\.(css|js|ico|gif|jpg|jpeg|png)$ { log_not_found off; # 關(guān)閉日志 access_log off; # 緩存時(shí)間7天 expires 7d; # 源服務(wù)器 proxy_pass http://localhost:8888; # 指定上面設(shè)置的緩存區(qū)域 proxy_cache imgcache; # 緩存過(guò)期管理 proxy_cache_valid 200 302 1d; proxy_cache_valid 404 10m; proxy_cache_valid any 1h; proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504; } ?
加上這兩塊配置之后,就能享受到緩存給你帶來(lái)的快樂(lè)了。當(dāng)然系統(tǒng)優(yōu)化之路還是相當(dāng)漫長(zhǎng)的,nginx緩存只是其中的一塊而已,想要把系統(tǒng)達(dá)到完美還需要在很多地方下功夫,比如這些靜態(tài)資源完全可以直接在客戶端緩存,這樣連請(qǐng)求都不會(huì)往服務(wù)端發(fā)了,更大的減輕服務(wù)器的壓力。
存是個(gè)老生長(zhǎng)談的問(wèn)題,對(duì)于前端工程師來(lái)講更是我們的必修課。或許很多人會(huì)說(shuō)我的項(xiàng)目并沒(méi)有問(wèn)題,根本不需要聊什么緩存。如果真的是這樣,只能證明你前端道路才剛剛開(kāi)始。
小郭今天分享緩存的原因在于:公司的一個(gè)核心APP中嵌入了SPA,而且應(yīng)用核心都分布在SPA中,功能復(fù)雜且重。問(wèn)題出現(xiàn)了:應(yīng)用核心頁(yè)面打開(kāi)一直處于加載狀態(tài),排除掉弱網(wǎng)環(huán)境的原因,重點(diǎn)就在于沒(méi)有緩存,每次進(jìn)入頁(yè)面都需要重載DOM和數(shù)據(jù),拖慢頁(yè)面打開(kāi)速度。
那應(yīng)該處理緩存問(wèn)題呢?接下來(lái)小郭從三個(gè)方向來(lái)講解。
在了解瀏覽器緩存前,我們需要先了解一下相關(guān)的概念:cache-control,expires,last-Modified,ETag。
瀏覽器通過(guò)請(qǐng)求頭實(shí)現(xiàn)緩存,關(guān)鍵的請(qǐng)求頭有cache-control,expires,last-Modified,ETag等。我們從時(shí)間和空間兩個(gè)角度來(lái)看瀏覽器緩存。
時(shí)間
瀏覽器發(fā)送第一次請(qǐng)求:不緩存,服務(wù)端根據(jù)設(shè)定的緩存策略返回相應(yīng)的header,如:cache-control,expires,last-Modified,ETag。
瀏覽器發(fā)送第二次請(qǐng)求:
空間
如果緩存就按理論上設(shè)置,那就太簡(jiǎn)單了。在實(shí)際應(yīng)用有個(gè)嚴(yán)重的問(wèn)題,我們不僅要緩存代碼,還需要更新代碼。如果靜態(tài)資源名字不變,怎么讓瀏覽器即能緩存又能在有新代碼時(shí)更新。最簡(jiǎn)單的解決方式就是靜態(tài)資源路徑添加一個(gè)版本值,版本不變就走緩存策略,版本變了就加載新資源。如下:
<script src="xx/xx.js?v=24334452"></script>
然而這種處理方式在部署時(shí)有問(wèn)題。
解決方法:靜態(tài)資源和頁(yè)面是分開(kāi)部署
這些問(wèn)題的本質(zhì)是以上的部署方式是“覆蓋式發(fā)布”,解決方式是“非覆蓋式發(fā)布”。即用靜態(tài)資源的文件摘要信息給文件命名,這樣每次更新資源不會(huì)覆蓋原來(lái)的資源,先將資源發(fā)布上去。這時(shí)候存在兩種資源,用戶用舊頁(yè)面訪問(wèn)舊資源,然后再更新頁(yè)面,用戶變成新頁(yè)面訪問(wèn)新資源,就能做到無(wú)縫切換。簡(jiǎn)單來(lái)說(shuō)就是給靜態(tài)文件名加hash值。
那如何實(shí)現(xiàn)呢?
現(xiàn)在前端代碼都用webpack之類(lèi)的構(gòu)建工具打包,那么結(jié)合webpack該怎么做,怎么才能做到持久化緩存?
一、webpack給文件名添加hash值是很簡(jiǎn)單的,但hash/chunkhash/contenthash要用哪個(gè)呢?
官方定義
hash: unique hash generated for every build
chunkhash: hashes based on each chunks' content
contenthash: hashes generated for extracted content
根據(jù)分析,contenthash才是我們需要的,內(nèi)容有更新,hash值才會(huì)更新。
二、webpack會(huì)打包業(yè)務(wù)代碼、第三方庫(kù)及運(yùn)行時(shí)代碼,為保證緩存互不干擾,應(yīng)該將它們提取出來(lái)。
第三方庫(kù)提取方式是設(shè)置optimization的splitChunks的cacheGroups。splitChunks能提取模塊,cacheGroups能緩存模塊,并且cacheGroups的配置會(huì)覆蓋splitChunks相同配置,既能提取又能緩存,故只需設(shè)置cacheGroups。
運(yùn)行時(shí)代碼的提取方式為配置runtimeChunk,默認(rèn)為false,表示運(yùn)行時(shí)代碼嵌入到不同的chunk文件中;現(xiàn)在將運(yùn)行時(shí)代碼提取出來(lái),并命名為manifest。
module.exports = {
entry: {
index: "./src/index.js",
bar: "./src/bar.js"
},
output: {
filename: "[name].[contenthash].js"
},
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test:/[\\/]node_modules[\\/]/,
name: "vendors",
chunks: "all"
}
}
},
runtimeChunk: {
name: "manifest"
}
}
};
三、 moduleName 和 chunkName 對(duì)文件的影響
module:就是js模塊
chunk:webpack編譯過(guò)程中由多個(gè)module組成的文件
bundle:bundle是chunk文件的最終狀態(tài),是webpack編譯后的結(jié)果
一個(gè)文件被分離為3個(gè)文件,文件間怎么相互依賴的,會(huì)影響彼此打包,解決方法是將moduleId和chunkId改成按照文件路徑生成。
optimization: {
moduleIds: 'hashed',
namedModules: true,
namedChunks: true
}
這樣子moduleId在編譯后的文件是文件目錄的hash值,更加安全。這也是namedChunks在production默認(rèn)為false的原因,不想依賴的文件路徑在編譯后的文件直接展示,但是為了持久性緩存,這里也只能打開(kāi)。
四、CSS文件緩存
當(dāng)css代碼提取成單獨(dú)文件,當(dāng)我們改變css時(shí),怎么保證不影響引用它的js文件呢?配置如下:
plugins: [
new MiniCssExtractPlugin({
filename: "[contenthash].css"
})
]
webpack持久化緩存目標(biāo)是當(dāng)且僅當(dāng)該文件內(nèi)容變動(dòng)才改變?cè)撐募值膆ash值
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
output: {
filename: [name].[contenthash].js, // 讓hash值只在內(nèi)容變動(dòng)時(shí)更新
chunkFilename: [name].[contenthash].js // 動(dòng)態(tài)引入的模塊命名,同上
},
module: {
rules: [ {
test: /\.css$/,
use: [
"loader: MiniCssExtractPlugin.loader", // 提取出來(lái)css "css-loader"
]
} ]
},
optimization: {
moduleIds: "hashed", // 混淆文件路徑名
runtimeChunk: { name: 'manifest' }, // 提取runtime代碼命名為manifest
namedModules: true, // 讓模塊id根據(jù)路徑設(shè)置,避免每增加新模塊,所有id都改變,造成緩存失效的情況
namedChunks: true, // 避免增加entrypoint,其他文件都緩存失效
cacheGroups: {
vendor: { // 提取第三方庫(kù)文件
test: /[\\/]node_modules[\\/]/,
name: 'vendors', chunks: 'all',
},
},
}
plugins: [
new webpack.HashedModuleIdsPlugin(), // 與namedModules: true作用一樣
new MiniCssExtractPlugin({
filename: "[contenthash].css", // css文件也是按contenthash命名
chunkFilename: "[contenthash].css", // 動(dòng)態(tài)引入的css命名,同上
})
],
}
瀏覽器有其緩存機(jī)制,想要既能緩存又能在部署時(shí)沒(méi)有問(wèn)題,需要給靜態(tài)文件名添加hash值。在webpack中,有些配置能讓我們實(shí)現(xiàn)持久化緩存。感興趣的同學(xué)可以自行去測(cè)試哦!
有任何問(wèn)題可以在下方留言,想了解更多前端知識(shí)歡迎關(guān)注公眾號(hào)“一郭鮮”,文章也將同步于公眾號(hào),前端學(xué)習(xí)不迷路
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。