ccidnet广告

新用户注册

赛迪社区

帮助

  新闻中心 | 关注 | 技术天地 | 软件特供 | IT财经 | 市场专家 | 互动学校 | DIY专区 | 新游戏客栈 | 媒体全文



相关文章

  使用ASP和Word进行服务器端拼写检查
  用双重验证实现网络安全访问
  实现ASP文件在线发邮件
  编写ASP图形计数器
  安全应用服务提供商,ASP家族的新生代







 当前页面位置: 主页: 技术天地: Internet开发: 技术文章

用ASP实现Web表单的数据保持
(作者:青苹果工作室编译 2000年09月27日 14:09)

  如果你使用过Internet Explorer 5,那么你可能已经有过在Web页面中保持一种状态的想法。其实这不是一个新的话题,只不过是一个新术语,它所包含的事情你可能已经在你的Web 站点上做过一段时间了。在我们所关心的范围内,保持的意思就是包含输入值或经用户设置的Web页面在下一次被打开时保持这些上一次数值的能力。

  事实上,有些浏览器能自动作到这一点--通常来源于用户选择的选项设置。比如说,当你点Back 按钮重新装载这一页时,Navigator 4 在其默认配置中能够记住你在表单的控制项上所使用的值。但是这些还是不太可靠,最好是有一种可以更加可靠地自动处理这个过程的方法。



用隐藏的表单控制域保持数值
  在特定Web 页面之间保持值的常用方法是通过使用嵌入在页面中的HIDDEN 类型控制域来实现。它与一些简单的ASP代码一起使用可以把数值从一页传递到另一页,而不用显示它们。举例说,在一个页面上< FORM > 上的控制:

  < INPUT TYPE="HIDDEN" NAME="hidThis" VALUE="< % = storedValue % >" >

  可以将数值传递到另一个页面,这个页面使用Request.Form摘取上层页面设置的数据。它可以用来设置一个可见控制域的数值,或者放置在另一个HIDDEN控制域中并传递到另一页。

  < INPUT TYPE="TEXT" NAME="txtThis" VALUE="< % = Request.Form("hidThis") % >" >

  或

  < INPUT TYPE="HIDDEN" NAME="hidNext" VALUE="< % = Request.Form("hidThis") % >" >

  有一个问题是:如果你装载了一个不传递数值的页面从而破坏了这个链条,它就永远丢失了。对此,我们可以用一些更永久和更可靠的办法。



一个更好的办法--ASP Sessions
  要存储那些我们想要放置在页面的HTML控制中的数值,ASP Sessions提供了一个很明显的办法。事实上,Sessions对于保存什么都是很有用的--甚至是从一个数据源创建的记录集,或我们想在一个用户Sessions中重复使用的对象引用。当每个用户装载一个global.asa文件作用范围内的ASP页面时,ASP都自动启动一个新的用户Session。global.asa文件的作用范围的含义换句话说就是,装载页面的文件夹包含global.asa文件,或者在这个文件夹中之上的文件夹中包含global.asa文件(距离根目录更近)。

  当你遇到一些个别数值时,可以在Session 对象中储存它们,然后再根据需要恢复它们。比如,在一个接收标准HTML页面的< FORM > 部分所传递的值的ASP页上,可以用下面代码将它们存储到Session 中:

  Session("thisValue") = Request.Form("thisValue")

  Session("nextValue") = Request.Form("nextValue")

  ... etc ...

  然后,包含< FORM >的页面 (它现在必须是一个ASP文件)可以在它装载时恢复这些值:

  < INPUT TYPE="TEXT" NAME="txtThis" VALUE="< % = Session("thisValue") % >" >

  < INPUT TYPE="TEXT" NAME="txtNext" VALUE="< % = Session("nextValue") % >" >

  但是,这需要一些预先的计划,并要注意Session变量名不被别的页面复制和覆盖。而且它不处理非文本类型的控制域,如选择框或选项按钮。要处理这些控制,当选择框或按钮在装载页面时自动设置时,需要给< INPUT >元素增加一个CHECKED属性。



在Sessions中使用数组
  通过将来自页面的值装入存储在用户Sessions中的数组,我们可以比前面的例子做得更好。并且为了防止变量名的冲突,我们要把页面的名用做Sessions变量的名。这样,只有当我们有多个拥有同样名字的页面时,才需要注意到这个问题。

  虽然通过把完整的URL作为session 变量名可以避免这个问题,但是变量名中不能包含斜线,所以必须将斜线转换成其它字符。本文中为了简短起见,我们使用页面名。

  但是你要说了,等一等,session对象不能储存数组,只能储存变量。没问题。当我们把一个数组赋给 session 时,这个session 就包含了一个到数组的变量指针。以后我们可以象恢复一个普通变量一样恢复整个数组。当数组被存储到session 中,我们做不到的是在数组中存取单独的条目--我们首先要释放它。这个过程是:

  Dim arrVals(1, 1) 'declare a 2 x 2 element array

  arrVals(0, 0) = "this " 'fill it with values

  arrVals(1, 0) = "is "

  arrVals(0, 1) = "my "

  arrVals(1, 1) = "array "

  Session("myarray") = arrVals 'and store it in the session

  然后我们用下面的代码恢复它。注意开始你不将变量(在本例中是arrVals)声明为数组:

  arrVals = Session("myarray") 'retrieve the array

  myString = arrVals(0, 0) & arrVals(1, 0) & arrVals(0, 1) & arrVals(1, 1)

  Response.Write myString 'gives "this is my array"

  我们的目标是将控制域的值与这些控制的名字储存在一起,存储在一个数组中,这个数组要跟随需要保持数值的页面来命名。换句话说,我们所要寻找的就是:

  Dim arrVals(1, 1)

  arrVals(0, 0) = "txtFirstname"

  arrVals(1, 0) = "James"

  arrVals(0, 1) = "txtLastName"

  arrVals(1, 1) = "Dixon"

  Session("getname.asp") = arrVals

  然后我们恢复它,并填充到控制中,用:

  arrVals = Session("getname.asp")

  < INPUT TYPE="TEXT" NAME="txtFirstName" VALUE="< % = arrVals(1, 0) % >" >

  < INPUT TYPE="TEXT" NAME="txtLastName" VALUE="< % = arrVals(1, 1) % >" >

  但是这还要求我们计算出哪个数组元素中保存着各个控制域的值。ASP Request集合的排序不是很整齐--它们中的值通常与页面上的控制顺序不同(在后面你就可以看到有关这个问题的证明)。我们在集合中循环并且填充数组之后,并不能保证各个值会落在数组的什么地方。



Request.Form 集合的内容
  所以,我们所要做的是创建一个简单的函数,它以控制域的名称为基础,从数组中为我们恢复正确的值。这还为我们提供了机会,在Request.Form 集合的值不能反映那些在页面中被要求设置为正确值的HTML的地方,处理选择框和按钮的情况。在我们看这个函数之前,先简要回顾一下不同的HTML控制域的名字和值是如何通过Request.Form 集合处理的。下面所显示的页面把控制域的名称作为标题。创建这些控制域的HTML是:

  FirstName: < INPUT TYPE="TEXT" NAME="FirstName" >< BR >

  LastName: < INPUT TYPE="TEXT" NAME="LastName" >

  < P >

  I enjoy:

  < INPUT TYPE="CHECKBOX" NAME="Swimming" > Swimming

  < INPUT TYPE="CHECKBOX" NAME="Reading" > Reading

  < INPUT TYPE="CHECKBOX" NAME="Eating" > Eating

  < P > OtherHobby: < INPUT TYPE="TEXT" NAME="OtherHobby" >< BR >

  OtherHobby: < INPUT TYPE="TEXT" NAME="OtherHobby" >< BR >

  OtherHobby: < INPUT TYPE="TEXT" NAME="OtherHobby" >

  < P >

  I live in:

  < INPUT TYPE="RADIO" NAME="Country" VALUE="America" > America

  < INPUT TYPE="RADIO" NAME="Country" VALUE="Europe" > Europe

  < INPUT TYPE="RADIO" NAME="Country" VALUE="Elsewhere" > Elsewhere

  < P >

  < INPUT TYPE="SUBMIT" VALUE="Submit" >

  < INPUT TYPE="RESET" VALUE="Reset" >

  下面是浏览器的样子,在这里我们可以填入一些值:


  当这被发送到服务器,我们可以检查Request.Form集合的内容,并恢复这些值。唯一令人苦恼的地方是,当两个或更多的控制名相同,以那个名字命名的Request.Form集合返回的值本身就是一个值的集合。为了解决这个问题,我们用以下代码来显示控制域的值。

'loop through the Request.Form collection

For Each varItem in Request.Form

If Request.Form(varItem).count > 1 Then

  'this is itself a collection so iterate each value

  For intLoop = 1 to Request.Form(varItem).count

   'display control name and value

   Response.Write varItem & "(" & intLoop & ") = "

           & Request.Form(varItem)(intLoop) & "< BR >"

  Next

Else

  'this is a single control item

  Response.Write varItem & " = " & Request.Form(varItem) & "< BR >"

End If

Next

  在上面的表单页面中被出这些值时,可以得到这些结果--注意名字/数值对的顺序与原始页面中控制域的顺序不同。

  FirstName = Alex

  LastName = Homer

  Swimming = on

  Country = Europe

  Eating = on

  OtherHobby(1) = Computers

  OtherHobby(2) = Gardening

  OtherHobby(3) =

  以上显示了每个文本框的名字,以及我们在其中输入的值。但是要注意三个有相同NAME属性的文本框被作为一个集合返回,由我们所输入的值组成--包括空文本框。结果还显示3个选择框中的2个的名字。没被设置(选择)的一个不在Request.Form 集合中放置数值。而且,因为我们不为选择框提供一个值,他们就只返回一个" on " 作为值。

  但是这三个选项按钮--有相同的名字--只返回一个值。这就是所选择的选项的VALUE 属性。如果我们不为每一个提供不同的值,Request.Form 集合就会只包含" Country=on ",换句话说就是所有三个选项按钮的名字和值" on "。



一个更加动态的方法
  还有一个等待解决的问题是我们不知道每个特定的页面有多少需要存储的值。我们真的想要有一种一般的方法可以反复地使用,而不用总是为获取正确的数组维数而担心。最容易的方法就是使用动态数组。在VBScript中,我们可以用ReDim 关键字创建一个动态数组,以后再用相同的关键字调整它的大小。如果我们在调整大小时包含了Preserve 关键字,我们就可以保持存储的值。有一个限制就是只能在最后一维调整多维数组的大小。

  我们例子中的页面(名为aspstate.asp )就使用了这个技术。首先声明一个能容纳255个控制名字和值的数组。注意第二列(最后一列)指数是与我们所发现的控制数不同的一个。我们还将计数器变量intIndex(当我们发现控制域时计算其个数)设置为0。

  'create a dynamic two-dimensional array

  ReDim arrVals(1, 255)

  intIndex = 0

  现在我们在Request.Form集合中循环以填充数组。

'loop through the Request.Form collection

For Each varItem in Request.Form

If Request.Form(varItem).count > 1 Then

  'this is itself a collection so iterate each value

  For intLoop = 1 to Request.Form(varItem).count

   'store control name and value in array

   arrVals(0, intIndex) = varItem & "(" & intLoop & ")"

   arrVals(1, intIndex) = Request.Form(varItem)(intLoop)

   intIndex = intIndex + 1

  Next

Else

  'this is a single control item so just store in array

  arrVals(0, intIndex) = varItem

  arrVals(1, intIndex) = Request.Form(varItem)

  intIndex = intIndex + 1

End If

Next

  现在我们的数组中已经有了全部控制域名和它们的值,intIndex 包含着Request.Form 集合中所表现的控制域个数的记数值。我们可以调整数列的大小,这样一来它就只包含元素的记数值。由于数列从第0列开始,我们就用intIndex - 1 作为新的列数。

  'resize the array to the correct size

  ReDim Preserve arrVals(1, intIndex - 1)

  现在该考虑一下我们如何将数组放入Session 对象中。我们从Request.ServerVariables 集合中收集引用页面(装载这个页面的表单页面)的名字,去掉路径。然后在Session 对象中把它用做变量名:

  'get the name for the session variable using the script

  'name of the form page which was the referrer

  strReferrer = Request.ServerVariables("HTTP_REFERER")

  strReferrer = Mid(strReferrer, InstrRev(strReferrer, "/") + 1)

  'save the array in the Session

  Session(strReferrer) = arrVals

  结束时,我们只要在页面中显示存储在数组中的值来证明我们所做的工作:

Response.Write "< P >---- values received and stored ----< BR >"

'display the array contents

For intIndex = 0 To UBound(arrVals, 2)

Response.Write arrVals(0, intIndex) & " = " _

       & arrVals(1, intIndex) & "< BR >"

Next

  这里是用我们以前使用过的相同的值所得到的结果:




