常見的HTTP返回碼
上一篇文章里我簡要的說了說HTTP消息頭的格式,注意到在服務(wù)器返回的HTTP消息頭里有一個“HTTP/1.1 200 OK”,這里的200是HTTP規(guī)定的返回代碼,表示請求已經(jīng)被正常處理完成。瀏覽器通過這個返回代碼就可以知道服務(wù)器對所發(fā)請求的處理情況是什么,每一種返回代碼都有自己的含義。這里列舉幾種常見的返回碼。
1 403 Access Forbidden
如果我們試圖請求服務(wù)器上一個文件夾,而在WEB服務(wù)器上這個文件夾并沒有允許對這個文件夾列目錄的話,就會返回這個代碼。一個完整的403回復(fù)可能是這樣的:(IIS5.1)
HTTP/1.1 403 Access Forbidden
Server: Microsoft-IIS/5.1
Date: Mon, 06 Mar 2006 08:57:39 GMT
Connection: close
Content-Type: text/html
Content-Length: 172
<html><head><title>Directory Listing Denied</title></head>
<body><h1>Directory Listing Denied</h1>This Virtual Directory does not allow contents to be listed.</body></html>
2 404 Object not found
當我們請求的對象在服務(wù)器上并不存在時,就會給出這個返回代碼,這可能也是最常見的錯誤代碼了。IIS給出的404消息內(nèi)容很長,除了消息頭以外還有一個完整的說明“為什么會這樣”的網(wǎng)頁。APACHE服務(wù)器的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>
也許你會問,無論是404還是200,都會在消息體內(nèi)給出一個說明網(wǎng)頁,那么對于客戶端來說二者有什么區(qū)別呢?一個比較明顯的區(qū)別在于200是成功請求,瀏覽器會記錄下這個地址,以便下次再訪問時可以自動提示該地址,而404是失敗請求,瀏覽器只會顯示出返回的頁面內(nèi)容,并不會記錄此地址,要再次訪問時還需要輸入完整的地址。
3 401 Access Denied
當WEB服務(wù)器不允許匿名訪問,而我們又沒有提供正確的用戶名/密碼時,服務(wù)器就會給出這個返回代碼。在IIS中,設(shè)置IIS的安全屬性為不允許匿名訪問(如下圖),此時直接訪問的話就會得到以下返回結(jié)果:
HTTP/1.1 401 Access Denied
Server: Microsoft-IIS/5.1
Date: Mon, 06 Mar 2006 09:15:55 GMT
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
Connection: close
Content-Length: 3964
Content-Type: text/html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html dir=ltr>
……
此時瀏覽器上給出的提示如下圖,讓我們輸入用戶名和密碼:
因返回信息中消息體較長,只取前面兩行內(nèi)容。注意,如果是用localhost來訪問本機的IIS,因IE可以直接取得當前用戶的身份,它會和服務(wù)器間直接進行協(xié)商,所以不會看到401提示。
當我們在輸入了用戶名和密碼以后,服務(wù)器與客戶端會再進行兩次對話。首先客戶端向服務(wù)器索取一個公鑰,服務(wù)器端會返回一個公鑰,二者都用BASE64編碼,相應(yīng)的消息如下(編碼部分已經(jīng)做了處理):
GET / HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
Host: 192.168.0.55:8080
Connection: Keep-Alive
Authorization: Negotiate ABCDEFG……
HTTP/1.1 401 Access Denied
Server: Microsoft-IIS/5.1
Date: Mon, 06 Mar 2006 09:20:53 GMT
WWW-Authenticate: Negotiate HIJKLMN……
Content-Length: 3715
Content-Type: text/html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html dir=ltr>
……
客戶端拿到公鑰之后使用公鑰對用戶名和密碼進行加密碼,然后把加密以后的結(jié)果重新發(fā)給服務(wù)器:
GET / HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
Host: 192.168.0.55:8080
Connection: Keep-Alive
Authorization: Negotiate OPQRST……
這樣,如果驗證通過,服務(wù)器端就會把請求的內(nèi)容發(fā)送過來了,也就是說禁止匿名訪問的網(wǎng)站會經(jīng)過三次請求才可以看到頁面。但因為客戶端瀏覽器已經(jīng)緩存了公鑰,用同一個瀏覽器窗口再次請求這個網(wǎng)站上的其它頁面時就可以直接發(fā)送驗證信息,從而一次交互就可以完成了。
4 302 Object Moved
用過ASP的人都知道ASP中頁面重定向至少有Redirect和Transfer兩種方法。二的區(qū)別在于Redirect是客戶端重定向,而Transfer是服務(wù)器端重定向,那么它們具體是如何通過HTTP消息頭實現(xiàn)的呢?
先來看一下Transfer的例子:
例如ASP文件1.asp只有一行
<% Server.Transfer "1.htm" %>
HTML文件1.htm也只有一行:
<p>this is 1.htm</p>
如果我們從瀏覽器里請求1.asp,發(fā)送的請求是:
GET /1.asp HTTP/1.1
Accept: */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
Host: localhost:8080
Connection: Keep-Alive
Cookie: ASPSESSIONIDACCTRTTT=PKKDJOPBAKMAMBNANIPIFDAP
注意請求的文件確實是1.asp,而得到的回應(yīng)則是:
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.1
Date: Mon, 06 Mar 2006 12:52:44 GMT
X-Powered-By: ASP.NET
Content-Length: 20
Content-Type: text/html
Cache-control: private
<p>this is 1.htm</p>
不難看出,通過Server.Transfer語句服務(wù)器端已經(jīng)做了頁面重定向,而客戶端對此一無所知,表面上看上去得到的就是1.asp的結(jié)果。
如果把1.asp的內(nèi)容改為:
<% Response.Redirect "1.htm" %>
再次請求1.asp,發(fā)送的請求沒有變化,得到的回應(yīng)卻變成了:
HTTP/1.1 302 Object moved
Server: Microsoft-IIS/5.1
Date: Mon, 06 Mar 2006 12:55:57 GMT
X-Powered-By: ASP.NET
Location: 1.htm
Content-Length: 121
Content-Type: text/html
Cache-control: private
<head><title>Object moved</title></head>
<body><h1>Object Moved</h1>This object may be found <a HREF="">here</a>.</body>
注意HTTP的返回代碼由200變成了302,表示這是一個重定向消息,客戶端需要根據(jù)消息頭中Location字段的值重新發(fā)送請求,于是就有了下面一組對話:
GET /1.htm HTTP/1.1
Accept: */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
Host: localhost:8080
Connection: Keep-Alive
If-Modified-Since: Thu, 02 Mar 2006 06:50:13 GMT
If-None-Match: "b224758ec53dc61:9f0"
Cookie: ASPSESSIONIDACCTRTTT=PKKDJOPBAKMAMBNANIPIFDAP
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.1
X-Powered-By: ASP.NET
Date: Mon, 06 Mar 2006 12:55:57 GMT
Content-Type: text/html
Accept-Ranges: bytes
Last-Modified: Mon, 06 Mar 2006 12:52:32 GMT
ETag: "76d85bd51c41c61:9f0"
Content-Length: 20
<p>this is 1.htm</p>
很明顯,兩種重定向方式雖然看上去結(jié)果很像,但在實現(xiàn)原理上有很大的不同。
5 500 Internal Server Error
500號錯誤發(fā)生在服務(wù)器程序有錯誤的時候,例如,ASP程序為
<% if %>
顯然這個程序并不完整,于是得到的結(jié)果為:
HTTP/1.1 500 Internal Server Error
Server: Microsoft-IIS/5.1
Date: Mon, 06 Mar 2006 12:58:55 GMT
X-Powered-By: ASP.NET
Content-Length: 4301
Content-Type: text/html
Expires: Mon, 06 Mar 2006 12:58:55 GMT
Set-Cookie: ASPSESSIONIDACCTRTTT=ALKDJOPBPPKNPCNOEPCNOOPD; path=/
Cache-control: private
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html dir=ltr>
……
服務(wù)器發(fā)送了500號錯誤,并且后面通過HTML的方式說明了錯誤的原因。
理解HTTP消息頭 (三)
(三) 客戶端發(fā)送的內(nèi)容
這一次主要來觀察HTTP消息頭中客戶端的請求,從中找到一些有意思的內(nèi)容。
1 HTTP_REFERER
寫兩個簡單的網(wǎng)頁:
a.htm:
<a href=b.htm>to page b</a>
b.htm:
haha內(nèi)容很簡單,就是網(wǎng)頁A中有一個到B的鏈接。把它們放到IIS上,并訪問網(wǎng)頁A,從中再點擊到B的鏈接,于是看到了B頁的“haha”。那么這兩次請求有什么不同嗎?觀察它們所發(fā)送的HTTP消息頭,最明顯的區(qū)別就是訪問B頁時比訪問A頁時多了一行:
Referer: http://localhost/a.htm這一行就表示,用戶要訪問的B頁是從A頁鏈接過來的。
服務(wù)器端要想取得這個值也是很容易的,以ASP為例,只需要寫一句
<% =Request.ServerVariables("HTTP_REFERER") %>
就可以了。
一些網(wǎng)站通過HTTP_REFERER來做安全驗證,判斷用戶是不是從允許的頁面鏈接來的,而不是直接從瀏覽器上打URL或從其他頁面鏈接過來,這樣可以從一定程度上防止網(wǎng)頁被做非法使用。但從上述原理來看,想要騙過服務(wù)器也并不困難,只要手工構(gòu)造輸入的HTTP消息頭就可以了,其他常用的手段還有通過HOSTS文件偽造域名等。
除了超鏈接以外,還有其他幾種方式會導(dǎo)致HTTP_REFERER信息被發(fā)送,如:
內(nèi)聯(lián)框架:<iframe src=b.asp></iframe>
框架集:<frameset><frame src=b.asp></frameset>
表單提交:<form action=b.asp><input type=submit></form>
SCRIPT引用:<script src=b.asp></script>
CSS引用:<link rel=stylesheet type=text/css href=b.asp>
XML數(shù)據(jù)島:<xml src=b.asp></xml>
而以下形式不會發(fā)送HTTP_REFERER:
script轉(zhuǎn)向:<script>location.href="b.asp"</script>
script開新窗口:<script>window.open("b.asp");</script>
META轉(zhuǎn)向:<meta http-equiv="refresh" content="0;URL=b.asp">
引入圖片:<img src=b.asp>
2 COOKIE
COOKIE是大家都非常熟悉的了,通過它可以在客戶端保存用戶狀態(tài),即使用戶關(guān)閉瀏覽器也能繼續(xù)保存。那么客戶端與服務(wù)器端是如何交換COOKIE信息的呢?沒錯,也是通過HTTP消息頭。
首先寫一個簡單的ASP網(wǎng)頁:
<%
Dim i
i = Request.Cookies("key")
Response.Write i
Response.Cookies("key") = "haha"
Response.Cookies("key").Expires = #2007-1-1#
%>
第一次訪問此網(wǎng)頁時,屏幕上一片白,第二次訪問時,則會顯示出“haha”。通過閱讀程序不難發(fā)現(xiàn),屏幕上顯示的內(nèi)容實際上是COOKIE的內(nèi)容,而第一次訪問時還沒有設(shè)置COOKIE的值,所以不會有顯示,第二次顯示的是第一次設(shè)置的值。那么對應(yīng)的HTTP消息頭應(yīng)該是什么樣的呢?
第一次請求時沒什么不同,略過
第一次返回時消息內(nèi)容多了下面這一行:
Set-Cookie: key=haha; expires=Sun, 31-Dec-2006 16:00:00 GMT; path=/
很明顯,key=haha表示鍵名為“key”的COOKIE的值為“haha”,后面是這則COOKIE的過期時間,因為我用的中文操作系統(tǒng)的時區(qū)是東八區(qū),2007年1月1日0點對應(yīng)的GMT時間就是2006年12月31日16點。
第二次再訪問此網(wǎng)頁時,發(fā)送的內(nèi)容多了如下一行:
Cookie: key=haha
它的內(nèi)容就是剛才設(shè)的COOKIE的內(nèi)容??梢?,客戶端在從服務(wù)器端得到COOKIE值以后就保存在硬盤上,再次訪問時就會把它發(fā)送到服務(wù)器。發(fā)送時并沒有發(fā)送過期時間,因為服務(wù)器對過期時間并不關(guān)心,當COOKIE過期后瀏覽器就不會再發(fā)送它了。
如果使用IE6.0瀏覽器并且禁用COOKIE功能,可以發(fā)現(xiàn)服務(wù)器端的set-cookie還是有的,但客戶端并不會接受它,也不會發(fā)送它。有些網(wǎng)站,特別是在線投票網(wǎng)站通過記錄COOKIE防止用戶重復(fù)投票,破解很簡單,只要用IE6瀏覽器并禁用COOKIE就可以了。也有的網(wǎng)站通過COOKIE值為某值來判斷用戶是否合法,這種判斷也非常容易通過手工構(gòu)造HTTP消息頭來欺騙,當然用HOSTS的方式也是可以欺騙的。
3 SESSION
HTTP協(xié)議本身是無狀態(tài)的,服務(wù)器和客戶端都不保證用戶訪問期間連接會一直保持,事實上保持連接是HTTP1.1才有的新內(nèi)容,當客戶端發(fā)送的消息頭中有“Connection: Keep-Alive”時表示客戶端瀏覽器支持保持連接的工作方式,但這個連接也會在一段時間沒有請求后自動斷開,以節(jié)省服務(wù)器資源。為了在服務(wù)器端維持用戶狀態(tài),SESSION就被發(fā)明出來了,現(xiàn)在各主流的動態(tài)網(wǎng)頁制做工具都支持SESSION,但支持的方式不完全相同,以下皆以ASP為例。
當用戶請求一個ASP網(wǎng)頁時,在返回的HTTP消息頭中會有一行:
Set-Cookie: ASPSESSIONIDCSQCRTBS=KOIPGIMBCOCBFMOBENDCAKDP; path=/
服務(wù)器通過COOKIE的方式告訴客戶端你的SESSIONID是多少,在這里是“KOIPGIMBCOCBFMOBENDCAKDP”,并且服務(wù)器上保留了和此SESSIONID相關(guān)的數(shù)據(jù),當同一用戶再次發(fā)送請求時,還會把這個COOKIE再發(fā)送回去,服務(wù)器端根據(jù)此ID找到此用戶的數(shù)據(jù),也就實現(xiàn)了服務(wù)器端用戶狀態(tài)的保存。所以我們用ASP編程時可以使用“session("name")=user”這樣的方式保存用戶信息。注意此COOKIE內(nèi)容里并沒有過期時間,這表示這是一個當關(guān)閉瀏覽器時立即過期的COOKIE,它不會被保存到硬盤上。這種工作方式比單純用COOKIE的方式要安全很多,因為在客戶端并沒有什么能讓我們修改和欺騙的值,唯一的信息就是SESSIONID,而這個ID在瀏覽器關(guān)閉時會立即失效,除非別人能在你瀏覽網(wǎng)站期間或關(guān)閉瀏覽器后很短時間內(nèi)知道此ID的值,才能做一些欺騙活動。因為服務(wù)器端判斷SESSION過期的方式并不是斷開連接或關(guān)閉瀏覽器,而是通過用戶手工結(jié)束SESSION或等待超時,當用戶關(guān)閉瀏覽器后的一段時間里SESSION還沒有超時,所以這時如果知道了剛才的SESSIONID,還是可以欺騙的。因此最安全的辦法還是在離開網(wǎng)站之前手工結(jié)束SESSION,很多網(wǎng)站都提供“Logout”功能,它會通過設(shè)置SESSION中的值為已退出狀態(tài)或讓SESSION立即過期從而起到安全的目的。
SESSION和COOKIE的方式各有優(yōu)缺點。SESSION的優(yōu)點是比較安全,不容易被欺騙,缺點是過期時間短,如果用過在超過過期時間里沒有向服務(wù)器發(fā)送任何信息,就會被認為超過過期了;COOKIE則相反,根據(jù)服務(wù)器端設(shè)置的超時時間,可以長時間保留信息,即使關(guān)機再開機也可能保留狀態(tài),而安全性自然大打折扣。很多網(wǎng)站都提供兩種驗證方式相結(jié)合,如果用戶臨時用這臺電腦訪問此訪問則需要輸入用戶名和密碼,不保存COOKIE;如果用戶使用的是自己的個人電腦,則可以讓網(wǎng)站在自己硬盤上保留COOKIE,以后訪問時就不需要重新輸入用戶名和密碼了。
4 POST
瀏覽器訪問服務(wù)器常用的方式有GET和POST兩種,GET方式只發(fā)送HTTP消息頭,沒有消息體,也就是除了要GET的基本信息之外不向服務(wù)器提供其他信息,網(wǎng)頁表單(FROM)的默認提交方式就是用GET方式,它會把所有向服務(wù)器提交的信息都作為URL后面的參數(shù),如a.asp?a=1&b=2這樣的方式。而當要提交的數(shù)據(jù)量很大,或者所提交內(nèi)容不希望別人直接看到時,應(yīng)該使用POST方式。POST方式提交的數(shù)據(jù)是作為HTTP消息體存在的,例如,寫一個網(wǎng)頁表單:
<form method=post>
<input type=text name=text1>
<input type=submit>
</form>
訪問此網(wǎng)頁,并在表單中填入一個“haha”,然后提交,可以看到此次提交所發(fā)送的信息如下:
POST /form.asp HTTP/1.1
Accept: */*
Referer: http://localhost:8080/form.aspAccept-Language: zh-cn
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; InfoPath.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
Host: localhost:8080
Content-Length: 10
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: key=haha; ASPSESSIONIDCSQCRTBS=LOIPGIMBLMNOGCOBOMPJBOKP
text1=haha
前面關(guān)鍵字從“GET”變?yōu)榱?#8220;POST”,Content-Type變成了“application/x-www-form-urlencoded”,后面內(nèi)容并無大變化,只是多了一行:Content-Length: 10,表示提交的內(nèi)容的長度??招泻竺媸窍Ⅲw,內(nèi)容就是表單中所填的內(nèi)容。注意此時發(fā)送的內(nèi)容只是“Name=Value”的形式,表單上其他的信息不會被發(fā)送,所以想直接從服務(wù)器端取得list box中所有的list item是辦不到的,除非在提交前用一段script把所有的item內(nèi)容都連在一起放到一個隱含表單域中。
如果是用表單上傳文件,情況就要復(fù)雜一些了,首先是表單聲明中要加上一句話:enctype='multipart/form-data',表示這個表單將提交多段數(shù)據(jù),并用HTML:input type=file來聲明一個文件提交域。
表單內(nèi)容如下:
<form method=post enctype='multipart/form-data'>
<input type=text name=text1>
<input type=file name=file1>
<input type=submit>
</form>
我們?yōu)閠ext1輸入文字:hehe,為file1選擇文件haha.txt,其內(nèi)容為“ABCDEFG”,然后提交此表單。提交的完全信息為:
POST /form.asp HTTP/1.1
Accept: */*
Referer: http://localhost:8080/form.aspAccept-Language: zh-cn
Content-Type: multipart/form-data; boundary=---------------------------7d62bf2f9066c
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; InfoPath.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)
Host: localhost:8080
Content-Length: 337
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: key=haha; ASPSESSIONIDCSQCRTBS=LOIPGIMBLMNOGCOBOMPJBOKP
-----------------------------7d62bf2f9066c
Content-Disposition: form-data; name="text1"
hehe
-----------------------------7d62bf2f9066c
Content-Disposition: form-data; name="file1"; filename="H:\Documents and Settings\Administrator\桌面\haha.txt"
Content-Type: text/plain
ABCDEFG
-----------------------------7d62bf2f9066c--
顯然這個提交的信息要比前述的復(fù)雜很多。Content-Type變成了“multipart/form-data”,后面還多了一個boundary,此值是為了區(qū)分POST的內(nèi)容的區(qū)段用的,只要在內(nèi)容中遇到了此值,就表示下面要開始一個新的區(qū)段了,每個區(qū)段的內(nèi)容相對獨立。如果遇到的是此值后面連著兩個減號,則表示全部內(nèi)容到此結(jié)束。每個段也分為段頭和段體兩部分,用空行隔開,每段都有自己的類型和相關(guān)信息。如第一區(qū)段是text1的值,它的名稱是“text1”,值為“hehe”。第二段是文件內(nèi)容,段首里表明了此文件域的名稱“file1”和此文件在用戶磁盤上的位置,后面就是文件的內(nèi)容。
如果我們想要自己寫一個上傳文件組件來接收HTML表單傳送的文件數(shù)據(jù),那么最核心的任務(wù)就是解析此數(shù)據(jù)包,從中取得需要的信息。
理解HTTP消息頭 (四)
服務(wù)器返回的消息
服務(wù)器返回的HTTP消息也分為消息頭和消息體兩部分。前面連載的第二篇里已經(jīng)介紹了返回消息中常見返回代碼的含義。對于非正常的返回代碼的處理比較簡單,只要照著要求去做就好了,而對于正常的返回代碼(200),其處理方式就多種多樣了。
1 Content-Type
Content-Type是返回消息中非常重要的內(nèi)容,它標識出這個返回內(nèi)容的類型,其值為“主類型/子類型”的格式,例如最常見的就是text/html,它的意思是說返回的內(nèi)容是文本類型,這個文本又是HTML格式的。原則上瀏覽器會根據(jù)Content-Type來決定如何顯示返回的消息體內(nèi)容。常見的內(nèi)容類型有:
text/html HTML文本
image/jpeg JPG圖片
image/gif GIF圖片
application/xml XML文檔
audio/x-mpegurl MP3文件列表,如果安裝了Winamp,則可以直接把它當面M3U文件來打開
更多的內(nèi)容類型可以在注冊表“HKCR\MIME\Database\Content Type”下看到
對于IE6瀏覽器來說,如果Content-Type中的類型和實際的消息體類型不一致,那么它會根據(jù)內(nèi)容中的類型來分析實際應(yīng)該是什么類型,對于JPG、GIF等常用圖片格式都可以正確的識別出來,而不管Content-Type中寫的是什么。
如果Content-Type中指定的是瀏覽器可以直接打開的類型,那么瀏覽器就會直接打開其內(nèi)容顯示出來,如果是被關(guān)聯(lián)到其它應(yīng)用程序的類型,這時就要查找注冊表中關(guān)于這種類型的注冊情況,如果是允許直接打開而不需要詢問的,就會直接調(diào)出這個關(guān)聯(lián)的應(yīng)用程序來打開這個文件,但如果是不允許直接打開的,就會詢問是否打開。對于沒有關(guān)聯(lián)到任何應(yīng)用程序的類型,IE瀏覽器不知道它該如何打開,此時IE6就會把它當成XML來嘗試打開。
2 Content-Disposition
如果用AddHeader的方法在HTTP消息頭中加入Content-Disposition段,并指定其值為“attachment”,那么無論這個文件是何類型,瀏覽器都會提示我們下載此文件,因為此時它認為后面的消息體是一個“附件”,不需要由瀏覽器來處理了。例如,在ASP.Net中寫入如下語句:
Response.AddHeader("Content-Disposition: attachment");
請求此頁面是得到的結(jié)果如:
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.1
Date: Thu, 23 Mar 2006 07:54:53 GMT
Content-Disposition: attachment
Cache-Control: private
Content-Type: text/html; charset=utf-8
……
也就是說,通過AddHeader函數(shù)可以為HTTP消息頭加入我們自定義的內(nèi)容。使用這種方法可以強制讓瀏覽器提示下載文件,即使這個文件是我們已知的類型,基于是HTML網(wǎng)頁。如果想要讓用戶下載時提示一個默認的文件名,只需要在前面一句話后加上“filename=文件名”即可。例如:
Response.AddHeader("Content-Disposition: attachment; filename=mypage.htm");
3 Content-Type與Content-Disposition
如果把Content-Type和Content-Disposition結(jié)合在一起使用會怎么樣呢?
打開一個網(wǎng)頁時,瀏覽器會首先看是否有Content-Disposition: attachment這一項,如果有,無論Content-Type的值是什么,都會提示文件下載。
如果指定了filename,就會提示默認的文件名為此文件名。注意到在IE6中除了“保存”按扭外還有“打開”按扭,此時打開文件的類型是由在filename中指定的文件擴展名決定的,例如讓filename=mypic.jpg,瀏覽器就會查找默認的圖片查看器來打開此文件。
如果沒有指定filename,那么瀏覽器就根據(jù)Content-Type中的類型來決定文件的類型,例如Content-Type類型為image/gif,那么就會去查找默認的看GIF圖片的工具,并且設(shè)置此文件的名字為所請求的網(wǎng)頁的主名(不帶擴展名)加上對應(yīng)于此文件類弄擴展名,例如請求的mypage.aspx,就會自動變成mypage.gif。如果并沒有指定Content-Type值,那么就默認它為“text/html”,并且保存的文件名就是所請求的網(wǎng)頁文件名。
但如果沒有指定Content-Disposition,那么就和前面關(guān)于Content-Type中所討論的情況是一樣的了。
4 Cache
返回消息中的Cache用于指定網(wǎng)頁緩存。我們經(jīng)常可以看到這樣的情況,打開一個網(wǎng)頁時速度不快,但再次打開時就會快很多,原因是瀏覽器已經(jīng)對此頁面進行了緩存,那么在同一瀏覽器窗口中再次打開此頁時不會重新從服務(wù)器端獲取。網(wǎng)頁的緩存是由HTTP消息頭中的“Cache-control”來控制的,常見的取值有private、no-cache、max-age、must-revalidate等,默認為private。其作用根據(jù)不同的重新瀏覽方式分為以下幾種情況:
(1) 打開新窗口
如果指定cache-control的值為private、no-cache、must-revalidate,那么打開新窗口訪問時都會重新訪問服務(wù)器。而如果指定了max-age值,那么在此值內(nèi)的時間里就不會重新訪問服務(wù)器,例如:
Cache-control: max-age=5
表示當訪問此網(wǎng)頁后的5秒內(nèi)再次訪問不會去服務(wù)器
(2) 在地址欄回車
如果值為private或must-revalidate(和網(wǎng)上說的不一樣),則只有第一次訪問時會訪問服務(wù)器,以后就不再訪問。如果值為no-cache,那么每次都會訪問。如果值為max-age,則在過期之前不會重復(fù)訪問。
(3) 按后退按扭
如果值為private、must-revalidate、max-age,則不會重訪問,而如果為no-cache,則每次都重復(fù)訪問
(4) 按刷新按扭
無論為何值,都會重復(fù)訪問
當指定Cache-control值為“no-cache”時,訪問此頁面不會在Internet臨時文章夾留下頁面?zhèn)浞荨?br>另外,通過指定“Expires”值也會影響到緩存。例如,指定Expires值為一個早已過去的時間,那么訪問此網(wǎng)時若重復(fù)在地址欄按回車,那么每次都會重復(fù)訪問:
Expires: Fri, 31 Dec 1999 16:00:00 GMT
在ASP中,可以通過Response對象的Expires、ExpiresAbsolute屬性控制Expires值;通過Response對象的CacheControl屬性控制Cache-control的值,例如:
Response.ExpiresAbsolute = #2000-1-1# ' 指定絕對的過期時間,這個時間用的是服務(wù)器當?shù)貢r間,會被自動轉(zhuǎn)換為GMT時間
Response.Expires = 20 ' 指定相對的過期時間,以分鐘為單位,表示從當前時間起過多少分鐘過期。
Response.CacheControl = "no-cache"
Expires值是可以通過在Internet臨時文件夾中查看臨時文件的屬性看到的,如: