在ASP中執行多個頁面的表單提交

穩萊

在ASP中執行多個頁面的表單提交


假設現在你要在站點上建立一個多頁面的訂貨表單,從而執行從站點通過email發給訂單書寫人,並假定這些單獨的頁面、頁面的個數以及頁面上的輸入域都可以根據實際要訂購的產品而改變。

如果我們將每個頁面都分別提交給FrontPage的 email 向導或CGI 的email 程式,那麼可憐的訂單書寫人就要為訂單的每一頁都收到一個單獨的郵件資訊。當然我們可以通過session變量、隱藏域或查詢字串將訂貨數據從一頁傳遞到另一頁,但是在內存中儲存這些數據會造成應用程序不平衡,另外潛在地需要大量的腳本。而且當頁面上的域變化時還必須要修改腳本程式,所以程式不太能夠再利用。

如果能夠寫出一套腳本可以用在所有這些訂單頁面中,只把HTML頁面的設計留給WEB設計者,那就太好了。


一個簡單的答案是,將每個頁面中的數據保存在一個文件中,然后將所有這些文件連接起,再將之用Email方式發送給訂單書寫人。這種方法另外還有一個好處是如果愿意可以在硬盤上保持臨時文件,那么在晚些時候用戶可以繼續一個未完成的訂單。最好的一點是,執行這一辦法不需要第三方組件,而只需要session變量,ASP腳本和Scripting.FileSystemObject 對象。

首先要為這些單獨的頁面數據文件確定唯一的名字,這樣在訂單結束時可以將它們放在一起,并且不會被應用程序中的其它文件所覆蓋。同時,需要從單個session變量表示的文件進行聚合中,這樣就不會偶然將一個舊session中的數據包含到當前的訂單中。

由IIS所提供的Session變量("SessionID")在一個Session過程中在網絡服務器上是唯一識別的,但是到了后面,Session就可以被復制。(事實上,SessionID在以后會話Session時總是要被復制的,SessionID存儲在客戶機的一個cookie 中。當網絡服務器從客戶機中接收到一個過去會話的SessionID時,若它沒有被另一個活動會話所使用,就使用相同的值,這樣就不用向瀏覽器發送另一個cookie了,有關的信息可以參見微軟MSDN上的Managing Sessions。

為了唯一識別session,我們要創建一個自己的session識別器Session("SessionToken"), 使用時間考驗的方法:
向SessionID附加日期和時間。

Sub SetSessionToken
dim dtTemp
dim sDate
dim sTime

dtTemp = date
sDate = Year(dtTemp) & Month(dtTemp) & Day(dtTemp)
dtTemp = Time
sTime = Hour(dtTemp) & Minute(dtTemp) & Second(dtTemp)
Session("SessionToken") = Session.SessionID & "-" & sDate & "-" & sTime
End Function

通過將SetSessionToken 封裝成函數,可以輕易地修改算法來生成一個session 標志,而不用影響程式的其它部分。在第一頁訂單的頂端將調用SetSessionToken。

每一頁都要有一個唯一的頁碼,要將它附加到session 標志變量上去產生文件名。給頁面使用任何一種標志都可以,但是在這里的應用程序中,頁面必須要按照輸入的順序被輸出,所以就使用一個捕捉這一順序的頁碼。跟前面一樣,將這個文件名算法封裝到一個函數中,以后修改起來就很容易了。

Function BuildFilename(iPage)
' -- if the session has expired, return a null filename.
If Session("SessionToken")="" Then
BuildFilename = ""
Else
BuildFilename = Session("SessionToken") & iPage & ".txt"
End If
End Function

在訂單第一頁后面每一頁的頂端,都調用WritePage函數將從前一頁提交的數據寫入文件。最后一頁之后也調用這個函數。

Function WritePage(iPrevPage)
dim sFilename
dim objFSO
dim objTS
dim objField

sFilename = buildFileName(iPrevPage)
Set objFSO = CreateObject("Scripting.FileSystemObject")

Set objTS = objFSO.OpenTextFile(sFilename, 2, True)

' -- note that using For .. Each on the Request.Form
' collection does not return the fields in any
' specific order.
For Each objField in Request.Form
' -- write one field per line to the output file, name = value
objTS.WriteLine objField & " = " & Request.Form(objField)
Next

objTS.Close
Set objTS = nothing
Set objFSO = nothing
End Function

要記住每一頁的數據都由后面一頁來負責寫入文件,所以第二頁必須知道它是跟隨第一頁的,依次類推。用一個session 變量 CurrDataPage把當前表單數據頁碼從這一頁的開始帶到下一頁的WritePage() 調用中,而不是在調用中使用每個頁碼的硬編碼。一會兒就能看到,將所有頁碼結合起來的程序要知道最后一個頁碼是什么,所以使用一個子程序來給CurrDataPage 賦值,同時查看它是不是最后一頁的頁碼。

< %
Sub SetCurrPage(iPageNum)
If iPageNum > Session("MaxDataPage") Then
Session("MaxDataPage") = iPageNum
End If
Session("CurrDataPage") = iPageNum
End Sub
% >

這樣,每一頁(第一頁除外)在腳本< BODY > 區的頂端都是這樣的:

< %
WritePage(Session("CurrDataPage"))
SetCurrPage( {this page's number} )
% >

而第一頁腳本是:

< %
SetSessionToken
SetCurrPage( 1 )
% >

數據的最后一頁將被提交給一個確認頁,這是任何電子商務站點上的標準步驟。我們的情況需要一個確認頁,以提供一個機會來寫出最后一頁訂單的數據,并建立傳輸數據的文件。(如果不想用一個確認頁,也可以用一個腳本在結尾重定向到純ASP頁。)

GetAllData 函數將返回在不同頁中輸入的所有數據元素的組合值。

Function getAllData
dim sFilename
dim I
dim objFSO
dim objTS
dim sLine
dim sBody
dim iNumPages

iNumPages = Session("MaxDataPage")
sBody = ""
Set objFSO = CreateObject("Scripting.FileSystemObject")
' -- retrieve pages in page-number order
For I = 1 to iNumPages
sFilename = buildFileName(I)
' -- not all pages exist for every order
If objFSO.FileExists(sFileName) Then
Set objTS = objFSO.OpenTextFile(sFilename, 1)
Do While Not objTS.AtEndOfStream
sLine = objTS.ReadLine
sBody = sBody & sLine & vbcrlf
Loop
objTS.Close
Set objTS = nothing
End If
Next
Set objFSO = nothing
getAllData = sBody
End Function

要注意GetAllData 函數在每行結束時使用回車換行符(carriage-return)組合,而不是用HTML的< BR >元素。如果在確認頁上用inline文本或HTML顯示所有訂單數據,這就會是一個很長的頁。如果這么做,就會將用戶接口的關鍵部分“用戶響應時用的按鈕和控件”都推到屏幕的可視部分之外,這樣,這個頁面就遠遠不能說是很注意用戶友好性。要使頁面不是太長,可以在一個滾動文本框中顯示訂單數據,而不是使用全部的HTML。要回顧這些數據時,用戶必須使用滾動條,但是他們不必滾動整個瀏覽器窗口。

通過在< TEXTBOX >元素的VALUE 子句中嵌入一些ASP腳本,可以顯示訂單的情況:

< FORM METHOD="POST" ACTION="mailer.asp" >
< TEXTAREA ROWS="20" onfocus="this.blur()" > < % Response.Write getAllData % >
< /TEXTAREA >< /FORM >

onfocus事件的處理并不是交給ASP,而是由客戶端的javascript執行。用< TEXTAREA > 元素作為滾動顯示框
的問題是用戶可以在框中顯示的數據之上打字。This.blur() 使焦點到下一個控制上,這樣用戶就不能在文本區打字了。他們可以從文本區中選擇文本,甚至可以在其中放置文本指針,但是不能真正修改它。

如果你對用戶在數據確認顯示中打字感到擔憂,那么就可以不使用javascript,另一種選擇是使用一個隱藏的域,除了實際顯示的文本區之外,將它填充到ASP中。根據你的頁面的排版情況,甚至可以把兩個文本區處理成單獨的表單,這樣用戶看到的這一個不是帶有提交按鈕表單的一部分。

現在所有的數據都集中在一個表單中,可以用FrontPage文件或email向導來處理它,或者用CDO或MAPI的方法來email它們。要發送HTML格式的email,就要在組合的訂單說明中用< BR > 元素取代回車換行符。如果需要支持這兩種格式,可以在GetAllData 中增加第二個參數來指明輸出是不是需要直接的文本或者HTML格式。

優點和缺點

任何一種工具或方法的價值取決于試圖解決的問題和特定的環境。現在來看看這種方法的正反兩面:

優點

○ 不需要第三方控制;
○ 不需要任何客戶端表單處理(除了確認頁上一點javascript 以外,而那也是可以選擇的);
○ 腳本與HTML很好地分離。多數腳本程式位于頁面的頂端和底部;
○ 最小的內存需求。

缺點

○ 需要更多的磁盤空間,至少是臨時的;
○ 更多的磁盤輸入輸出,在輕型-中型服務器負載下,可能會比在內存中處理的解決方法速度慢;
○ 使用了Session變量(更多的細節請看使用Session變量的優點與缺點)。

結論

這個處理多頁面表單的方法很容易理解,執行起來也很直接,但是有許多地方明顯地需要加強。首先,可以用另一種方法儲存臨時數據,可能是數據庫。另一種可能是使用一個XML 文件,這樣就可以免除每一頁都需要一個單獨的數據文件的需要,而且可以利用Microsoft XML 分解工具:IE5 和 XMLDOM API。XML文件更容易被其它應用程序使用,也更容易文檔化。

尤其是在一個多服務器的環境中,我們也許會選擇用Session 對象來管理session 。在session變量的管理技術方法上有兩篇很好的文章,是Jeff Benson的 "Serialize Your Session with a Homegrown State Management Component for ASP", 和Craig McQueen的 "Maintaining User State with the Active User Object (AUO)"。

最后,我們也許想要改變創建session識別號的方式。把用戶帳號與訂單的日期和時間結合起來,就允許用戶重新開始一個以前未完成的訂單。或者也可以把以前的session識別號儲存在一個cookie 中并重新裝載到下一個session 中。

點擊此處下載本文相關文檔。

 給當前日誌評分:
Loading Vote
正在讀取評分資料...


文章來自: Tank部落格
引用通告: 查看所有引用 | 我要引用此文章
Tags:
相關日誌:

評論: 0 | 引用: 0 | 查看次數: -
發表評論
暱 稱:
密 碼: 遊客發言不需要密碼.
內 容:
驗證碼: 驗證碼
選 項:
雖然發表評論不用註冊,但是為了保護您的發言權,建議您註冊帳號.