訪問和更新CookIEs集合
CookIEs的值比ASP其他集合(例如Form和ServerVariables)的值要復雜得多。CookIE是一小塊由浏覽器存貯在客戶端系統上的文本,且隨同每次請求發往它們應用於的域中的服務器。
ASP使得應用cookie較為容易,可以從Request對象的Cookies集合中獲得所有隨同請求發出的cookie值,並可創建或修改cookie,通過Response對象的CookIEs集合發回給用戶。
Cookie包含可用兩種方式構造的信息,單值cookIE提供其值給代碼是通過一個一般的類ASP集合。然而,集合的每個成員可能本身也是一個集合,包含這種信息的cookIE通過稱為多值(multiple-Value)cookIE。
創建一個單值的cookIE較為簡單,如下所示:
Response.CookIEs(“item-name”) = “item-value”
創建一個多值的cookIE,可以使用如下命令:
Response.CookIEs(“item-name”)(“sub-item-name”) = “sub-item-value”
設置cookIE應用的域及路徑及其有效期,我們使用:
Response.CookIEs(“item-name”).domain = “domain-url”
Response.CookIEs(“item-name”).path = “virtual-path”
Response.CookIEs(“item-name”).expires = #date#
通常,客戶只在對創建cookie的目錄中的頁面提出請求時,才將cookIE隨請示發住服務器。通過指定path屬性,可以指定站點中何處這個cookie是合法的,並且這個cookie將隨請求發送。如果cookIE隨對整個站點的頁面請求發送,設置
path為“/”。
假如Expires屬性沒有設置,關閉當前的浏覽器實例時,cookIE將被自動消除。
注意,我們在向浏覽器發送任何輸出時,已經創建了cookie。因為,這些cookIE是頁面HTTP報頭的一部分。
在ASP 3.0中,緩沖的缺省狀態是打開的,且沒有輸出被發送,除非使用Response.Flush指定做這個工作或者頁面已到末端。這意味著創建cookIE的代碼可以在頁面上的任何位置,直到任何輸出“刷新”(flush)到客戶端前,它都可以被執
行。
要讀現有的cookie,使用Request.CookIEs集合。可以單獨訪問其中的項目,方法類似於創建它們時使用的方法。
StrSingleValue = Request.CookIEs(“item-name”)
StrSubItemValue = Request.CookIEs(“item-name”)(“sub-item-name”)
注意Request.Cookies集合(和所有其他Request集合一樣)是只讀的。Response.Cookies集合是只寫的,事實上可以訪問這個集合中一系列cookIE的名稱,而不是它們的值。
遍歷CookIEs集合
為了使用Cookies集合更加方便,可使用名稱為Haskeys的附加屬性。假如訪問的cookie本身也是個集合,即它是一個多值的cookie,這將返回True。使用Haskeys屬性,可以遍歷完整的Request.Cookies集合,從而獲得所有cookIE的列表及它們的值。
For Each objItem In Request.CookIEs
If Request.CookIEs(objItem).HasKey Then
‘Use another For Each to iterate all subkeys
For Each objItemKey in Request.CookIEs(objItem)
Response.Write objItem & “(“ & objItemKey & “) = “_
& Request.CookIEs(objItem)(objItemKey) & “<BR>”
Next
Else
‘PRint out the cookIE string as normal
Response.Write objItem & “ = ”& Request.CookIEs(objItem) & “<BR>”
End If
Next
這非常類似於前面的從Request.Form集合中提取多個值的復雜代碼。但是這裡可以使用Haskeys屬性來判別每個條目是否為一個集合。而在Form例子裡,必須查詢Request.Form(item_name).Count屬性,這是因為Form集合(和所有的除cookIE外的其他集合)成員不可能是真正的集合。ASP只是做了“幕後”的工作,得到了每個多條目集合的值。
Form和QueryString的差異
了解了訪問各種ASP集合的技術以後,需要解決另一個問題是:Form和QueryString集合之間的差異是什麼?假如准備使用ASP,毫無疑問應該清楚這種差異,但需要參考HTTP工作方式來重新認識,理解它們。
通過HTTP從Web服務器請求頁面或其他資源,有兩個通用的方法。可使用GET方法直接獲得資源,也可使用POST把值傳給相應資源。GET方法是缺省的,可以看一下本章前面的一個HTTP請求的實例:
7/8/99 10:27:16 Sent GET /Store/Download.ASP HTTP/1.1
假如把一個或多個成對的名稱/值附在請求頁面的URL後,就變成請求的查詢字符串,且在QueryString集合中提供給ASP頁面。單擊Web頁面、Email消息或其它文檔的超鏈接,或在浏覽器的地址欄中輸入地址並按回車,或單擊浏覽器中的
Links或Favorites按鈕,所有這些都要使用GET方法。
因此,對這些動作中傳遞值給ASP的唯一方法是通過QueryString集合,把值附在URL後。
出現在Request.QueryString集合中並被訪問的值,與前面看到的Form集合實例中的工作方式相同。URL和查詢字符串的結合:
http://mysite.com/process_page.ASP?FirstName=Priscilla&LastName=Descartes
可以采用如下方式訪問在QueryString集合中提供的值:
strFirstName = Request.QueryString(“FirstName”) ‘Return “Priscilla”
strLastName = Request.QueryString(“LastName”) ‘Return “Descartes”
strRaw = Request.QueryString
‘Return “FirstName=Priscilla&LastName=Descartes”
窗體的GET和POST方法
在一個頁面內使用<FORM>段時,可以設置打開的FORM標記的METHOD屬性值為“GET”或“POST”,缺省值為“GET”。假如使用“GET”或省略其屬性,浏覽器將該值綁定在頁面所有控件上,成為一個查詢字符串,且附在被請求頁面的URL上。
當這個請求到達Web服務器時,其值由ASP的Request.QueryString集合提供。然而,假如設置METHOD屬性為“POST”,浏覽器將值包裝進發送服務器的HTTP報頭中,通過Request.Form集合提供給ASP。
通過來說,可以在所有的Html窗體中使用POST方法。然而,浏覽器或服務器的URL字符串長度存在一定的限制。因此,附有長的字符串可能會引起溢出和某些字符串的字符被截掉。同時,查詢字符串出現在浏覽器的地址欄和所有的保存的鏈接和收藏夾中。不僅如此,還顯露了通過Web服務器時在HTTP請求中不想顯示的值,它也可能出現你的服務器和其他路由服務器的日志文件中。在HTTP請求報頭中的值很少是可見的,並且不出現在日志文件中。
使用POST方法需要注意的小問題是,當用戶重新下載<FORM>時,窗體的值將不再保留,其值為空且必須重新輸入。然而,當附在URL上時,其值被存儲為一個鏈接,將被保留,因此將出現在所有的URL與字符串結合的請求中,這或許是個優點也可能是個缺點,這根據應用而定(一些浏覽器在客戶端上能夠在一定范圍內自動保留一個頁面上的值)。
另一點是URL與查詢字符串的結合體不能包含任何空格或其他非法字符,否則的話,Navigator和一些其他的浏覽器將出現問題。非法字符是那些用來分隔URL和查詢字符串的部分,例如“/”、“:”、“?”和“&”(IE能夠自動將空格轉換為正確的格式——加號“+”,但其他的非法字符不能處理)。ASP服務器對象提供URLEncode方法處理這種變換,以後章節討論相關內容。
查看Request和Response對象內容
到目前,主要討論了一些理論問題,沒有列舉特別的實例。因為已經討論過的內容多數情況下互相之間是密切相關的。然而本書為這一章提供了一系列的實例頁面,說明Request和Response對象的大多數屬性。應用所講過的實例,能夠理解這些頁面,並可進行相應的修改,用它們作為試驗實例。
本章及其他所有章節的代碼樣例均提供給用戶,可以從Wrox出版社站點下載。
http://webdev.wrox.co.uk/books/2610
http://www.wrox.com/Store/Details.ASP?Code=2610
必須首先在Web服務器的WWWRoot內的子目錄下安裝實例,然後使用浏覽器訪問Chapter02子目錄,使用:
http://your_server_name_or_IP/subdirectory_name/Chapter02/Default.ASP
這裡your_server_name_or_IP/subdirectory_name是安裝下載文件的本地路徑。
1、查看Request對象成員
這提供一個包含選頁的菜單用來試驗Request和Response對象,首先選擇Using the Request Object,如下圖所示:
下圖顯示一個Html窗體的實例,包含一些預先設置好的值,可以按自己的想法編輯這些值,然後點擊“Submit”按鈕。
這將打開一個頁面,如下圖所示,顯示集合和TotalBytes屬性的全部內容。第一屏顯示的是Form、QueryString和CookIEs
集合。
注意,假如在窗體頁中編輯了Html控件的值,對於CookIEs集合以及其他的集合,在讀者的計算機上的頁面上可能顯示不同的值。
可以從“Form Collection”段中看到窗體上的HTML控件的值如何在ASP的Request.Form集合中表示。也可以用原始的<FORM>頁(名稱為request_form.ASP)來試驗和檢測,以了解創建窗體的Html和如何與相應值相聯系。
這個頁面後面是ClIEntCertificate集合。這裡是空的,因為服務器不要求客戶端提供證書。下面是的ServerVariables集合,下圖的屏幕圖顯示的是集合中包含的有用的值。
在本書的後面附錄中可以找到所有ServerVariables集合成員的一個列表,及其值的說明。然而,可從前面討論的在請求頁面時從客戶端發出的HTTP報頭中見到這些成員。當請求收到後,Web服務器也增加它本身的一些值到集合中,正如上面可以看到的運行在IIS 5.0創建的頁面那樣。
1) 頁面是如何工作的
為了創建這個頁面,使用了本章前面在對Form集合和如何訪問其值的討論中所看到的完全相同的代碼。例如,遍歷所有的集合(除Request.CookIEs外),使用:
For Each objItem In Request.collection_name
Response.Write objItem & “ = “ & Request.collection_name(objItem) & “<BR>”
Next
遍歷CookIEs集合,可以使用:
For Each objItem In Request.CookIEs
If Request.CookIEs(objItem).HasKeys Then
‘Use another For ... Each to iterate all keys of dictionary
For Each objItemKey in Request.CookIEs(objItem)
Resonse.Write objItem objItem & “(“ & objItemKey & “) = “_
& Request.CookIEs(objItem)(objItemKey) & “<BR>”
Next
Else
‘Print out the cookIE string as normal
Response.Write objItem & “ = “ & Request.CookIEs(objItem) & “<BR>”
End If
Next
為獲得TotalBytes屬性,可簡單地使用:
Request.TotalBytes = <% = Request.TotalBytes %><P>
讀者應該注意到,在兩個集合中出現的某些值不是從窗體的Html控件中直接得到的。QueryString集合包括了兩個名為chapter和sample的值,如下圖所示:
為在請求中創建這兩個值,將一個字符串附在窗體的ACTION屬性的URL上,這是可以接受的。工作方式與附在一個<A>元素的HREF屬性上相類似。查詢字符的值出現在QueryString集合中,且被POST的窗體控件值出現在Form集合中。
<FORM ACTION=”show_request.ASP?chapter=2&sample=The+Request+Object” METHOD=”POST”>
為防止非IE類的浏覽器出現錯誤,必須將查詢字符串中的空格用加號“+”來代替,讀者將在第4章的Server對象的URLEncode方法中看到更多這種情況。
2) 創建客戶端的cookIE
為了確保至少有些值出現在Request.CookIEs集合中,增加一些客戶端腳本代碼到原始的<FORM>頁面request_form.ASP。將創建名稱為VisitCount的多值cookie。另一個cookIE是由另一個頁面創建的,且已經存在於浏覽器中。如下圖所示,讀者可以看到另外的cookIE。
這是一段窗體被裝載時設置文檔對象的cookIEs屬性的客戶端代碼:
<SCRIPT LANGUAGE=”JavaScript”>
<!--
documet.cookIE = ’VisitCount=VISITS=3&LASTDATE=6%2F4%2F99+10%3A10%3A13+AM’;
//-->
</SCRIPT>
另外,必須將內容進行編碼,以便它能被正確地傳送到服務器(同樣的規則也適用於將查詢字符串附到URL上)。在第4章中,討論Server對象的URLEncode方法時,讀者將了解到更多細節。
2、查看Response對象的成員
回到Chapter02實例的最初的Default.ASP頁面,這次選擇“Using the Response Object”鏈接,這個頁面顯示的是Response對象的集合和屬性的內容,且提供到所有Response對象方法的鏈接。
下圖是使用浏覽器Netsape Communicator 4.61的屏幕,用來證明使用的是純服務器端和跨平台兼容技術。需要注意的是Cookies集合是為Response對象而建立的,僅顯示cookie的名稱而不顯示其值。浏覽該頁時,可能得不到cookie或得到與這個頁面不同的cookie。(由於豆豆沒有安裝Netsape Communicator 4.61,仍用IE5.5顯示,並無什麼區別)
各種Response屬性說明了將要用來創建HTTP報頭的一些信息。HTTP報頭頁面的其他部分(Html和文本內容)被發往到客戶端。這些屬性中的一些以及所有的Response對象的方法均有鏈接,允許讀者打開另一個頁面來顯示其使用情況。我們稍後再回到這些頁面。
頁面中的屬性是通過讀取相應的屬性並插入到頁面中創建的。由於這些是動態鏈接,通過<A>元素來選擇。
<A HREF = ”headers/expiretet_form.ASP”>Response.CacheControl</A>
= <% = Resposne.CacheControl %><BR>
鏈接到每個方法是通過<A>鏈接元素,頁面中唯一復雜的部分是Response.Cookies集合。通過只能訪問cookie,讀取Request.Cookies集合中的值。當訪問Response.CookIE集合時,必須在發送任何輸出到客戶端之前結束對它的所有引用。因此在頁面的上部,通過遍歷集合創建頁面的Html放在一個局部字符串變量中。
StrCookIEs = “”
‘We can only read the key names and not the values because
‘the Response.CookIEs collection is ‘write only’
For Each objItem In Response.CookIEs
If Response.CookIEs(objItem).HasKeys Then
‘Use another For Each to iterate all subkeys
For Each objItemKey in Response.CookIEs(objItem)
StrCookies = strCookIEs & objItem & “(“ &objItemKey & “)<BR>”
Next
Else
‘print out the cookIE as normal
strCookies = strCookIEs & objItem & “<BR>”
End If
Next
然後在頁面的適當點上插入結果。
<P><DIV CLASS=”subhead”>The Response.CookIEs Collection</DIV>
<I><A HREF=”cookies/setcookIEs.ASP”>Response.CookIEs</A>
is a write-only collection so the values cannot be displayed</I><BR>
<% = strCookIEs %>
ASP中的cookIE的使用
在前面所看到頁面中,一些集合、屬性和方法已經鏈接到其他的頁面,用來顯示Request和Response對象的各個特性細節,我們將在本章的其余的部分研究這些內容,我們將學習那些提供給ASP代碼使用的集合、方法和屬性的各種技術。
在本章前面已經看到了如何使用Request.Cookies和Response.Cookies集合來創建和閱讀cookie,點擊上面兩個頁面中的任一個的“Cookies”鏈接時,這個頁面包含一些設置了三個cookIE的值的ASP代碼,且在頁面上顯示被執行的代碼,如下圖所示:
點擊“Show Cookies”的鏈接時,cookie的內容就顯示出來了。這是通過遍歷Request.CookIEs集合而得到的,這與在上一頁所用的方式完全相同,如下圖所示:
這個屏幕圖顯示的是運行前面看到的設置cookie值的代碼的結果。可能會看到其他已經存貯在計算機系統裡的cookie。然而,假如現在關閉浏覽器然後重新打開浏覽器,然後運行顯示cookie的頁面,除了TimedCookie外,所有的cookie都不見了,這是由於只有這個TimedCookIE具有有效期的設置,其他的在浏覽器關閉時,自動消失了。
1) cookIE中存儲用戶的細節情況
可以使用cookIE來存儲這兩類值:當浏覽器關閉時我們不想保存的值(例如用戶的注冊信息)以及在用戶訪問站點時要保留的值。在每種情況下cookIE的值對於來自用戶浏覽器的每個頁面請求的ASP都是可用的。
然而,需要記住的是,cookie只有在對CookIE中的虛擬路徑(path)內的頁面發出請求時,才會發往服務器。缺省時,假如path的值在cookie中沒有設置,則其值為創建cookie的頁面的虛擬路徑。為使一個cookIE發往一個站點的所有頁面,需要使用path=“/”。
這裡是個實例,從自定義的Login頁面中,將用戶的注冊信息存貯在一個cookie中,由於沒有應用有效期,cookIE值僅在關閉這個浏覽器這前保留:
...
Request.CookIEs(“User”)(“UID”) = “<% = Request(“UserName”) %>”
Request.CookIEs(“User”)(“PWD”) = “<% = Request(“PassWord”) %>”
Request.Cookies(“User”).Path = “/adminstuff” ‘Only applIEs to admin pages
...
現在,在用戶從adminstuff目錄或其子目錄請求的每個頁面中,都可以找到這個cookIE。假如它不存在,可以將用戶重定向到注冊頁面:
If (Request.CookIEs(“User”)(“UID”) <> “alexhomer”) _
Or (Request.CookIEs(“User”)(“PWD”) <> “secret”) Then
Response.Redirect “login.ASP?UserName=” & Request.CookIEs(“User”)(“UID”)
End If
...
由於把cookIE中的用戶名放在Response.Redirect的URL查詢字符串中,假如在口令輸入時出現錯誤且希望用戶不必重新鍵入用戶名,可以在login.ASP頁面中使用它:
<FORM ACTION=”check_user.ASP” METHOD=”POST”>
<INPUT TYPE=”TEXT” NAME=”UserName”
VALUE=”<% = Request.QueryString(“UserName”) %>”><P>
<INPUT TYPE=”SUBMIT” VALUE=”LOGIN”>
</FORM>
2) 修改現有的cookIE
可以使用ASP修改現有的cookie,但不能只修改cookie中的一個值。當更新一個在Response.Cookies集合中的Cookie時,現有的值將丟失。我們可以用如下代碼創建一個cookIE,可以使用:
Response.CookIEs(“VisitCount”)(“StartDate”) = dtmStart
Response.CookIEs(“VisitCount”)(“LastDate”) = Now
Response.CookIEs(“VisitCount”)(“Visits”) = CStr(intVisits)
Response.CookIEs(“VisitCount”).Path = “/” ‘Apply to entire site
Response.CookIEs(“VisitCount”).Expires = DateAdd(“m”,3,Now)
假如想要更新Visits和LastDate的值,必須先不需改變的所有值,然後重寫整個的cookIE:
datDtart = Response.CookIEs(“VisitCount”)(“StartDate”)
intVisits = Response.CookIEs(“VisitCount”)(“Visits”)
Response.CookIEs(“VisitCount”)(“StartDate”) = dtmStart
Response.CookIEs(“VisitCount”)(“LastDate”) = Now
Response.CookIEs(“VisitCount”)(“Visits”) = Cstr(intVisits)
Response.CookIEs(“VisitCount”).Path = “/”
Response.CookIEs(“VisitCount”).Expires = DateADD(“m”,3,Now + 1)且對於幾乎所有的其他Response方法和屬性,應該在寫入任何內容(即打開<HTML>標記或任何文本或其他的Html)到響應之前完成這個工作。