取得和使用Session 值
  好,现在我们已经安全地包装好了我们的控制域值并且把它们存储在用户Session 中。由于每个用户都有唯一的Session ,这个Session 可以维持到他们关闭浏览器或不从服务器装载页面后20分钟,只有这个用户可以看到和使用他们输入的值。这些值被安全地存储在服务器内存中,而不是象我们使用HIDDEN 类型控制来保存这些值时那样,随着每个页面请求频繁地在客户机和服务器之间来回发送。

  事实上,可以用ASP的Session.Abandon 方法来中断一个Session,而通过使用Session.Timeout或编辑注册文件也可以修改timeout 。如果用户装载一个当前global.asa 文件范围以外的页面,如它的文件夹或它的其中一个子文件夹--值也会超出范围。

  我们例子中的页面aspstateform.asp 证明恢复和使用存储在一个session 中的动态数组里的值很简洁并且可以反复使用。这个页面所包含的ASP代码首先获取当前页面的名字,然后用它从session对象中恢复恢复一个动态数组。

  'get the name of the current page

  strScriptName = Request.ServerVariables("SCRIPT_NAME")

  strScriptName = Mid(strScriptName, InstrRev(strScriptName, "/") + 1)

  'retrieve the array from the Session variables

  arrVals = Session(strScriptName)

  这样我们就解决了根据需求从数组中恢复个别控制域值的问题。我们所选择的方法是在页面中使用ASP的一个定制函数。这个函数的名字是GetSavedValue ,下面就来讲解它。



GetSavedValue 函数
  给出了页面上的一个控制名,GetSavedValue 就返回一个文本字符串,这个字符串就是将控制设置成与最后一次提交给服务器相同的状态所要求的值。这是通过从存储的数组中提取值来完成的。但是我们已经发现要将选择框和选项按钮分开处理,要给函数增加第二个参数。这是一个字符串值,我们将它与数组中存储的值进行匹配。

  Function GetSavedValue(strControlName, strMatchValue)

  我们需要做的第一件事是要查看我们是否确实得到了一些值,如果是的话,有多少值。如果这一页是在一个session中第一次被装载,那么在Session 对象中就不会有任何为它保存的数组。如果我们试图用UBound函数来得到数列的大小,就会得到一个错误。代码用On Error Resume Next 来抑制错误信息,然后查看Err 对象看看是否真的发生了错误。如果是就退出。注意我们如何指明希望得到哪一维的大小--在本例中是第二维。如果我们忽略了这个参数,按照默认设置,UBound 函数就会返回第一维的大小。

  On Error Resume Next

  'find the number of values in the array

  intValCount = UBound(arrVals, 2)

  'if there is no array stored in the Session an error

  'will have been generated by the UBound function

  If Err.Number < > 0 Then Exit Function

  如果我们得到了这个值,那么现在就一定要有一个数组。我们在第一维中存储的控制名中循环,直到找到函数中第一个参数提供的控制名相匹配的。如果找到了,就要寻找一下,看看在函数的第二个参数中是否提供了一个“匹配值”,--如果是,我们就知道这是一个选择框或选项按钮。在这种情况下,我们就将“匹配值”与数组中第二维的值比较(存储的控制域的VALUE),如果它们相同就返回" CHECKED ”,如果不同就返回一个空字符串。同时,如果没有提供第二个参数,我们就简单地返回数列中第二维的值。

'return the value for a given control name

For intLoop = 0 To intValCount

  'see if the name of the control is in the array

  If LCase(arrVals(0, intLoop)) = LCase(strControlName) Then

   'see if we have to match a particular value

   '(for use with a check box or an option button)

   If strMatchValue < > "" Then

    'see if the value does match that specified

    If LCase(strMatchValue) = LCase(arrVals(1, intLoop)) Then

     GetSavedValue = "CHECKED"

    Else

     GetSavedValue = ""

    End If

   Else

    'just return the value from the array

    GetSavedValue = arrVals(1, intLoop)

   End If

   Exit Function

  End If

Next

End Function

  在两个比较测试中,我们的检查与HTML语法是大小写不敏感的。如果需要大小写敏感,可以去掉LCase 函数调用。



使用GetSavedValue 函数
  GetSavedValue 函数的结果是控制域值,或者--如果我们指明是第二个参数的匹配值的话--是字符串 "CHECKED" 或空字符串。我们可以用它预先填充我们的表单页面上的所有控制域值。以下是包含HTML控制域的部分:

< FORM ACTION="aspstate.asp" METHOD="POST" >

FirstName: < INPUT TYPE="TEXT" NAME="FirstName"

      VALUE="< % = GetSavedValue("FirstName", "") % >" >

< BR >

LastName: < INPUT TYPE="TEXT" NAME="LastName"

     VALUE="< % = GetSavedValue("LastName", "") % >" >

< P >

I enjoy:

< INPUT TYPE="CHECKBOX" NAME="Swimming"

< % = GetSavedValue("Swimming", "on") % > > Swimming

< INPUT TYPE="CHECKBOX" NAME="Reading"

< % = GetSavedValue("Reading", "on") % > > Reading

< INPUT TYPE="CHECKBOX" NAME="Eating"

< % = GetSavedValue("Eating", "on") % > > Eating

< P >

OtherHobby: < INPUT TYPE="TEXT" NAME="OtherHobby"

       VALUE="< % = GetSavedValue("OtherHobby(1)", "") % >" >

< BR >

OtherHobby: < INPUT TYPE="TEXT" NAME="OtherHobby"

       VALUE="< % = GetSavedValue("OtherHobby(2)", "") % >" >

< BR >

OtherHobby: < INPUT TYPE="TEXT" NAME="OtherHobby"

       VALUE="< % = GetSavedValue("OtherHobby(3)", "") % >" >

< P >

I live in:

< INPUT TYPE="RADIO" NAME="Country" VALUE="America"

< % = GetSavedValue("Country", "America") % > > America

< INPUT TYPE="RADIO" NAME="Country" VALUE="Europe"

< % = GetSavedValue("Country", "Europe") % > > Europe

< INPUT TYPE="RADIO" NAME="Country" VALUE="Elsewhere"

< % = GetSavedValue("Country", "Elsewhere") % > > Elsewhere

< P >

< INPUT TYPE="SUBMIT" VALUE="Submit" >

< INPUT TYPE="RESET" VALUE="Reset" >

< /FORM >



文本框控制域
  你可以看到我们在没有第二个参数的情况下,用这个函数填充一个文本控制:

  FirstName: < INPUT TYPE="TEXT" NAME="FirstName" VALUE="< % = GetSavedValue("FirstName", "") % >" >

  当它到达客户机,将会有文本值插入,例如:

  FirstName: < INPUT TYPE="TEXT" NAME="FirstName" VALUE="Alex" >

  同样对OtherHobby 文本框也适用,但是这次它们是一个集合的成员,所以 我们必须指明NAME和列数:

  OtherHobby: < INPUT TYPE="TEXT" NAME="OtherHobby" VALUE="< % = GetSavedValue("OtherHobby(1)", "") % >" >



选择框控制域
  对于选择框控制,我们将控制名作为第二个参数提供给函数。在我们的例子中,我们不必为选择框指明VALUE 属性,所以我们把" on " 当作匹配值。这是将要放在Request.Form 集合中的值,于是在我们的存储值数组中:

  < INPUT TYPE="CHECKBOX" NAME="Swimming" < % = GetSavedValue("Swimming", "on") % > >

  并且,由于当有第二个参数提供时,我们的函数返回" CHECKED "或空字符串,我们可以预见结束时客户机寻找一些东西,如下:

  < INPUT TYPE="CHECKBOX" NAME="Swimming" CHECKED >



选项按钮控制域
  选项按钮工作的方式与选择框大致相同。但是必须为它们提供一个VALUE属性,这样就可以破译出来哪个选项被选中。要记住有相同NAME 的选项按钮的完整设置在Request.Form 集合中只提供一个单一值。所以不需要在第一个参数NAME中增加按钮的列数,但是在第二个参数中必须使用VALUE。

  < INPUT TYPE="RADIO" NAME="Country" VALUE="Europe" < % = GetSavedValue("Country", "Europe") % > > Europe

  如果选择的地方是America,结果就是:

  < INPUT TYPE="RADIO" NAME="Country" VALUE="America" CHECKED > America



这是全部需要做的
  如果你尝试了样本页面,你可以看到我们得到了希望的结果。在当前用户session 持续的过程中,每个控制域值都正确地保持了。如果你还提供了浏览器的第二个版本,你会发现每个都单独保持它自己的值,因为它们都拥有自己的Session 对象。每次< FORM > 被提交,在那个session 过程中储存在动态数组中的值用表单中的新值更新。

  如果你修改了值,点击了Reset 而没有提交表单,最后的值就会被恢复。这是因为HTML的Reset 控制将控制的值恢复成原始值--在这种情况下在页面中用ASP代码事先调整值。当然,当< FORM > 和处理< FORM > 中的值的代码在同一页上时这一技术就不能使用,这是没有道理的,也就是说这种情况是表单将页面提交回它自己的情况。

  如果你在许多页面中都使用这个技术,你也许愿意考虑一些其它选择。这当然值得将创建数组并把它存储在session中、GetSavedValue 函数以及与恢复数列相关的代码提升到'include' 文件中。用一个SSI #include 指令可以轻易地把这些插入任何页面中。

  你也许还愿意考虑创建一个Active Server 组件,它可以完成同样的任务,并且可以用 Server.CreateObject 声明插入适当的页面中。虽然这可能会导致一个更高的服务器负载(因为例示组件的需要),却可以提供更好的性能,因为组件在执行中比编译的ASP代码效率更高。



与CCIDNET联系
webmaster@ciw.com.cn