每個開發(fā)人員面對的困難是預(yù)測用戶能夠或是將要做什么--這對于網(wǎng)絡(luò)開發(fā)人員來說就更為困 難,因為他的預(yù)測必須考慮到Web 的多樣性和缺乏真正的session控制機(jī)制。如果你已經(jīng)創(chuàng)建過一個使用表單的ASP應(yīng)用程序,也許你已經(jīng)遇到過一些奇怪的問題,如數(shù)據(jù)傳輸兩次,接收數(shù)據(jù)不完整,或者用戶報告表單顯示不正確。盡管你也插入了確認(rèn)數(shù)據(jù)所需的所有客戶機(jī)端和服務(wù)器端的腳本,表單仍然會發(fā)生許多異常情況。這些異常情況與意外用戶行為或瀏覽器書簽的誤使用有關(guān)。本文將集中解決一些容易引起表單問題的典型情況:用戶意外地重復(fù)發(fā)送數(shù)據(jù),在多步驟表單中直接使用中間表單。
數(shù)據(jù)復(fù)制
通過表單重復(fù)發(fā)送數(shù)據(jù)是一個常見的情況,但是它會帶來問題。在理想的情況下,用戶在一個 Web 站點遇到一個表單,用正確的數(shù)據(jù)類型填充它,將它提交給處理數(shù)據(jù)的服務(wù)器,然后作為回應(yīng)發(fā)送給用戶一個確認(rèn)頁,這時用戶就可以再去做別的。如果用戶重新訪問前面那一頁,使用back 按鈕,然后無意中再將數(shù)據(jù)發(fā)送一次,那將會出現(xiàn)什么情形呢?如果你沒有預(yù)料到這一場景并且有所準(zhǔn)備,數(shù)據(jù)就將被重新傳送給服務(wù)器并且再處理一次。試想這些數(shù)據(jù)是一份訂單或旅館預(yù)約,那將會帶來很不愉快的結(jié)果。
終止重復(fù)數(shù)據(jù)傳輸
為了避免那些錯誤地重復(fù)發(fā)送給服務(wù)器的數(shù)據(jù),可以在服務(wù)器側(cè)進(jìn)行一些校驗,來確定用戶能 意識到他們正在發(fā)送數(shù)據(jù)。這里使用的例子包含一個有單一文本框的簡單表單,表單接收一些文本,然后將其發(fā)送到一個顯示它們的ASP頁。 為確保用戶不將同樣的信息發(fā)送兩次,需要指示數(shù)據(jù)已經(jīng)被服務(wù)器接收到。存儲這些信息的最好的地方是一個session變量。定義一個session變量Session("submitted") ,當(dāng)用戶第一次到達(dá)這個表單時將它初始化為False,在用戶進(jìn)行最初的數(shù)據(jù)傳輸時將它設(shè)置為true 。如果用戶在當(dāng)前的session期間重新訪問這個表單,將出現(xiàn)相關(guān)重復(fù)提交信息。 所以用戶只能是在有意的情況下向服務(wù)器重復(fù)發(fā)送數(shù)據(jù)。現(xiàn)在來看看執(zhí)行這一校驗的代碼。建立表單并且校驗已發(fā)送數(shù)據(jù)的ASP頁(在下載處為form.asp)有以下結(jié)構(gòu): 〈 HTML 〉 〈 HEAD 〉 〈 /HEAD 〉 〈 BODY 〉 〈 % If Session("submitted") Then % 〉 〈 !-- Code showing the warning message -- 〉 ... 〈 % Else % 〉 〈 !-- Code showing the form -- 〉 ... 〈 % End If % 〉 〈 /BODY 〉 〈 /HTML 〉
表單和警告信息都是從同一個ASP頁創(chuàng)建的。表單包括標(biāo)準(zhǔn)的HTML代碼,引用ManageForm.asp頁作為它的ACTION 屬性:
〈 FORM METHOD="post" ACTION="ManageForm.asp" 〉 Send me some data: 〈 INPUT TYPE="text" NAME="data" 〉 〈 P 〉 〈 INPUT TYPE="submit" VALUE="Submit" 〉 〈 INPUT TYPE="reset" VALUE="Cancel" 〉 〈 /FORM 〉
ManageForm.asp 頁接收用戶發(fā)送的文本,顯示它并將session 變量submitted設(shè)置為True: 〈 HTML 〉 〈 HEAD 〉 〈 /HEAD 〉 〈 BODY 〉 You have sent the following information: 〈 P 〉 〈 %= Request("data") % 〉 〈 % Session("submitted") = True % 〉 〈 /BODY 〉 〈 /HTML 〉
所以當(dāng)用戶又回到這個表單時,測試session 變量submitted,當(dāng)它的值為True時,發(fā)送給用 戶的是警告信息而不是輸入表單。這個警告信息是用HTML和客戶機(jī)側(cè)的JavaScript代碼組合編寫的: 〈 SCRIPT 〉 function SendAnswer(answer) { document.AnswerForm.answer.value = answer document.AnswerForm.submit() } 〈 /SCRIPT 〉 You have already submitted some information to this Web site. 〈 BR 〉 Do you want submit again? 〈 P 〉 〈 FORM NAME="AnswerForm" METHOD="post" ACTION="CheckAnswer.asp" 〉 〈 INPUT TYPE="button" VALUE="Yes" onClick="SendAnswer('Y')" 〉 〈 INPUT TYPE="button" VALUE="No" onClick="SendAnswer('N')" 〉 〈 INPUT TYPE="hidden" NAME="answer" VALUE="" 〉 〈 /FORM 〉
表單包含兩個按鈕((Yes 和 No) 以及一個隱含控制域(answer) ,在其中保存用戶所選擇的值: Y 或 N。這個值由JavaScript 函數(shù)SendAnswer() 設(shè)置,這個函數(shù)還將它發(fā)送給CheckAnswer.asp 頁以執(zhí)行正確的重定向。如果用戶選擇了No按鈕,CheckAnswer.asp 檢驗隱含控制的值,并將其重定向到一個普通 welcome 頁,反之就將session 變量submitted設(shè)置為False 并再次將其重定向到表單頁。 〈 % If Request("answer") = "Y" Then Session("submitted") = False Response.Redirect "form.asp" Else Response.Redirect "welcome.htm" End If % 〉
控制瀏覽器緩沖器
如果你已經(jīng)實施了以上方法,你會發(fā)現(xiàn),只有當(dāng)你在瀏覽器的地址文本框內(nèi)鍵入URL來回到這個 表單時,此方法才奏效。它依靠的是瀏覽器的緩沖器機(jī)制。如果你使用back按鈕來返回頁,瀏覽器就檢測它的緩沖器來找到該頁的副本。它將使用緩存的頁而不是向服務(wù)器發(fā)出請求。所以服務(wù)器就 不能在session 變量submitted上進(jìn)行校驗。為了避免這種情況,就要抑制瀏覽器的頁緩沖器。這通過在表單頁中處理Response對象來實現(xiàn)。取消頁緩沖器有多種方法。所有這些方法都要依靠HTTP頭文件中到瀏覽器的地址指示。但是所有瀏覽器對服務(wù)器發(fā)送的指示反應(yīng)不同,所以說最好能多發(fā)送一些指示來為更多的瀏覽器抑制緩沖器,按以下代碼所示:
〈 % Response.AddHeader "cache-control", "private" Response.AddHeader "pragma", "no-cache" Response.ExpiresAbsolute = #January 1, 1990 00:00:01# Response.Expires=0 % 〉
以上代碼的頭兩行使用Response 對象的AddHeader 方法來將頭信息附加到HTTP頭文件中。 Expires 和 ExpiresAbsolute 屬性用瀏覽器緩沖器中頁的持續(xù)時間信息來標(biāo)記當(dāng)前頁。在表單頁中,這些行必須要插入在所有代碼之前,因為她們所引用的信息放置在HTTP頭文件中,在所有輸出之前發(fā)送給瀏覽器。
多步驟表單
如果一個表單需要許多數(shù)據(jù),那么最好將你要求的數(shù)據(jù)劃分成多個小表單,這樣使用戶可以一步一步地填充表單,而不用等待表單加載許多HTML控制。另外還有一些情況,表單中的某些控制不完全必要,并且可以用已經(jīng)提交的數(shù)據(jù)逐行填充。使用多步驟表單允許顯示倚賴于用戶以前答案的定制表單。如果用戶在瀏覽器中將一個中間表單設(shè)置為書簽的話就會產(chǎn)生問題。在隨后的一個session中,用戶就試圖直接到達(dá)這個表單并提交數(shù)據(jù),這些數(shù)據(jù)已經(jīng)在上下文范圍之外,因為本來應(yīng)該在前面 表單收集的session 數(shù)據(jù)丟失了。
避免使用中間步驟表單
為了避免這些問題,可以存儲當(dāng)前數(shù)據(jù)收集的狀態(tài)。這個狀態(tài)可以用一個session 變量來代表 來記錄是否執(zhí)行了一個特定的步驟---用戶是否填充了給出的表單。在一個多步驟表單中,每個表單都可以通過一個Boolean型的session變量來實現(xiàn)。如果有關(guān)表單沒有被處理,變量就為False ,反之就是True。下載部分的第二個例子顯示一個兩步驟表單:第一個表單要求用戶名,第二個表單顯示一個組合框,它的列表項要依賴第一個表單所提供的用戶名。第一個表單與一個session變量requested1相關(guān)聯(lián),你可以想象出來,第二個表單與變量requested2相關(guān)聯(lián)。當(dāng)用戶要求第一個表單(form1.asp) 時,session變量 requested1 被設(shè)置為 True :
〈 FORM METHOD="post" ACTION="form2.asp" 〉 Your name: 〈 INPUT TYPE="text" NAME="name" 〉 〈 P 〉 〈 INPUT TYPE="submit" VALUE="Submit" 〉 〈 INPUT TYPE="reset" VALUE="Cancel" 〉 〈 /FORM 〉 〈 % Session("requested1") = True % 〉
這個值將由下一個表單( form2.asp ) 來校驗,以確定是否滿足了要求。事實上當(dāng)用戶要求第二個表單時校驗requested1 變量。如果為True,就向瀏覽器發(fā)送第二個表單并將requested2變量設(shè)置為True。如果為False 就意味著用戶想要直接使用第二個表單,于是瀏覽器就重定向到第一個表單。以下代碼是第二個表單的ASP頁:
〈 % If Session("requested1") Then % 〉 〈 HTML 〉 〈 HEAD 〉 〈 /HEAD 〉 〈 BODY 〉 〈 !-- Code for the second form -- 〉 ... 〈 % Session("requested2") = True Else Response.Redirect "form1.asp" End If % 〉 〈 /BODY 〉 〈 /HTML 〉
要注意對requested1 的校驗必須要在〈 HTML 〉記錄之前進(jìn)行,這樣就允許可能的重定向。實際上,重定向是對瀏覽器的指示,它出現(xiàn)在HTTP頭文件中,在所有的HTML代碼之前。
結(jié)論
本文所示范的兩種技巧允許ASP開發(fā)人員對某些奇怪的情況有所控制,這些奇怪情況會造成用戶 通過一個Web 表單向服務(wù)器重復(fù)發(fā)送數(shù)據(jù)。每個技巧解決一個特定問題,所以最好將兩者混合使用,在ASP應(yīng)用程序每個表單中管理兩個session 變量。
|