在ASP中执行多个页面的表单提交

80酷酷网    80kuku.com

  

假设现在你要在站点上建立一个多页面的订货表单,从而执行从站点通过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上的。

  为了唯一识别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" > < % 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变量(更多的细节请看)。

结论

  这个处理多页面表单的方法很容易理解,执行起来也很直接,但是有许多地方明显地需要加强。首先,可以用另一种方法储存临时数据,可能是数据库。另一种可能是使用一个XML 文件,这样就可以免除每一页都需要一个单独的数据文件的需要,而且可以利用Microsoft XML 分解工具:IE5 和 XMLDOM API。XML文件更容易被其它应用程序使用,也更容易文档化。

  尤其是在一个多服务器的环境中,我们也许会选择用Session 对象来管理session 。在session变量的管理技术方法上有两篇很好的文章,是Jeff Benson的 "", 和Craig McQueen的 ""。

  最后,我们也许想要改变创建session识别号的方式。把用户帐号与订单的日期和时间结合起来,就允许用户重新开始一个以前未完成的订单。或者也可以把以前的session识别号储存在一个cookie 中并重新装载到下一个session 中。

点击下载本文相关文档。

分享到
  • 微信分享
  • 新浪微博
  • QQ好友
  • QQ空间
点击: