asp脚本对象教程
收集:TTXS(Fcbu.Com)
来源:互联网
时间:2006-06-25
动态页中服务器端的处理
前面的章节已经研究了ASP的一些内置对象。重点介绍的对象有Request、Response、Session和Application对象。ASP中另一个比较主要的对象为Server对象。本章重点介绍这个对象的背景知识和使用方法。
Server对象在服务器端脚本中通过实例和使用其他外部对象和组件,提供了一种扩展ASP页的功能的方法。事实上,许多人认为这个对象是ASP之所以能够流行的主要因素。引入Server对象的意义很大,这意味着ASP不必提供人们所需要的所有属性。它可调用其他应用程序和组件完成指定工作。
这也恰好符合了构建由独立的对象组成的应用程序的总体目标,而不是构建通常见到的那种耗尽硬盘空间的“可做每件事情”的巨型程序。不仅仅是在计算机的世界中,整个世界正在朝着组件和“即插即用”概念方面发展。如今,当汽车或电视机发生故障时,技师或工程师很可能会拔出有问题的部件并且插入一个新的部件,因此,汽车维护人员或电视机维修人员的工作也变成了面向对象的。
然而,IIS同样支持使用外部对象和与服务器环境进行交互作用的许多传统方法。这是一个特定的例外,这些方法并不是真正的ASP组成部分,但通常的确非常有用,并且通过Server对象的一些新特性已经与ASP很好地进行了集成。本章将简要地回顾这些传统方法,然后详细地介绍ASP Server对象。
本章的主要内容为:
· 传统的服务器端包含(SSI)指令的背景知识和使用方法。
· Server对象所要完成的任务,以及与SSI的比较。
· 如何使用Server对象实例、外部组件和应用程序。
· 如何使用Server对象执行封装的脚本或其他ASP页面。
· 如何使用Server对象管理在脚本中出现的错误。
· 如何使用Server对象完成与HTML或HTTP兼容的格式转换。
Server对象是ASP页中的错误处理过程的一部分,在IIS 5.0和ASP 3.0中是新的内容。本章介绍该对象是如何工作的。因为有单独的一章(第7章)专门讨论有关调试和错误处理方面的所有问题。所以本章只简要地讨论错误处理方法,并且仅限于Server对象直接涉及的过程。
4.1 动态页中服务器端的处理
就服务器端处理而言,ASP是产生动态Web网页的一种相对较新的技术。动态页意味着什么呢?先暂时不考虑客户端相关功能上的进展,也不讨论客户端脚本、Java Applet、动态HTML或ActiveX控件等内容。这里的动态页是专指服务器响应客户端请求产生的页面,并且根据情况每次产生的页面可能是不同的。
举个简单的例子,创建一个只包含当前日期和时间的页面。每次请求该页面时将显示一个不同的值,因为日期和时间取决于服务器的时钟,或取决于提供日期和时间的一个资源(例如一个独立的服务器或来自于互联网上一个标准时钟)。当然,实际上动态页要比这复杂得多,也许显示数据库记录的当前值或者邮件服务器上等待着的邮件消息的摘要。重要的是服务器不仅阅读一个无格式的HTML页面、或磁盘上的文本文件以及把它们发送给客户,而且,必须完成一些工作来创建该页面。
Internet服务器应用编程接口
第1章介绍了创建动态页的一些方法。传统的技术是使用与Web服务器的一个接口,它被称为Internet服务器应用编程接口(Internet Server Application Programming Interface,ISAPI)。
ISAPI可用于执行其他的应用程序,这些应用程序通过C语言风格的stdin和stdout数据流函数来读取客户端请求的值并创建Web服务器的响应。ISAPI应用程序所必须做的全部事情就是编写相应结果页面的文本和HTML,并通过stdout函数输出到Web服务器。事实上ASP DLL内部真正做的事情是更面向对象的。
IIS自开始就运行ISAPI的应用程序和脚本解释器。它提供一个特殊的解释器动态链接库,给出访问服务器的请求和响应的另一种方法,尽管受到一定的限制。它通过服务器端包含指令实现,之所以这样说,是因为它们是在服务器上执行的,并且结果包含在传送给客户端的响应中。这个特性在IIS中是通过一个名为ssinc.dll的动态链接库实现的。缺省情况下,IIS把文件扩展名为.shtml、.shtm或.stm的任意页面都映射到这个动态链接库。打开默认Web站点的Properties对话框,在Application Setting中单击Configuration按钮,可以看到这种映射,如图4-1所示:
这样,带有这些被映射的文件扩展名的页面将被传送给ssinc.dll进行处理。因此,执行页面中所有的服务器端包含语句,结果(如有的话)插入到服务器的响应中,即插入到客户端接收到的页面中。
因为这些文件是映射到ssinc.dll文件而不是映射到ASP动态链接库(asp.dll)的,所以在这些页面中的所有ASP代码将被忽略并且按照原有状态传送给客户端,客户端将能够看到这些脚本。然而,在ASP 3.0版本中有一个避免这种情况的方法,稍后在讨论Server对象的Execute和Transfer方法时,研究这个方法。
服务器端的包含指令
利用服务器端的包含(SSI)语句(或者指令)能够做些什么呢?实际上不多,除非打算创建在Web服务器上运行的可执行文件,并通过stdin和stdout函数访问ISAPI。这就意味能够用C、C 或其他语言(如Delphi)等编写它们,但VB并不适合。此外,使用SSI指令能够做的事情可达到与在ASP中实现同样好的效果。许多方法中,IIS的SSI特性对使用这些特性的原有的Web网站和Web页面具有向下兼容性。
然而,可能有时会希望在站点上使用SSI而不是ASP。在IIS 5.0中,服务器端的包含指令能够比以前更加容易地集成到一个远程站点上的ASP页,它们是有用的,特别是作为执行操作系统命令或原有的CGI应用程序的一种方式。后面将会非常详细地介绍可用的指令。
#include指令是这些指令之一,它已经与ASP一起使用了一段时间了,同样也在SSI页中使用。事实上,这已经对那些不具备传统的Web开发背景的ASP开发人员带来了很多混乱。
4.2.1 不可思议的ASP #include指令
在一个ASP页中,可以使用#include指令把另一个文件的内容插入到当前的页面中:
<!-- #include file=”/scripts/usefulbits.inc” -->
这条指令读取该文件的全部内容并插入到该页中,替代<!-- #include.. -->行。这是一种非常有用的插入HTML段落的技术,可反复使用。也常用该指令来插入代码段。例如,如果有一个包含几个脚本函数(或者只是单行脚本代码)的文件同时在几个页面中使用,则可以使用#include指令将其插入到需要它的每个页中。
通过把脚本和内容分开的方法,给页面提供了一个组成层次。这意味着如果对脚本进行了修改,在客户端再次打开该页面时,脚本的修改情况自动地反映到使用包含文件的每个页面中。包含文件也是一种插入服务器特定的信息的简单方法,所以把站点转移到另一个服务器不意味着必须编辑涉及原来服务器的所有页面(明显的例子是数据库连接字符串或指定一个完整的URL或服务器名字的链接)。这可以极大地减少维护费用。
例如,可以把下面的内容作为一个包含文件,命名为connect.inc:
<%
strConnect = “SERVER=myserver;DATABASE=mydb;DRIVER={SQL Server};” _
& “UID=username;PWD=secretpassword”
%>
然后可以在任何页中使用这个文件:
<!-- #include file=”path_to_fileconnect.inc” -->
<%
…
strTheConnectionString = strConnect ‘From include file
…
%>
使用包含文件的另一种情况是有些内容需要按指定的时间间隔进行修改。例如,在Wrox Web Deverloper站点上显示书目列表的网页,它包含了一个表,其中提供了所有的封面、书名和一些按钮,如图4-2所示:
这个表的HTML和文本保留在一个单独的文件中,该文件通过一条单独的#include语句包含在主页中。每次一本新书加入到该网页所基于的数据库中时,那个包含文本文件根据该数据库的情况重新创建,并作为一个文本文件写到磁盘上。
这个技术大大减少了在Web服务器和数据库服务器上的工作量,对该站点的访问者实现较快地响应。
1. 包含文件和ASP
在ASP网页(即带有.asp文件扩展名的网页)中使用的#include指令不能像一条真正的SSI指令那样进行处理,它仅是一条ASP能够识别并进行语法分析的特别指令。ssinc.dll直接用于执行SSI #include指令。然而这个由相应文件的内容替代#include指令的页面由ASP解释。
这意味着ASP对#include指令所进行的操作不实施控制。例如,可以试验以下代码:
<%
‘This will *not* work
strIncludeURL = Request.Form(“FileName”)
%>
…
<!-- #include file=”<% = strIncludeURL %>” -->
ssinc.dll将查找名为<% = strIncludeURL %>的文件,并且不可能找到,因此这段代码不会工作。
2. 包含文件的安全性
如果没有包含可执行脚本,在Web服务器上的ASP网页不能通过IIS的Web服务程序下载到一个客户端。但是,有人已经发现了偶然的安全性漏洞,比如著名的$DATA问题,所有在NTFS格式化的磁盘上保留Web内容的Web服务器都存在相应的问题。在IIS 5.0中这个问题已经得到解决。
$DATA问题的出现是因为在Windows NTFS驱动器上的所有文件都有一个缺省的“值”,即是该文件的内容,并且通过文件名加后缀“::$DATA”来指示。将其增加到一个ASP网页的URL的末尾将打乱IIS中的脚本映射关系,且允许服务器不对其中包含的脚本进行处理而不载该页面。对IIS 4.0和早期版本,有一个方法可以解决这个问题,或者可以只是增加几个映射来强制IIS正常地执行该网页:即增加对“.asp::$DATA”和“.asa::$DATA”的映射,两者都指向asp.dll文件。
包含文件的扩展各一般是.inc或.txt。如果在站点上发现一个包含文件的路径和文件名,可通过把包含文件的URL键入到浏览器的地址栏中,下载该包含文件,而不会把其作为ASP网页的一部分来执行。为防止出现这样情况,特别是在文件包含有敏感信息(诸如一个数据库链接字符串)的情况下,可能希望包含文件的扩展名为.asp。在这种情况下,如果试图下载一个包含文件,它将首先被传送到ASP,ASP将执行该文件中的所有脚本代码,并只发送出结果。如在包含文件中定义的一个链接字符串如下:
<%
strConnect = “SERVER=myserver;DATABASE=mydb;DRIVER={SQL Server};” _
& “UID=username;PWD=secretpassword”
Response.Write vbCrlf ‘Output a carriage return character
%>
客户端只能接受到单个回车符而不是脚本代码,因为该文件已经被ASP在服务器上执行了。如果不包含回车符,浏览器将挂起并等待一个响应(这并不是我们的问题,因为我们确实不打算允许用户直接访问这个文件)。
IIS 5.0和Windows的访问控制列表
在IIS 5.0中,Microsoft已经改变了Web服务器和操作系统访问服务器端包含文件的方法。
在IIS早期版本中,当ssinc.dll载入一个虚拟URL(即使用VIRTUAL = “filename”而不是FILE = “filename”)定位的一个包含文件时,将绕过Windows本身的安全性检查并忽略该文件及所存储的目录上的任何安全性设置。现在,在IIS 5.0中,运行当前ASP或SSI页面的帐号必须与对该文件和目录在Windows访问控制列表(ACL)中设置的权限相一致。如果不一致,该SSI指令运行将失败。
4.2.2 服务器端包含指令概要
除了已经讨论过的#include语句以外,还有IIS支持的五条服务器端包含指令(记住,除#include以外,这些语句不能在ASP网页中执行)。这些服务器端包含指令及说明如表4-1所示:
表4-1 服务器端包含指令及说明
指 令
说 明
#include
把一个指定文件的内容插入到将被发送给客户端的响应流中并代替该指令。例如:
<!-- #include FILE = “usefulbits.inc” -->
这条指令把名为usefulbits.inc文件的内容插入到响应中。这个文件可以由一个相对或全路径与文件名的组合描述,如FILE=”..scriptsmyscr.inc”。通过使用VIRTUAL属性,可使用一个虚拟的相对或绝对路径来描述它,例如:
<!-- #include VIRTUAL=”/mysite/ussefulbits.inc” -->
<!-- #include VIRTUAL=”../../thisbit/usefulbits.inc” -->
#config
说明在其后的指令中将用于数据、时间和文件大小以及返回给客户端的一般性的SSI错误信息的文本的格式。例如:
<!-- #config ERRMSG=”SSI Processing Error” -->
设置SSI错误信息内容为';SSI Processing Error';。
<!-- #config TIMEFMT=”%A,%B %d %Y %H:%M:%S” -->
设置由其后的SSI指令返回的日期和时间的格式。这个例子设置一个格式风格:Saturday, August 14 1999 10:34:50。可以用于格式字符串的标志的列表在附录C中给出。
<!-- #config SIZEFMT=”BYTES” -->
设置由其后的IIS指令返回的文件大小的单位。这个例子设置单位为字节。对SIZEFMT可供选择的值是“ABBREV”,指明计算值将千字节(KB)返回文件的大小
#echo
把一个HTTP环境变量的值插入到发送给客户端的响应流中并替换该指令。例如:
<!-- #echo VAR=”SERVER_NAME” -->
写出正在执行指令到该网页的服务器的名字
#exec
执行一个程序或一个服务器外壳命令,例如:
<!-- #exec CGI=”/scripts/myapp.exe?value1=this&value2=that -->
执行名为myapp.exe的CGI程序,允许传递查询字符串,程序在单独内存中执行。
<!-- #exec CMD=”cmd.exe/c iisreset/stop” -->
启动特定操作系统命令解释器(cmd.exe)并执行命令iisreset/stop。/c表示当命令结束时,命令解释器也结束。使用CMD要添加下列注册表项:
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/w3SVC
/Parameters/SSIEnableCmdDirective
设置值为1,并重启动WWW服务,就允许CMD标志用于#exec指令中。值为0,则禁止使用,并防止未验证的使用
#flastmod
把一个指定的文件上一次修改的日期和时间插入到发送给客户端的响应流中并代替该指令。
例如:
<!-- #flastmod FILE=”Default.asp” -->
像#include指令一样,也可以使用虚拟路径对该文件进行定义,如:
VIRTUAL=”/mysite/usefulbits.inc”
或
VIRTUAL=”../thisbit/usefulbits.inc”
#fsize
把一个指定的文件的大小插入到发送给客户端的响应流中并代替该指令。例如:
<!-- #fsize FILE=”Default.asp” -->
象#include指令一样,也可以使用虚拟路径对该文件进行定义,如:
VIRTUAL=”/mysite/usefulbits.inc”
或
VIRTUAL=”../thisbit/usefulbits.inc”
1. IISRESET实用程序
iisreset.exe是由IIS 5.0提供的一个新的实用程序。作为一个命令行的实用程序,如果用于执行该实用程序的帐号具有管理员权限,它对于控制运行在本地或一个网络计算机上的Internet联网服务器是非常有用的。它可用于以正确的顺序停止或启动所有的服务、显示服务的状态、重新引导服务器以及允许或禁止服务的管理。例如:
iisreset /RESTART /TIMEOUT:30 /REBOOTONERROR
这将以正确的顺序停止和重新启动所有Internet服务。如果一种服务在指定的超时周期(30秒)内未能停止或重新启动,服务器将重新引导。可以用在CMD类型的#echo SSI指令中的一些开关,使该页面不能进行匿名访问并且要求用户提供在目标服务器上具有管理员权限的有效帐号的详细情况。这个实用程序的完整描述和可用的命令开关在附录C中。
2. NET STOP和NET START命令
如果用来执行实用程序net.exe的帐号具有管理员权限,它可以用来管理服务器上运行的任何服务(即可以是本地的也可以是来自其他的一个计算机)。虽然不提倡把该程序用于Internet服务(如WWW或FTP服务),但其停止和启动其他服务的功能是非常有用的。事实上,net命令同样可以用于一系列的其他网络相关命令。
语法是:
net [start | stop> service_name
例如,可以用命令net stop cisvc和net start cisvc来停止和启动Miscrosoft Indexing Service。可以用CMD类型的#echo SSI指令使该页面不能进行匿名访问并要求用户提供在目标服务器上的具有管理员权限的有效帐号的详细情况。稍后将看到一个这样的例子。
在windows 2000帮助文件中可以找到net命令的所有选项和开关的一个完整列表。从Start菜单中选择Help项,在Help窗口的Index页查找“netcommands”。
4.2.3 服务器端包含指令的例子
本节提供了一些示例页面,可以用来对各种服务器端包含语句进行实验。打开示例网页的子目录Chapter04,显示“SSI Directives and the ASP Server Object”主页(即子目录Chapter04中的Default.asp),如图4-3所示:
本书的所有示例都可以从我们的Web网站下载。读者将在示例的子目录Chapter04中发现本章其余部分的所有示例页面。
1. 使用SSI/CGI处理指令
单击链接进入“Server-Side Include and CGI Statements”页面,这将打开ssi_cgi.stm页面。需要注意的是该页面的文件扩展名为.stm,表明这不是一个ASP网页。该页面使用了前面已经讨论过的除#exec指令(稍后将看到)以外的所有SSI指令,且显示指令的使用方法和结果,如图4-4所示:
(1)#include指令
该页的开始部分“Include Files with SSI”,显示名为intro.inc的另一个单独文件的内容。下面是该文件的全部内容:
注意我们必须使用HTML条目“<”和“>”来显示网页中的尖括号。如果不这样做,它们就不能被当作注解元素部分看待,并引起其中的指令被执行。
在主ssi_cgi.stm页面中,把这个文件插入到该页中的代码是很简单的:
<!-- #include file=”intro.inc” -->
(2)#config、#fsize和#flastmod指令
网页下一部分显示了与该页面在相同的目录中的文件Default.asp的大小和最后被修改的时间。这里三次使用了#config指令:
· 一次是设置SSI错误信息。
· 一次是设置日期和时间的格式。
· 一次是设置文件大小计算的格式。
使用#fsize和#fiastmod指令把值插入到该网页中:
<P><DIV CLASS="subhead">SSI Statements</DIV>
<!-- #config ERRMSG="SSI Processing Error" -->
(sets error message in case of SSI error)<BR>
<!-- #config ERRMSG="SSI Processing Error" --><P>
Details of file ';Default.asp';:<BR>
<!-- #config SIZEFMT="BYTES" -->
(sets fsize to return size in bytes)<BR>
<!-- #config SIZEFMT="BYTES" -->
<!-- #fsize FILE="Default.asp" -->
returns: <B><!-- #fsize FILE="Default.asp" --> bytes</B><BR>
<!-- #config TIMEFMT="%A, %B %d %Y %H:%M:%S" -->
(sets format for date/time results)<BR>
<!-- #config TIMEFMT="%A, %B %d %Y %H:%M:%S" -->
<!-- #flastmod FILE="Default.asp" -->
returns: <B><!-- #flastmod file="Default.asp" --></B><P>
(3)#echo 指令
该页的最后部分(在屏幕上只能看到一部分)显示可以使用#echo指令访问的所有HTTP报头的内容。每一行的代码都是相同的,仅仅是VAR属性值有变化。附录G中给出了VAR属性的所有容许值的一个完整列表。
<DIV CLASS="subhead">HTTP Variables</DIV>
<!-- #echo VAR="AUTH_TYPE" -->
returns: <B><!-- #echo var="AUTH_TYPE" --></B><BR>
<!-- #echo VAR="AUTH_PASSWORD" -->
returns: <B><!-- #echo var="AUTH_PASSWORD" --></B><BR>
… etc …
2. 使用#exec指令
#exec指令与其他的SSI指令相比使用起来困难一些,正因为如此,将其独立地放到了另一个页面上。可以从“ASP Server Object and SSI Directives”主菜单上访问启动页面。
在该页面上,选择“Using the #echo Server-Side Include Directive”链接。这个操作打开“The SSI #exec Directive”页面,如图4-5所示:
这是一个ASP网页ssi_exec.asp。两个按钮用来打开.stm页面,该页面执行其中使用#exec指令所描述的动作。
(1) 在服务器上运行这个示例
在SSI #exec指令示例能够在服务器上工作之前,必须对一些配置进行修改。首先,需要在Web服务器的注册表中创建SSIEnableCmdDirective项(类型DWORD),位置在下面的键名下:
HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesW3SVCParameters
然后设置该值为1,如图4-6所示:
这样就允许#exec指令与CMD属性一起使用。
其次,必须对包含使用#exec指令的.stm文件的目录禁止匿名访问,客户端将被强制提供帐号的详细情况,该帐号应是一个具有管理员级权限帐号。这也是net命令正常工作的要求。
激活Internet Services Manager应用程序,并选择包含使用#exec指令的.stm文件的目录(在示例中,这些文件是exec目录下的start_cisvc.stm和stop_cisvc.stm)。然后打开该目录的Properties对话框。在Directory Security选项卡中单击Anonymous access and authentication control区域中的Edit按钮,打开 Authentication Methods对话框,如图4-7所示:
这个对话框不选中Anonymous access复选框。如果不使用Internet Explorer访问该页面,打开Basic authentication选项以允许非IE浏览器通过提交用户名/口令访问该页面。设置时,会出现一个有关安全的警告,单击Yes。现在浏览器将被强制出示合适的帐号和身份证明,因为不能匿名访问该网页。
为了能看到启动和终止服务的结果,打开“Services MMC插件”,终止Indexing Service,如图4-8所示:
(2) 启动和终止Indexing Service
单击示例Web网页上的按钮,启动Microsoft Indexing Service。
这个服务的短名称为cisvc,它通常称为Microsoft Index Server,名称中的“ci”字符,实际上代表“content indexer”。
出现提示时,输入在Web服务器上的具有管理员权限的一个帐号的用户名和口令。当该页面(start_cisvc.stm)打开时,你将感觉到一定的延迟,这是因为#exec指令载入一个窗口命令解释器(cmd.exe)的实例,然后执行net start命令。一旦服务启动(或者如果已经在运行),将显示该页面的其余部分,如图4-9所示:
这个页面的代码十分简单。可以看到#exec指令带有CMD属性,它设置为“cmd.exe /c net start cisvc”。窗体包含有重新回到前一页面的SUBMIT按钮:
<P>Processing the SSI directive:</P>
<P><B><!-- #exec CMD="cmd.exe /c net stop cisvc" --></B></P>
<!-- #exec CMD="cmd.exe /c net stop cisvc" -->
<FORM ACTION="../ssi_exec.asp">
<INPUT TYPE="SUBMIT" NAME="cmdOK" VALUE=" ">
Return to the previous page<P>
</FORM>
从前一页面可以打开其他的.stm网页,如stop_cisvc.stm用来再次终止该服务,不同之处仅在于使用了net stop命令而不是net start命令。
…
<!-- #exec CMD=”cmd.exe /c net stop cisvc” -->
ASP Server对象
正像在前面看到的那样,通过服务器端包含的经ISAPI访问Web服务器的页面,用传统动态页面指令和命令可以做相当多的事情。但同时也有一些明显的限制。
例如,可以从Request.ServerVariables集合检索到所有随同客户端的请求发送过来的HTTP报头的值。它几乎可以与使用SSI #echo匹敌,同时具备的主要优点是把这些值作为字符串返回到代码中(而#echo指令简单地把这些值插入到页面中),因此可以根据自己的愿望来检索和维护这些字符串。许多相同的参数应用于#fsize和#flastmod指令,使用带有VBScript和JScript脚本引擎中的对象的脚本,同样也可以容易地获取这个信息。在后续章节你将看到相关的细节。
#exec指令既非常有用,又受到一定的限制。实际上该指令仅运行系统命令或定制的CGI应用程序,并不能给脚本提供真正的对进程的控制。ASP Server对象提供了一种全新的方法,与#exec指令相比,能够更安全和更容易地运行其他的应用程序或组件。当然,对于一些情况,尤其是在确实需要执行一个操作系统命令或一个原有的CGI应用程序的地方,#exec是无法替代的。
为了研究Server对象,先概要介绍其所有可用的方法和属性,然后再进一步详细地进行讨论。
4.3.1 ASP Server对象成员概述
Server对象是专为处理服务器上的特定任务而设计的,特别是与服务器的环境和处理活动有关的任务。因此提供信息的属性只有一个,却有七种方法用来以服务器特定的方法格式化数据、管理其他网页的执行、管理外部对象和组件的执行以及处理错误。
1. Server对象的属性
Server对象的唯一一个属性用于访问一个正在执行的ASP网页的脚本超时值,如表4-2所示:
表4-2 Server对象的属性及说明
特 性
说 明
ScriptTimeout
整型。缺省值为90。
设置或返回页面的脚本在服务器退出执行和报告一个错误之前可以执行的时间(秒数)。达到该值后将自动停止页面的执行,并从内存中删除包含可能进入死循环的错误的页面或者是那些长时间等待其他资源的网页。这会防止服务器因存在错误的页面而过载。对于运行时间较长的页面需要增大这个值
2. Server对象的方法
Server对象的方法用于格式化数据、管理网页执行和创建其他对象实例,如表4-3所示。
表4-3 Server对象的方法及说明
方 法
说 明
CreateObject(“identifier”)
创建由identifier标识的对象(一个组件、应用程序或脚本对象)的一个实例,返回可以在代码中使用的一个引用。可以用于一个虚拟应用程序(global.asa页)创建会话层或应用程序层范围内的对象。该对象可以用其ClassID来标识,如“{clsid: BD96C556-65A3…37A9}”或一个ProgID串来标识,如“ADODB.Connection”
Execute(“url”)
停止当前页面的执行,把控制转到在url中指定的网页。用户的当前环境(即会话状态和当前事务状态)也传递到新的网页。在该页面执行完成后,控制传递回原先的页面,并继续执行Execute方法后面的语句
GetLastError()
返回ASP ASPError对象的一个引用,这个对象包含该页面在ASP处理过程中发生的最近一次错误的详细数据。这些由ASPError对象给出的信息包含文件名、行号、错误代码等等
HTMLEncode(“string”)
返回一个字符串,该串是输入值string的拷贝,但去掉了所有非法的HTML字符,如<、>、&和双引号,并转换为等价的HTML条目,即<、';>';、';&';、';"';等等
MapPath(“url”)
返回在url中指定的文件或资源的完整物理路径和文件名
Transfer(“url”)
停止当前页面的执行,把控制转到url中指定的页面。用户的当前环境(即会话状态和当前事务状态)也传递到新的页面。与Execute方法不同,当新页面执行完成时,不回到原来的页面,而是结束执行过程
URLEncode(“string”)
返回一个字符串,该串是输入值string的拷贝,但是在URL中无效的所有字符,如?、&和空格,都转换为等价的URL条目,即?、&和
4.3.2 创建其他对象的实例
在前一章中,讨论了ASP的虚拟应用程序概念,了解了虚拟应用程序通过Application Protection设置为ASP网页中的组件和其他对象提供进程隔离。这延续了第1章的讨论,即ASP的ObjectContext对象如何为ASP网页提供运行环境,以及如何使用在相同的环境中运行的其他组件和对象。
ASP Server对象提供创建这些组件和应用程序实例的功能,因此可用来扩充ASP脚本的能力。通过实现CreateObject方法的一个特定版本来实现这个功能。
1. 在VBScript和JScript中创建对象实例
在VB或VBA中,可使用多种方法创建对象的实例。可以使用New关键字来创建指定类型的一个新对象:
Dim objNewObject As New MyComponent
然而,不能在ASP中用VBScript或JScript这么做,因为这些脚本引擎不能实现数据类型定义。不能声明一个变量为任意指定的数据类型,其变量都是Variants类型,或一个等价的类型(根据使用的脚本语言而定)。
在VB和VBA中另一个方法是使用CreateObject或GetObject方法。CreateObject方法的参数是一个ClassID(通常情况)或一个ProgID字符串,它返回相应类型的一个新对象:
Set objNewObject = CreateObject(“ADODB.Connection”)
当拥有一个指定的文档类型,并且想创建一个可以处理这种文档的对象实例时,通常使用GetObject方法:
Set objExcel = GetObject(“C:myfilessales.xlw”)
也可以指定所需要的对象类型和文件名,在几种对象都能处理该文档类型的情况下,这种做法是非常有用的:
Set objExcel = GetObject(“C:myfilessales.xlw”,”Excel.Application”)
VBScript支持CreateObject和GetObject方法。JScript也有getObject方法,与VBScript中的GetObject工作方式相同。JScript中的ActiveXObject实现了与VBScript的CreateObject方法相同的功能。但这个函数常与JScript的new运算符协同使用:
ObjNewObject = new ActiveXObject(“This.object”);
除了VB的NEW关键字在VBScript和JScript中不予支持以外,能够使用所有这些技术在一个ASP网页中创建对象的实例。然而,能够并不意味着应该,而且大多数情况下不应该在一个ASP网页中使用脚本引擎的对象创建函数。
2. 在ASP网页中创建对象实例
为了理解一般的脚本引擎对象创建方法为什么在ASP网页中使用效果不理想,需进一步对ASP中的环境和ObjectContext对象进行讨论。
使用脚本引擎的一般方法在一个ASP网页中创建一个对象实例时,该对象在当前执行的页面的环境中并未实例化。得不到ObjectContext对象的引用,所以不能使用该对象来访问页面的环境,即不能访问该页面环境中的值。
这意味着该对象不能使用内置的ASP对象,即不能够访问在Request、Response、Application和Session对象的集合中的值,也不能使用内置的ASP对象提供的方法和属性。该对象也不能够与此环境中任何现有的事务进行交互。如果发生错误,不能使用ObjectContext方法放弃一个事务。
当然,你可能不想与该网页的环境进行交互。但是有其他的理由说明使用一般的对象创建方法通常是不明智的。IIS自动地在COM 运行期包装程序hllhost.dll中实例化对象,使得该对象可以在当前的虚拟应用程序中完全地共享和重新使用(缺省的Web网站本身是一个虚拟的应用)。
你在上一章所看到的对一个虚拟应用程序的设置,既允许在Web服务器的内存空间中创建对象,也可以在共享的或独立的进程外DLLHost.dll实例中创建对象。如果使用一般的脚本引擎对象创建方法,将绕过所有的组件隔离和可扩展特性。而在使用ASP Server对象的CreateObject方法时会自动地提供这些特性。
3. Server对象的CreateObject方法
为了试验CreateObject方法,打开示例的Chapter04主菜单页,单击“Using the ASP Server Object”链接,如图4-10所示:
这个链接打开一个名为show_server.asp的网页,该页面可以用来试验所有的Server对象的方法。它同时也显示Server对象唯一的属性ScriptTimeout的值、其缺省值是90秒,如图4-11所示:
在该页面的“Create an Instance of a Component”区域,有一个文本框,可以在其中键入想要在该网页的环境中创建的对象的ProgID字符串,甚至可以键入一个ClassID数值。这里文本框的缺省值已经设置为一个来自ActiveX数据对象库的公用对象的ProgID:ADODB.Connection。
单击“Server.CreateObject”选项旁的按钮,把该页面提交给其自身,因为所有的控件都在一个带有ACTION属性的<FORM>上,而这个ACTION属性被设置为这个网页的路径和文件名:
…
FORM ACTION="<% = Request.ServerVariables("SCRIPT_NAME") %>" METHOD="POST">
<P><DIV CLASS="subhead">Create an Instance of a Component</DIV>
<INPUT TYPE="SUBMIT" NAME="cmdCreateObject" VALUE=" ">
Server.CreateObject ("
<INPUT TYPE="TEXT" NAME="txtProgID" SIZE="25" VALUE="ADODB.Connection">
")<P>
…
</FORM>
…
当该页面重新载入时,该页中的一段ASP代码(位于<FORM>段的前面)将查看提交该窗体时,单击了哪个按钮。如果是名称为“cmdCreateObject”的按钮时,该代码将读取文本框中的ProgID字符串。为防止用户输入的ProgID无效而导致执行中止,关闭缺省的脚本错误处理,再尝试使用Server.CreateObject方法创建一个对象的实例。最后,再重新打开缺省的错误处理,通过使用IsObject函数检查是否创建了一个对象实例,并显示一个相应的信息:
QUOT = Chr(34) ';double-quote character
…
';look for a command sent from the FORM section buttons
If Len(Request.Form("cmdCreateObject")) Then
strProgID = Request.Form("txtProgID")
On Error Resume Next
Set objObject = Server.CreateObject(strProgID)
On Error Goto 0
If IsObject(objObject) Then
Response.Write "<B>Results:</B><BR>Sucessfully created object with ProgID of <B>" _
& QUOT & strProgID & QUOT & "</B><HR>"
Else
Response.Write "<B>Results:</B><BR>Failed to create object with ProgID of <B>" _
& QUOT & strProgID & QUOT & "</B><HR>"
End If
End If
…
图4-12所示的是创建ADODB.Connection对象的结果。可以看到该对象已被正常实例化,已可以在代码中使用。
本章不讨论如何使用这些对象,你可能已经对此很熟悉了。一旦创建了对象实例,就可以像在任何其他情况中一样使用它。调用对象的方法,读取或设置属性,与使用VB时一样;或者用浏览器中客户端的VBScript、JScript使用它。
在接下来的章节中将对对象和组件的使用进行更加详细的介绍。我们将研究由脚本引擎实现的一些对象,以及IIS 5.0/ASP 3.0中的可安装组件,还有一些其他的免费或商用的组件,并讨论在各种情况下如何选择相应的组件。在本书的后面,甚至会说明创建自己的能够在ASP中使用的组件是非常简单的。
4.3.3 执行其他的网页
ASP 3.0和IIS 5.0的新特性之一就是引入了可编程的服务器端重定向(server-side redirection)的概念。这意味着,可以把一个网页的控制和执行转到另外一个网页,而不需要在客户端使用Response.Rdedirect方法。
1. 客户端重定向带来的问题
ASP编程人员通常使用Response.Redirect语句把一个页面载入到当前正在执行的网页。然而,许多人没有意识到这条语句不会自动地使服务器立即装入和执行新的网页。其真正做的是把一个HTTP重定向报头(redirection header)增加到由Web服务器发送给客户的输出流中。这个报头如下:
HTTP/1.1 302 Object Moved
Location newpage.asp
在这个报头中的标准HTTP状态信息“302 Object Moved”,告知浏览器所要求的资源已经发生移动。Location报头提供相应的网页地址。当然这个地址不一定是真实的,现在正在做的事情就是“欺骗”浏览器,使浏览器认为可在另一个位置上找到所需要的网页。实际发生的是,服务器将执行所请求的网页,但是通知浏览器需要的网页已经发生移动。这就是在发送任何页面的内容到浏览器之前必须执行Redirect方法的原因。
当一个浏览器接受到“302 Object Moved”信息时,中断当前的请求并为Location值中指定的网页发送一个新的请求。这与在网页的<HEAD>段使用一个META HTTP-EQUIV标记时的工作方式相同,前面给出的HTTP报头还可写为:
<META HTTP-EQUIV=”REFRESH” CONTENT=”0;URL=newpage.asp”>
因此重定向实际上发生在客户机端,而不是在服务器上。如果在这个连接的客户端有一个代理服务器在使用的话,可能会引起显示虚假消息。这就是在使用Response.Redirect时,“The object you requested has been moved and can be found here”消息经常在客户机上显示的原因,正确地使用缓冲通常可以防止这个问题。
在IIS 4.0或更早的版本中使用Response.Redirect时,应该在ASP网页的开头打开缓冲,然后在执行Response.Redirect方法之前调用Response.Clear。当然,在ASP 3.0中网页缓冲的缺省状态为打开,因此这不成问题。只要在执行该语句之前使用Response.Clear,以前产生的输出将不会发送给客户。
2. 在ASP 3.0中服务器端的重定向
在ASP 3.0和IIS 5.0中,在几乎所有情况下,通过使用两个新的Server对象方法Execute和Transfer,可以避免使用客户端重定向。这两个方法使控制立即转到另一个网页,该网页可以是一个ASP网页或者是任何其他的资源,例如一个HTTP网页、压缩文件或其他类型的文件。
它们之间的不同之处是:Execute方法“调用”另一个的网页,与在脚本代码中调用一个子程序或函数非常相似。当另一个网页或资源已经执行完毕或传送到客户端时,控制返回到原网页中调用Execute方法的语句的下一条语句,并继续执行。而使用Transfer方法时,控制不再返回到原页面中,在控制传送到的网页或资源的末尾处,执行过程停止。
当前网页的环境也传送给了目标网页或资源,因此这两个方法更有用。网页环境包含了原有的ASP对象中的所有变量的值,例如Request、Response和Session对象的集合以及它们的所有属性。即使该网页不在同一个虚拟应用程序中,也将传送Application对象的环境。
结果是浏览器认为它仍在接收原先的页面,它并不了解服务器所做的事情。浏览器的地址栏一直显示相同的URL,并且Back、Forward和Refresh按钮正常地工作。在使用客户端重定向时,尤其是使用HTML META元素时,情况通常不是这样的。
传送到新的页面或资源的环境包括所有现存的事务状态(transaction state)。当前网页的环境用ASP的ObjectContext对象(在第1章中已经讨论过)进行封装。如果需要将这个对象作为一个正在进行的事务的一部分,可以在传送控制的目的页面中使用这个对象。
(1) Server对象的Execute和Transfer方法的使用
在前面的示例页面中,可以试验使用Excute和Transfer方法。该页面包含了在示例中已经提供的另一个文件名字another_page.asp,它作为这两个方法的缺省参数值,如图4-13所示:
单击Server.Execute和Server.Transfer方法的按钮,提交到此窗体并重新装载该窗体。在这个页面顶部的脚本代码查看是哪个按扭被单击。如果是cmdExecute或cmdTransfer按钮,则把当前网页的路径写入到输出流中,然后调用相应的方法,并传送与该按钮相联系的文本框中的值,然后再把当前页面的路径写到输出流中。
…
If Len(Request.Form("cmdExecute")) Then
strPath = Request.Form("txtExecPath")
Response.Write "Currently executing the page: <B>" _
& Request.ServerVariables("SCRIPT_NAME") & "</B><BR>"
Server.Execute (strPath)
Response.Write "Currently executing the page: <B>" _
& Request.ServerVariables("SCRIPT_NAME") & "</B><BR>"
End If
If Len(Request.Form("cmdTransfer")) Then
strPath = Request.Form("txtTransferPath")
Response.Write "Currently executing the page: <B>" _
& Request.ServerVariables("SCRIPT_NAME") & "</B><BR>"
Server.Transfer (strPath)
End If
…
当单击Server.Excute方法的按钮时,会看到当前页面的路径,这是由上面代码中的第一条Response.Write语句创建并显示的。后面接着的内容是来自被执行的网页(another_page.asp)的一些输出内容。在这之后是第二个Response.Write语句的输出内容,这表明控制又回到了原先的网页,屏幕如图4-14所示:
页面的两条水平线之间的段落(显示当前执行的网页为show_server.asp)来自原先的网页。在接下来的段落来自被执行的网页another_page.asp。下面是该页面的完整代码:
<%@ LANGUAGE=VBSCRIPT %>
<HR>
Currently executing the page: <B>another_page.asp</B><BR>
However the value of <B>Request.ServerVariables("SCRIPT_NAME")</B> is still <BR>
<B><% = Request.ServerVariables("SCRIPT_NAME") %></B>
because the <B>Request</B> collections hold<BR>
the same values as they had in the page that executed this one.<BR>
<FORM ACTION="<% = Request.ServerVariables("HTTP_REFERER") %>" METHOD="POST">
<INPUT TYPE="SUBMIT" NAME="cmdOK" VALUE=" ">
Return to the previous page<P>
</FORM>
<HR>
注意,该页面执行时,不能使用Request.ServerVariables(“SCRIPT_NAME”)获取它的路径,因为环境仍然是原网页的。我们不得不把页面名作为文本写入,因为实在没有办法可以从ASP环境中直接获取。
这里包括了一个返回前一个网页的按钮的原因是,通过在主网页中单击相对应的按钮,可以使用Server.Transfer方法调用这个页面。这次看到了完全相同的输出,只是没有第二次路径输出,因为是“传送”这个页面而不是“执行”该页面,所以控制不会回传给原先的网页,如图4-15所示:
(2) 从ASP执行SSI网页
目前有了一个方法,如果需要的话可在ASP网页中成功地使用SSI指令。虽然这种要求不常出现,但可实现。过去的问题是,由于在SSI网页(文件扩展名是.stm、.shtml和.shtm)中不能包含ASP代码,所以程序不能“无缝”地重定向回到原先的网页,必须增加一个按钮或链接,以装载原先的或另外的ASP网页。
现在,由于有了Server.Execute方法,可以执行一个SSI网页并且将控制自动返回到原先的网页,客户端意识不到这些过程正在进行。客户端只是看到原先的ASP网页和执行结果。来自于SSI网页的任何输出都“无缝”地插入到流中。当然,如果在SSI网页完成后,不想使原先的网页继续执行,可以使用Server.Transfer方法。
为了看到这个技术的执行,把前面使用过的CGI-SSI例子网页的虚拟路径输入到Server.Excute方法(或Server.Transfer方法)的文本框中。这个路径是“../ssi_cgi/ssi_cgi.stm”。在单击按钮对Execute或Transfer方法进行调用以后,将看到.stm网页已经执行,其中有SSI指令的结果。在来自ssi_cgi.stm的内容之后出现的是原先的网页的其余部分,虽然在图4-16中看不到,但可通过滚动条看到该内容。
3. SSI #exec指令的不足
遗憾的是Execute和Transfer方法一般不能与SSI的#exec指令一起工作,因为包含这个指令的.stm网页会在调用它的ASP网页的环境中运行。在大多数情况下,它需要运行于直接引用该网页的一个独立的环境中。
存在这样的限制真是遗憾,如果没有这种限制,我们通过Server.Execute执行的网页可以“不可见地”包含来自于ASP网页的#exec指令。对前面的通过net stop和net start命令停止和启动Indexing Service的示例来说,它可能是一种理想的解决方案。
但是,我们必须求助于老的和已经验证的方法。当用户单击一个按钮时,简单地使用Response.Redirect方法来打开相关的网页:
<%
‘Look for a command sent from the FORM section buttons
If Len(Request.Form(“cmdStop”)) Then
Response.Redirect(“exec/stop_cisvc.stm”)
End If
If Len(Request.Form(“cmdStart”)) Then
Response.Redirect(“exec/start_cisvc.stm”)
End If
%>
可以试着把使用#exec指令的一个SSI网页的虚拟路径输入到示例页面的Server.Execute和Server.Transfer方法的文本框中。前面使用过的#exec示例的虚拟路径是“../ssi_cgi/exe/start_cisvc.stm”和“../ssi_cgi/exec/stop_cisvc.stm”。
4.3.4 Server对象的错误处理
ASP没有错误处理机制一直受到批评。
在VBScript中,有一个On Error Resume Next语句,它使脚本解释器忽略运行期错误并继续脚本代码的执行。接着该脚本可以检查Err.Number属性的值,判别是否出现了错误。如果出现错误,返回一个非零值。在ASP 3.0中,也可以使用On Error Goto 0“转回到”缺省的错误处理。在ASP 2.0中实际也进行这种处理,但是没有相应文档说明。
在Jscript中,有一个新的错误处理功能:C语言风格的try和catch语句。然而所有的这些错误处理技术都不是由ASP或IIS实现的,而是由ASP使用的脚本引擎实现的。
第7章专门讨论脚本和脚本引擎涉及到的调试和错误处理技术。
同时,ASP和IIS的开发小组已经增加了一个新的功能,用于在ASP网页中进行错误处理。这分为两个部分:IIS错误页面的配置及使用ASP的一个新的方法和对象。
1. Server对象的GetLastError方法
在ASP 3.0中,Server对象有一个名为GetLastError的新方法。与VBScript的Err对象不同,不能为查看是否出现了错误而随时调用该方法,只能在一个ASP定制的错误网页中使用。如果像对Err对象进行操作那样,通过关闭缺省的错误处理(用On Error Resume Next语句)来使用,则GetLastError方法不能访问错误的详细数据。
GetLastError方法要做的事情是提供更多的有关错误源和错误原因的信息。GetLastError方法创建并返回一个对象的引用,该对象是一个名为ASPError的新对象。这个对象具有一系列的属性,这些属性返回有关在GetLastError方法调用之前出现的最新错误的信息。
2. ASPError对象的属性
ASPError对象提供了九个属性说明所出现的错误的性质和错误源,并返回引发错误的实际代码,其属性及说明如表4-4所示:
表4-4 ASPError对象的属性及说明
属 性
说 明
ASPCode
整型。由ASP/IIS产生的错误号,例如0x800A009
ASPDescription
字符串型。如果这个错误是与ASP相关的错误,这个属性是错误的详细说明
Category
字符串型。错误源,即ASP内部脚本语言、或一个对象
Column
整型。产生错误的文件中的字符位置
Description
字符串型。错误的简短说明
File
字符串型。错误出现时正在处理的文件的名称
Line
整型。产生错误的文件中的行号
Number
整型。一个标准的COM错误代码
Source
字符串型。引发错误的行的实际代码
3. 配置“单个网页”错误处理
在IIS中“不可思议”地出现一个错误(例如404 Not Found)时,页面看起来像是从服务器返回给客户端的一个错误信息页面,但实际上并不是这样。它们是普通的HTML网页,在对一个错误进行响应时被下载并且发送给客户端。这些网页通常称为定制的错误网页(custom error page)。
然而,错误网页作为IIS的缺省安装部分,可根据要求定制。事实上,也可以在IIS的早期版本中建立定制的错误网页。
在IIS 4.0中,可以为每种不同类型的HTTP协议或服务器错误指定一个定制的错误网页,为服务器上任意的Web网站中的每个目录建立一个定制的错误信息网页。
(1) IIS缺省的错误网页
由IIS提供的缺省错误页面放在Web服务器的WinNTHelp目录中。在Windows 2000中的IIS 5.0的环境下,该页面放在WinNTHelpiishelpcommon目录下,如图4-17所示:
可在浏览器中打开这些文件查看结果,或者在文本编辑器中查看HTML源程序和脚本代码。当一个404错误出现时,使用的页面是404b.htm,这个文件包含一个客户端脚本代码部分,它获得当前文档的URL(从document对象的url属性中检索)并在该页面中显示:
<tr>
<td width="400" colspan="2"> <font style="COLOR:000000; FONT: 9pt/11pt 宋体">您正在搜索的网页可能已经删除、更名或暂时不可用。</font></td>
</tr>
<tr>
<td width="400" colspan="2"> <font style="COLOR:000000; FONT: 9pt/11pt 宋体">
<hr color="#C0C0C0" noshade>
<p>请尝试下列操作:</p>
<ul>
<li>如果您在“地址”栏中键入了网页地址,请检查其拼写是否正确。<br>
</li>
<li>打开 <script>
<!--
if (!((window.navigator.userAgent.indexOf("MSIE") > 0) && (window.navigator.appVersion.charAt(0) == "2")))
{
Homepage();
}
//-->
</script> 主页,寻找指向所需信息的链接。</li>
…
<script>
function Homepage(){
<!--
DocURL = document.URL;
protocolIndex=DocURL.indexOf("://",4);
serverIndex=DocURL.indexOf("/",protocolIndex 3);
BeginURL=DocURL.indexOf("#",1) 1;
urlresult=DocURL.substring(BeginURL,serverIndex);
displayresult=DocURL.substring(protocolIndex 3 ,serverIndex);
document.write(';<A HREF="'; urlresult ';">'; displayresult "</a>");
}
//-->
</script>
这会产生你经常看到的页面,如图4-18所示:
(2) IIS中错误网页的映射
当IIS检测到一个错误时,会把相应的错误页面传送给客户端。如何判别应该向客户端发送那一个页面?很明显,网页的名字应具有解决这个问题的一些信息,但事实上文件名是不重要的。错误和错误网页文件之间的映射关系是在每个目录的properties对话框的Custom Errors选项卡中决定的。
在Internet Services Manager中,在想编辑映射关系的目录上单击右键,并选择Properties。如果对示例文件进行设置,在Chapter04目录中选择server子目录,如图4-19所示:
Properties对话框的Custom Errors选项卡在IIS安装时(除非已经进行过修改)设置了缺省映射关系的列表,如图4-20所示:
靠近该列表的底部是HTTP错误500:100的一个条目。类型500错误是由ASP产生的,可以从中看出一些错误已经与错误网页建立了映射关系。这些错误都是一般性的错误,比如“Invalid Application”、“Server Shutting Down”等等。然而,如果ASP载入包含语法错误的页面,或者出现一个运行期错误,则出现500:100错误页面。在列表中显示的缺省映射关系表明,在这个目录中的一个文件出现上述错误时,将执行500-100.asp页面。
当一个ASP错误出现时,我们所看到的信息不再是一个普通的Web网页,而是一个ASP Web网页(也就是说它具有文件扩展名.asp)。也可以根据需要编辑该映射关系来指向另一个页面。
(3) 指定一个定制的错误网页
单击Custom Errors选项中的Edit Properties按钮,打开Error Mapping Properties对话框。在Message Type下拉列表中选择URL,键入自己的定制错误网页的完整虚拟路径,如图4-21所示:
在图4-21中给出的值指向我们创建的与示例网页一起使用的一个定制错误网页。根据你安装示例文件的具体位置,可能要使用不同的路径。
现在无论何时出现一个500:100类型的错误,将打开我们的定制错误页面。Message Type的其他两个选项是:
· Default(缺省):可以简单地输入一个短的文本信息,而不是指定一个发送给客户端的页面。
· File(文件):指定一个HTTP错误网页的物理路径。
在选择File选项时,指定的网页由IIS载入,载入的方式与在Windows Explorer中双击要载入的文件时的方式相同。这意味着ASP网页不能使用这个选项,因为在这种情况下不会执行其中的任何脚本。
4. 使用GetLastError方法和ASPError对象
配置好IIS后,在编辑了错误映射属性的目录内的任一页面上出现一个与ASP相关的错误时,都将载入定制错误页面。实际上,现在已经设置了一个正常的脚本错误陷阱,因为在这个目录内的任何一个网页上的ASP运行期错误都将触发定制错误页面。
事实上在内部IIS通过Server.Transfer方法进行这种操作,这意味着能够访问正在运行的原网页的全部环境。可以在脚本环境中获取信息,这样可以根据所出现的错误决定要做些什么。在此基础上,可以在定制的错误网页中检索ASPError对象,找到引起载入页面出错的错误的所有信息。
在IIS 4.0中,编辑错误映射属性要做一些类似的工作。但是只有一般的500错误(“Internal Server Error”)在映射中是可用的。另外,当定制错误网页载入时,不会传送网页的环境,除了提供一个非特定的错误信息外,做其他任何工作都是比较困难的。
在以前例子中已经使用过ASP Server Object示例页面,在其中可以看到ASPError对象的详细情况。单击Server.GetLastError()对应的按钮,如图4-22所示:
这个操作会重新载入该网页,其中的ASP脚本查看点击的是哪个按钮。如果是Server.GetLastError()对应的名为cmdGetError的按钮,将执行一些示例代码,这些代码将会产生一个运行期脚本错误。
…
If Len(Request.Form(“cmdGetError”)) Then
Dim arrThis(3)
ArrThis(4) = “Causes an error”
End If
…
因为已对这个目录设置了错误网页映射,即配置为装入定制错误页面,所以当错误出现时,就打开这个页面(通过Server.Transfer方法在后台不可见地工作),见图4-23所示:
(1) 示例错误网页代码的功能
定制错误网页显示ASPError对象属性的所有值,并通过使用Response.Status方法,把一个HTTP报头状态消息返回给客户端,指明出现了一个错误。接着使用GetLastError方法获取对ASPError对象的一个引用,因此可以访问错误的详细数据:
…
<%
Response.Status = "500 Internal Server Error"
Set objASPError = Server.GetLastError()
%>
Currently executing the page: <B>show_error.asp</B><P>
<B>Error Details:</B><BR>
ASPError.ASPCode = <% = objASPError.ASPCode %><BR>
ASPError.Number = <% = objASPError.Number %> (0x<% = Hex(objASPError.Number) %>)<BR>
ASPError.Source = <% = Server.HTMLEncode(objASPError.Source) %><BR>
ASPError.Category = <% = objASPError.Category %><BR>
ASPError.File = <% = objASPError.File %><BR>
ASPError.Line = <% = objASPError.Line %><BR>
ASPError.Column = <% = objASPError.Column %><BR>
ASPError.Description = <% = objASPError.Description %><BR>
ASPError.ASPDescription = <% = objASPError.ASPDescription %>
<FORM ACTION="<% = Request.ServerVariables("HTTP_REFERER") %>" METHOD="POST">
<INPUT TYPE="SUBMIT" NAME="cmdOK" VALUE=" ">
Return to the previous page<P>
</FORM>
值得注意的一点是,如果一个脚本或ASP错误出现在定制错误网页中,IIS将仅仅返回一个与错误代码500:100对应的一般性消息。这可能是脚本引擎自己的错误消息,或者只是相当简单的消息:“Internal Server Error”。不会再次重新载入定制的错误网页。
包含错误的网页的全部环境将传送给定制错误网页。也就是说,可以使用存储在任何ASP内部对象集合或属性中的值。例如,如果检索来自Request.ServerVariables集合的HTTP_REFERER值,它将反映调用原网页的网页(即在错误出现之前的网页)的URL。在服务器把执行转到错误网页时,这个值不会发生变化,并且它将不包含当错误发生时正在执行的网页的URL。
同样,SCRIPT_NAME值将是包含该错误的网页的名字,而不是错误网页的URL。在一个错误网页已经装入时,通过检查浏览器地址栏中的URL,可以对此进行确认。但是在原网页的脚本变量中存储的值,在定制的错误网页中都是不可用的。
如果原ASP网页正在一个事务内运行,即在网页的最前面包含有一个<% @TRANSACTION=”…” %>指令,也应该确定是否需要在网页中采取一些方法,以退出该事务。例如可以调用内置ObjectContext对象的SetAbort方法:
objectContext.SetAbort ‘Fail the transaction if an ASP error occurs
在本书的后面将介绍与事务的相关全部内容。
(2) 使用ASPError对象的属性
关于使用ASPError对象的属性,有以下几点值得注意的:
· 即使没有出现错误,Number属性应该一直有一个值。如果ASP网页调用GetLastError方法时没有错误出现,该属性的值是0。通常情况下,对ASP脚本的运行期错误,Number属性返回十六进制的值“0x800A0000”,加上标准的脚本引擎错误代码。例如,前面的例子对“Subscript out of Range”错误的返回值为“0x800A0009”,因为VBScript对该类型错误的错误代码是“9”。
· 当出现已经过一个错误时,Category和Description属性将一直有一个值。
· APSCode属性的值由IIS产生,对大多数脚本错误将为空。更多情况下,涉及外部组件使用出错时有相应的值。
· ASPDescription属性的值由ASP预处理程序产生,而不是由当前正在使用的脚本引擎产生的,并且对大多数脚本错误而言将是空的。更多情况下,对诸如对ASP内置对象调用无效的方法的错误有相应的值。
· File、Source、Line和column属性仅在错误出现时,并且在错误的详细数据是可用的情况下才能进行设置。对一个运行期错误,File和Line属性通常是有效的,但是column属性经常返回-1。当错误是一个阻止页面被ASP处理的语法错误,才返回Source属性。一般在这些情况下,Line和Column属性是有效的。如果把Source属性的值写到页面,明智的办法是先将该值传给HTMLEncode,以防在其含有非法的HTML字符。在本章的后面将详细地讨论HTMLEncode方法。
4.3.5 获取Server对象的路径信息
在对存储在Web网站上的文件进行操作时,需要获得文件的实际的物理路径,而不是使用虚拟路径或URL,尽管在其他网页中能用它们正常地定位文件。下一章中有一个例子,它使用FileSystemObject对Web站点的InetPubWWWRoot文件夹中的文件进行读写。当创建自己的定制组件或者使用商业化的组件对文件系统进行访问时,经常需要为其提供一个文件的物理路径。
Server对象的MapPath方法
可以从Request.ServerVariables集合中提取HTTP报头变量,它们包含了当前文件的物理路径(在DOCUMENT_NAME和PATH_TRANSLATED报头中)。Server对象提供了一个方法MapPath,可以使用这个方法对我们能够提供一个有效的虚拟路径的任何文件提取相应的物理路径。可以在已经使用过的示例网页中看到使用MapPath方法,并可试验使用该方法。
如图4-24所示,在页面的底部的Miscellaneous Methods部分,有一个按钮执行Server.MapPath方法,并提供给它靠近该按钮的文本框中的值。在该网页的源代码中已经把这个值设置为“/iishelp/default.htm”,这个文件应该自动地安装在计算机上。也可以输入另一个网页的URL。
单击该按钮重新装载这个网页,执行该方法并在顶部显示结果,在下部显示原页面的其余部分,如图4-25所示:
(1) 示例网页代码的功能
处理这个过程的代码是与前面在相似的示例文件中已经使用过的代码十分相似。
在该页面顶部的ASP脚本区域中,对单击的按钮的名字进行检查。在这种情况下,该按钮的名字将是cmdMapPath,简单地把相匹配的文本框中的值txtMapPath传送给Server.MapPath方法,并显示得到的结果:
If Len(Request.Form(“cmdMapPath”)) Then
StrValue = Request.Form(“txtMapPath”)
Response.Write “<B>Results:</B><BR>Server.MapPath (“ & QUOT & strValue _
& QUOT & “ ) returned <B>” & QUOT & Server.MapPath(strValue) _
& QUOT & “</B><HR>”
End If
(2) MapPath和虚拟应用程序目录
注意,MapPath方法为/iishelp/default.htm文件获取的结果在Web服务器目录外,并在主winnt目录的help目录中。这清楚地证明了MapPath方法是非常有用的。
对于在缺省的Web网站目录中的文件,其URL的路径部分与物理路径通常是相同的。例如,一个文件存储在Web服务器上:
c:InetPubWWWRootyourfilesthisfile.asp
如果安装时已经在缺省目录中安装了的Web根目录,则URL如下:
http://http://www.zjjv.com///yourfiles/thisfile.asp
然而,IIS Help文件安装在缺省Web网站目录外的一个虚拟目录中,所以用于对其进行访问的URL和物理路径之间没有直接的关联。只有通过使用Server.MapPath方法才能获取真实的物理路径。
4.3..6 使用Server对象格式化数据
当前面讨论演示SSI指令的网页的代码时,碰巧遇到了使用HTML的一个老问题。在一个HTML网页中如何显示HTML代码?如果“照现在的样子”使用,也就是在相应的位置上使用所有的HTML字符,会被浏览器当作HTML解释和执行。这样当下列内容在浏览器中显示时:
This is the syntax of a <TABLE> element:
将不会显示文本<TABLE>,因为浏览器将其作为一个数据表的一个开始标记,并照此来执行。为了避免这种情况,必须把在HTML中非法或无效的所有字符转换到等价的HTML字符实体(character entity)。多数常见的字符如表4-5所示:
表4-5 字符与等价的HTML实体的关系
字 符
等价的HTML实体
字 符
等价的HTML实体
<
<
>
>
&
&
“
"
?
?
?
?
所有的实体以&号开始并以分号结束,是在一些语言中表明一个实体的标准方法的一部分,这些语言是基于SGML(标准化常规标识语言)规则的,如HTML语言。
1. 数字的HTML实体等价字符串
注意最后一个例子,已经注册的商标?是一个以“#”字符为前缀的数字值,而不是相应含义的一个文本缩写(如copy对应版权符号?)。具有一个大于126的ANSI代码值的所有字符在HTML中被表示为十进制字符的ANSI代码,以&#为前缀,以分号为后缀。
事实上,需要留心的是使用数字实体等价字符串要优先于一些较少被支持文本实体字符串。一个例子是商标字符(?),该字符的实体等价字符串为“?”但不是所有浏览器(例如Navigator)都能识别这个字符串,这种情况下,将在网页中显示该实体字符串。相反,使用?在所有浏览器中都能很好地工作。
2. Server对象的HTMLEncode方法
把HTML转换为文本是进行有效显示需要的,否则HTML会被浏览器当成HTML来对待和执行,这意味着必须对无效的字符进行编码,使其成为等价的HTML实体字符串。为管理这种转换,Server对象提供了HTMLEncode方法。可以在本书提供的ASP Server Object示例网页中练习使用这个方法。
简单地把一些文本输入到HTMLEncode对应的文本框中并单击按钮。示例中提供了一些真实的HTML作为缺省的文本,如图4-26所示:
重新载入该页面时,在该页面的顶部显示结果。HTMLEncode方法把尖括号转换成了“<”和“>”,而且把双引号转换成为“";”,如图4-27所示:
(1) 示例网页代码的功能
关于得到的结果,有几个有趣的地方。
首先,在方法名字后面的括号中已经丢掉了<B>和</B>标记,相应增加了一个粗体文本部分。在网页中显示原有的值时,<B>和</B>被当成HTML提交了,所以<B>和</B>标记消失了,相关内容以粗体文本显示。
可以十分容易地避免这种情况。事实上,这就是设计HTMLEncode方法的原因。原示例代码如下:
…
Response.Write “Server.HTMLEncode (“ & QUOT & strValue & QUOT & “) returned”
…
现在所能做的就是把HTMLEncode方法应用于正在输出的值上:
…
strResult = Server.HTMLEncode(strValue)
Response.Write “Server.HTMLEncode (“ & QUOT & strResult & QUOT & “) returned”
现在输出了一个十分有用的结果,如图4-28所示:
现在已经解决了不提交HTML而显示HTML的问题。但是如果要在HTML中显示HTMLEncode方法的结果,而又不提交和处理这些结果,又会发生什么情况?为了解决这个问题,要从HTMLEncode方法本身考虑:
This is <B>"bold"</B> text
上面的语句在HTML网页中得不到同样的显示结果,这是因为HTML字符实体将被浏览器处理和执行,并显示为实体所替代的字符。换句话说,得到的是:
This is <B>”bold”</B> text
我们没有看到实体。为了避免这种情况,可两次使用Server.HTMLEncode方法。这就把“&”号变换为“&”,这样就得到了所需的显示结果。示例网页的这个部分的代码是:
If Len(Request.Form(“cmdHTMLEncode”)) Then
strValue = Request.Form(“txtHTMLEncode”) ‘Get the value from the text box
strResult = Server.HTMLEncode(strValue) ‘HTMLEncode to convert <,> and “
strDisplay = Server.HTMLEncode(strResult) ‘Then again to convert & to &
Response.Write “<B>Results:</B><BR>Server.HTMLEncode (“ & QUOT & strResult _
& QUOT & “) returned <B>” & QUOT & strDisplay & QUOT _
& “</B><HR>”
End If
(2) HTMLEncode与HTML控件的缺省值
从上面可以看出,如果在一个HTML网页中要显示HTML代码,而又不使之被作为HTML进行处理和执行,HTMLEncode方法是非常有用的。在大多数普通的ASP网页中不大可能会遇到这种情况,除非使用包含有HTML的数据库或其他数据源中的数据,而又需要作为文本进行显示。
但是HTMLEncode方法真正有用的地方是,通过设置VALUE属性预设页面中文本类型的HTML控件的值。作为例子,可看一下已经用于练习HTMLEncode方法的示例网页的源程序。创建HTMLEncode对应的文本框的HTML在HTML页中定义如下:
…
<INPUT TYPE=”TEXT” NAME=”txtHTMLEncode” SIZE=”35”
VALUE=”This is <B>"bold"</B> text”>
…
这是“手工编码”而不是使用Server.HTMLEncode方法。这里也只关心对双引号进行编码而不关心对尖括号的编码。为什么?这是因为如果没有这样做,该代码将被读为:
VALUE=”This is <B>”bold”</B> text”
而在这种情况中尖括号不会带来问题,未编码的双引号则会。在文本框中替换的实际值将是“This is <B>”,即它将在第二个双引号字符处被截断。所以,在创建预置控件值的页面时,应该考虑使用HTMLEncode方法,以避免这些值被截断:
<%
strValue = Request.Form(“txtSomeValue”)
%>
…
<INPUT TYPE=”TEXT” NAME=”txtSomeValue”
VALUE=”<% = ServerEncode(“strValue”) %>”>
…
当浏览器发送已经被HTML编码的一个控件的值给服务器时,自动进行反向译码。即服务器使用Request集合中原来格式的数据。
3. 格式化UTL的数据
还有另外一种情况,就是经常需要把一个文本字符串变换成能够在Web网页中使用的另外一种格式。现代Web服务器和操作系统都十分友好地支持包含空格字符的文件名,但是我们所使用的URL可能包含有空格字符,由于HTTP使用的URL语法不允许有空格字符(和几个其他字符),可能会出现麻烦。
另外一种更普遍的情况也会出现麻烦。当把这些值作为QueryString集合的成员传送给服务器时,将被追加到URL的末尾(在一个问号字符之后)。这种情况发生在<FORM>的METHOD属性被设置为“GET”(或者是省略了METHOD属性)的情况。换句话说,对于直接追加到URL上的值,都可能出现麻烦。这可能发生在<A>元素中:
<A HREF=http://http://www.zjjv.com///mypage.asp?title=Instant Jscript>Instant Jscript</A>
一些浏览器(例如Internet Explorer)可以对此进行处理,因为它们在把HTTP请求发送到服务器之前,自动地执行必要的转换。然而,许多其他的浏览器不进行这种转换,并导致了URL通常在第一个空格或非法字符处被截断。这样在Navigator中,上面给出的链接要求的网页变为http://http://www.zjjv.com///mypage.asp?title=Instant。在服务器上,title名字/值对的丢失部分会使代码失败。
考虑到HTTP协议定义的限制,必须从作为HTTP请求中的URL使用的字符串中删除非法的字符(非法字符是所有那些ANSI代码在126之上的字符和ANSI代码在126以下的某些字符)。
ANSI代码大于126的字符必须用百分号后跟十六进制形式的ANSI代码进行替换。这样,版权字符?变成。ANSI代码在126之下在URL中不合法的字符,同样使用相应的替代字符串;如表4-6所示:
表4-6 字符与HTTP/URL代替物的关系
字 符
HTTP/URL代替物
字 符
HTTP/URL代替物
空格
\
‘
'
>
]
!
!
^
^
#
#
`
`
$
$
{
{
%
%
|
|
&
&
}
}
(
(
+
)
)
<
<
/
/
=
=
:
:
>
>
;
;
Chr(10)
忽略
[[/TD>
[
Chr(13) [TD>
4. Server对象的URLEncode方法
Server对象提供了可以用来把任意字符串转换成相应的合法HTTP URL的方法。可以利用示例网页对这个名为URLEncode的方法进行练习,如图4-29所示:
这里,输入的值作为URL是非法的,它包含了空格和ANSI代码大于126的字符。对这个值,使用URLEncode方法的结果是所有的空格被替换成一个加号,版权符号被替换为 ,如图4-30所示:
(1) 示例网页代码的功能
在示例网页中,处理这个功能的代码非常简单,仅仅检查是否单击了URLEncode方法对应的按钮,如果单击了,把对应的文本框中的值传递给Server.URLEncode方法并显示结果:
If Len(Request.Form(“cmdURLEncode”)) Then
strValue = Request.Form(“txtURLEncode”)
Response.Write “<B>Results:</B><BR>Server.URLEncode (“ & QUOT & strValue _
& QUOT & “) returned <B>” & QUOT & Server.URLEncode(strValue) _
& QUOT & “</B><HR>”
End If
(2) 对HTML元素和其他链接使用URLEncode
URLEncode方法更普遍地用于把<A>元素或其他链接的值写到ASP网页。例如,如果在查询字符串中建立了一系列的链接,这;些链接包含来自一个数据库的值,首先应该对这个字符串使用Server.URLEncode方法:
<%
strValue = Request.Form(“txtSomeValue”)
‘Create the full URL for the link as an HTTP-legal string
strURL = http://http://www.zjjv.com///books.asp?title= & Server.URLEncode(“strValue”)
‘Make sure we don';t have any non-legal HTML characters in the page text
strLink = Server.HTMLEncode(“strValue”)
%>
…
<A HREF=”<% = strURL %>”><% = strValue %></A>
…
如果放入字符串strValue的值包含标题“Active Server Pages?”,将得到由这个代码段创建的如下所示的HTML:
<A HREF=http://http://www.zjjv.com///books.asp?title=Active Server Pages>
Active Server Pages?</A>
注意,我们不仅仅使用Server.URLEncode方法来建立一个合法的URL字符串,而且还对链接的文本使用了Server.HTMLEncode方法,以确保把所有非法的字符转换为合适的HTML等价实体。
和HTMLEncode方法一样,不用反译码ASP网页中的URL编码值。IIS自动地实现URL编码字符串的转换,该字符串在HTTP请求中转换为它们原先格式,使得它们在内置对象中是可用的。
4.4 小结
在这一章中,通过在Web服务器上发生的处理过程,讨论了为Web网页提供动态内容所涉及的一些问题。这些问题的一部分不是直接地与ASP本身相关,但对这些问题的理解,将有助于理解基本的处理工作是如何进行的。
本章介绍了IIS如何支持传统的服务器端包含指令,有一些指令可能仍然是有用的。特别是,&#35;exec指令对执行系统命令以及集成原有的应用程序都是有用的。同时也讨论了一条特别的服务器端包含指令——&#35;include语句,了解了在ASP网页内部使用这条命令的相关问题。
然而,ASP Server对象占了本章的大部分。它提供了在ASP网页内管理服务器端处理过程的方法。在Web服务器和ASP的正确的环境中,它可用来创建其他对象、应用程序或组件的实例。它同时也提供了一系列的方法,这些方法允许执行其他的网页或资源,以及以正确方式格式化信息,以便在ASP脚本和网页中使用。 Server对象也带来了一个新的ASP内置对象:ASPError对象,它为脚本提供较好的错误处理方法。现在可以提供“正统的”脚本错误处理,并获取有关错误的信息。
脚本对象的定义
脚本运行期库对象
前面章节已经介绍了ASP如何使用在服务器上定义的对象的实例,充分利用所提供的方法和属性扩展ASP的性能。有一系列的对象可供使用,包括脚本对象和标准IIS/ASP安装的组件,以及自己创建的或者从其他供应商处购买的对象。也可以在互联网上各种网站免费下载对象,并在自己的页面上使用。
这一章将讨论由ASP脚本环境提供的一般称为“脚本运行期库”(Scripting Runtime Library)的对象。这些对象通过正在使用的脚本引擎提供给代码,与ASP脚本程序一起完成多种实用任务。
还有一种组件是“活动服务器组件”(Active Server Component),通过单独的ActiveX DLL文件或者其他文件来实现。后面章节将讨论相关内容。
当然,需要研究如何在页面上使用这些对象。在前一章中,我们已经了解了服务器如何提供一个方法来实例化对象,本章将深入讨论这个内容。
本章将介绍以下内容:
· 脚本引擎以脚本对象方式提供了什么。
· 如何创建脚本对象及其他组件实例。
· 脚本对象的成员和属性概要。
· 如何在代码中使用脚本对象。
下面开始研究脚本对象的定义。
5.1 脚本对象的定义
前面章节研究了ASP对象模型。
对象模型是用来理解系统的各个部分相互关系的一种基本手段。
ASP对象模型提供了一种结构,用来作为一个整体操纵HTTP请求、响应及ASP环境中的不同元素。例如,我们已经看到,如何通过查看ASP请求对象的cookie集合,得到来自浏览器的任何cookie值。
我们使用的脚本语言也有对象模型。然而,脚本语言提供的这一对象模型,不同于由ASP DLL直接提供的对象模型,脚本对象是由Microsoft脚本运行期库(scrrun.dll)提供的,安装缺省的Active Scripting脚本引擎时,也安装了Microsoft脚本运行期库。
5.1.1 不同类型的对象和组件
不要对“对象”和“组件”这两个名词感到困惑,在一定范围内它们都可以作为ASP的一部分,同样可以通过COM对其进行访问。从概念上可以将它们分为四类:
· ASP内置对象,如ObjectContext、Request、Response、Application、Session、Server和ASPError。本书的第2章到第4章已经研究了这些内容。
· 脚本对象。通过脚本运行期库使用,如Dictionary、FileSystem和TextStream。这是本章要讨论的对象。
· 可安装的组件。由Microsoft在IIS 5.0和ASP 3.0标准安装时提供。这将在下一章讨论。
· 其他组件。从其他独立厂商购买的、在网站上发现的或者自己创建的组件。还有一些其他的由Windows服务或产品提供的组件,如Windows Scripting Host。在本书的附录中提供了相应的列表,本书专门有一部分章节讲述如何构建自己的组件。
5.1.2 VBScript和Jscript脚本对象
作为脚本运行期库的一部分,Microsoft提供三个主要的对象:
· Dictionary对象提供一个极为有用的存储对象,它用来存储值,通过对象的名字而不是其索引进行访问和引用。例如,对于存储从ASP Request对象中检索到的名称/值对,这是非常合适的。
· FileSystemObject对象提供了对服务器底层文件系统的访问(在客户端上使用IE 5.0,与名为“Hypertext Application(HTA)”的特殊类型的页面协同使用)。可用FileSystemObject对象遍历计算机的本地及网络的驱动器、文件夹和文件。
· TextStream对象提供对存储在磁盘上文件的访问,用于同FileSystemObject对象协同使用。TextStream对象能够读出或写入文本(顺序的)文件,并仅能通过FileSystemObject对象进行实例化,所以人们常常认为TextStream对象是FileSystemObject对象的子对象。
FileSystemObject对象是其他一系列用来与文件系统交互的对象和集合的“父代”。该对象提供了对象的三个集合:Drives、Folders和Files集合,每个集合分别是相应的Drive、Folder和File对象的集合。它们用来进行磁盘上的驱动器、文件夹(目录)和文件的遍历和定位。对象间的关系如图5-1所示:
下面,将依次介绍这些对象和集合,以及如何使用它们。然而,首先要理解对象实例与组件的创建或实例化方式之间的差异。这是下一节的主要内容。
创建对象生组件实例
创建脚本运行期库对象的实例与创建任何其他对象和组件的实例化方式完全相同。可使用ASP Server对象提供的CreateObject方法(确保对象创建在当前页面的环境内),或者使用一个<OBJECT>元素。我们将研究这两种方法,究竟采用那种方法依赖于页面的需要。
5.2.1 使用Server.CreateObject方法
正如在研究Server对象的时候看到的,组件或其他对象实例可根据它们的ProgID来创建:
<%
Dim objThis
Set objThis = Server.CreateObject(“ADODB.Connection”)
%>
ProgID字符串“正式的”格式是“供应商.组件.版本”,供应商的名字和版本是可选的。通常ProgID只包含前两部分(如上例)。少数供应商在ProgID中设置版本编号,这将避免向后兼容的新版本使用同样的ProgID,这要求改变ASP页面才能使用新版本。
5.2.2 使用<OBJECT>元素
可以使用标准的HTML<OBJECT>元素通过增加RUNAT参数并指定其值为“SERVER”来在服务器上创建一个组件实例。另外,通常是提供对象的ProgID字符串而不是数字的ClassID:
<OBJECT ID=”objThis” RUNAT=”SERVER” PROGID=”This.Object”>
<PARAM NAME=”param1” VALUE=”value1”>
<PARAM NAME=”param2” VALUE=”value2”>
</OBJECT>
如果上面脚本的对象有相应的属性可在脚本中使用,在<OBJECT>元素内可通过<PARAM>元素进行设置,就像通常在HTML页面中所做的一样。在ASP中使用<OBJECT>元素时不要求CODEBASE属性,当其不可用时,服务器不会试图下载以及安装对象或组件。
1. 指定一个ClassID
另外,可以指定想要创建的对象或组件的ClassID。在不知道目标机安装了什么其他组件的情况下,这是非常有用的。例如在客户端上的浏览器的页面上实例化组件时。
在理论上,组件的ProgID(文本“供应商.组件”)不应该相互冲突,应该是唯一的。然而,这不是无懈可击的。有可能美国北方的一个供应商与希腊小岛上的一个供应商同名。但是,使用ClassID识别访问时,因为ClassID是唯一的,同名情况就不会发生。
如果决定使用对象或组件的ClassID,应将其放入CLASSID属性中,而不是PROGID属性。如:
<OBJECT ID=”objThis” RUNAT=”SERVER”
CLASSID=”clsid:892D6DA7-E0F9-11D2-B2E9-00105A42AF30”>
<PARAM NAME=”param1” VALUE=”value1”>
<PARAM NAME=”param2” VALUE=”value2”>
</OBJECT>
但在自己的服务器上实例化对象时,应该知道对象和组件的安装方式。这样在ASP代码中创建对象实例时,可以安全地使用ProgID。这就是ClassID很少在ASP页面内使用的原因。然而,因为ProgID用于查找ClassID,如果愿意也可以用组件或对象的ClassID代替ProgID。
2. 设置对象实例的作用域
缺省情况下,所有ASP页面中创建的对象与组件实例(无论用Server.CreateObject方法或<OBJECT>元素)都有页面内的作用域(page scope)。这意味着,对象与组件只有该页在ASP上运行时才存在,当页面完成并且把结果发送到客户端以后就自动地取消了。
然而,如果在global.asa文件(它存在于站点或虚拟应用程序的根目录)中放置<OBJECT>声明,可以将对象或组件的作用域指定为应用程序或会话作用域。
(1) 在应用程序层作用域创建对象
通过设置SCOPE属性为“APPLICATION”,创建应用程序层作用域对象:
<OBJECT ID=”objThis” RUNAT=”SERVER” PROGID=”This.Object”
SCOPE=”APPLICATION”>
</OBJECT>
应用程序开始时创建了对象实例,即一旦用户从虚拟应用程序的目录请求一个页面,就创建对象实例。对于缺省Web站点,这可以是站点上的任一目录。直到应用程序结束(最后的用户会话结束)前,对象实例一直存在,并且可以被虚拟应用程序或站点目录内任一页面内的任意用户引用和使用。
(2) 在会话层作用域创建对象
如果想创建由单个用户使用的对象实例,其作用域为他访问的所有页面,可创建会话层作用域对象。这通过将SCOPE属性设置为“SESSION”来实现:
<OBJECT ID=”objThis” RUNAT=”SERVER” PROGID=”This.Object”
SCOPE=SESSION”>
</OBJECT>
对象一旦被引用就被创建,引用是由用户从虚拟应用程序或站点载入的页面内的程序代码完成的(在global.asa文件中有<OBJECT>声明)。当用户会话生命周期结束并被取消时,它引用的对象实例也就取消了。
(3) 关于作用域和状态
使对象实例的作用域为全局的或者为用户会话全局环境看起来是一个好主意,但在实际使用时有些问题需要考虑,其中之一是在用户的许多请求之间能够有效地保护对象的状态。换句话说,可以设定对象的一些属性,它们对使用的所有页面是共用的。因为不必每次都创建新的实例并设置其属性,所以这看起来是个较好的办法。
事实上,微软建议一般情况下不要这样做,这一思想是传统程序设计思想的残余。在Web上,要面对的最大问题是服务器以及Web应用程序及所提供的动态网页如何应付数以百万计的网站访问者。将组件实例驻留在内存中等待一个特定用户的页面请求,对可能有几百个用户同时浏览的网站来说,这样做不能有效地使用资源。
Windows 2000提供新的COM 运行期特性,它能够处理组件的创建、缓存和使用,采用一种吞吐量最大化但所占服务器资源最小化的方式。对象实例存储在哪里和存储多久的问题,最好由操作系统自己完成,而不是由程序员决定。
也就是说,在页面内需要的地方创建对象实例,当页面终止时让其消失。COM 整理这些碎片,自动处理后台的一些复杂工作。如果要了解有关这方面的内容,第14章比较详细地研究了组件的创建。
当然,在某种情况下,我们可能要求一个对象具有应用程序层和会话层的作用域,尤其是在页面请求间保存状态时。在后面讨论Dictionary对象时,将有一个这方面的实例。
5.2.3 Server.CreateObject与<OBJECT>的区别
Server.CreateObject方法立即创建一个对象实例。在大多数情况下这也是我们所希望的。而<OBJECT>元素只有首次引用一个对象时才创建指定的对象实例。因此如果在代码中停止使用该对象,则不创建该对象实例。
如果代码只在某种情况下使用这个对象(可能依赖于请求参数的值),这也许是有用的。因为如果不需要这个对象,则可以节省服务器的资源。
然而,如果肯定需要创建某一对象,可使用Server.CreateObject方法完成。用<OBJECT>元素创建对象有助于防止在代码中取消对对象的调用时,忘记取消程序中的Server.CreateObject行,当然这是一个粗心的程序设计。
最后需要记住的是,如果对象是使用Server.CreateObject方法创建的,就可以从会话或应用程序中去掉对象,但使用<OBJECT>元素创建的,则不行。
5.2.4 组件线程模型
在页面内使用对象或组件时,应该考虑的另一个问题是该对象涉及到的响应多个请求的行为方式。事实上在ASP里,这是所需要理解的最复杂的题目之一。一个组件的线程模型,结合其作用域,影响该组件和应用程序的性能和效率,也影响将它实例化的ASP页面。
线程就是由处理器执行的系统对象,用于完成由组件代码定义的任务。每一个线程都可以被认为是单个二进制指令集。在像Windows这样的多线程环境中,多个线程可同时运行。
实际上有五个线程模型(包括在Windows 2000里引入的Neutral-threading模型):
· Single-threaded(单线程):某一时刻只能有一个进程使用某组件。
· Apartment-threaded(单元线程):若干进程都可以使用某组件,但只有一个在指定的线程上。
· Neutral-threaded(中立线程):若干进程都能使用某组件,并且可以使用指定的一组线程中的任何一个。
· Multiple-threaded或Free-threaded(多线程或自由线程):若干进程都能使用某组件,并且这些进程可以运行在不同的线程上。
· Both-threaded(双线程):对象既可以是单元线程的又可以作为自由线程的。
在这里不解释线程模型的技术细节,本书后面有相应的内容。
单元线程的组件(例如使用Visual Basic创建的或作为XML脚本的组件)可在页面层作用域内很好地运行,在会话层作用域内也是可以接受的。事实上,在页面层,由于较低的数据处理开销,也能很好地运行双线程的组件。
Winodws 2000中的中立线程的模型甚至提供了更好的性能,尽管到目前为止只有很少的这样的组件和与之相适应的开发工具。
如果需要会话层组件,使用可用的双线程的组件。并且如果需要应用程序层作用域,可一直使用双线程的组件。
然而,微软建议避免使用会话层作用域的组件,甚至不使用应用程序层作用域的组件,除非这些组件是绝对需要的。使组件的活动时间超过作用域为页面级的组件所要求的时间,对于由COM 提供代理特性的对象是没有益处的。 5.2.5 引用对象类型库
在早先的ASP版本中,在脚本中使用对象或组件时,组件内定义的公共常数(如果有的话)在ASP里将不再有效。这意味着我们需要自己声明它们(或等价物)并指定相应的值。
例如,当在早期版本的ASP中使用ActiveX数据库对象(ADO)组件时(将在第8章进行详细的研究),不得不用记录集的Open方法加入预定义常数声明。例如:
Const adOpenKeyset = &H0001
Const adLockPessimistic = &H0003
Const adCmdTable = &H0002
…
rs.Open “Contact”,”DSN=GlobalExampleData;UID=examples;Password=;”, _
adOpenKeyset, adLockPessimistic, adCmdTable
…
另一种方法是使用&#35;include指令在页面插入一个名为adovbs.inc的文件。该文件由IIS/ASP提供,包含ADO所需的所有预定义常数。更新代码时,必须确认使用的是最新版本,并检查它对于所有的页面请求都可用。
对于IIS 5.0,有一个更好的方法,通过在HTML注释元素内使用METADATA指令,可以给组件或对象的类型库增加引用(IIS 4.0不支持这一功能)。
<!-- METADATA TYPE=”TypeLib”
FILE=”path_and_name_of_file”
UUID=”type_library_uuid”
VERSSION=”major_version_number.minor_version_number”
LCID=”locale_id”
-->
其中:
· path_and_name_of_file是某一类型库文件(.tlb)或ActiveX DLL的绝对物理路径,必须提供这一参数或者是type_library_uuid参数。
· type_library_uuid是该类型库的唯唯一标识符,必须提供这一参数或者是path_and_name_of_file参数。
· major_version_number.minor_version_number(可选)定义了所需组件的版本。如果没有该版本则使用最近的版本。
· locale_id(可选)是区域标志符。如果在该区域没有发现类型库,计算机将使用缺省的(安装时定义的)区域。
因此,使用这一技术,通过使用下面的代码,能使内置的ADO预定义常数在ASP页面可用:
<!-- METADATA TYPE=”TypeLib”
FILE=”C:Program FilesCommon FilesSystemadomsado15.dll”
-->
文件名msado15.dll还可用于更高版本(2.50以后)的ADO组件。
如果ASP不能装载类型库,就返回一个错误并停止该页的执行。可能的错误提示如表5-1所示:
表5-1 错误提示代码及说明
错 误 说 明
ASP 0222 无效的类型库说明
ASP 0223 未找到类型库
ASP 0224 类型库不能加载
ASP 0225 类型库不能打包(即ASP不能从指定的类型库中创建类型库包装对象)
5.2.6 在客户端上创建对象实例
在ASP中讨论在服务器上实例化对象和组件的技术时,值得强调的是在浏览器中运行客户端页面而完成同样工作的方式。如果你使用ASP创建包含客户端脚本程序的页面,或者使用<OBJECT>元素创建客户端组件实例,将会发现这是非常有用的。在大多数情况下,脚本运行期对象可在客户端上实例化和使用,效果与服务器上的ASP相同。
1. VBScript CreateObject方法
在客户端使用CreateObject时,在浏览器的环境内创建组件或对象实例,它们与浏览器运行在相同的内存空间里(即进程内),除非实现的对象是带有.exe扩展文件名的可执行文件。
通常指定对象的ClassID,而不是使用ProgID字符串,这样就不可能与其他安装在客户端的对象发生冲突。
<SCRIPT LANGUAGE=”VBScript”>
Dim objThis
Set objThis = CreateObject(“clsid:892D6DA7-E0F9-00105A42AF30”)
…
</SCRIPT>
当然也可以使用ProgID,并且使用通用的对象或组件(特别是标准安装提供的对象或组件),那么得到错误的组件的风险是很小的:
<SCRIPT LANGUAGE=”VBScript”>
Dim objThis
Set objThis = CreateObject(“Scripting.Dictionary”)
…
</SCRIPT>
2. Jscript ActiveXObject方法
为了在客户端上实例化Jscript的对象和组件,必须使用ActiveXObject方法和new操作符:
<SCRIPT LANGUAGE=”JScript”>
var objMyData = new ActiveXObject(‘clsid: 892D6DA7-E0F9-00105A42AF30’);
</SCRIPT>
或:
<SCRIPT LANGUAGE=”JScript”>
var objMyData = new ActiveXObject(‘this.object’);
</SCRIPT>
3. <OBJECT>元素技术
也可使用<OBJECT>元素创建客户端对象或组件的实例。应省略RUNAT属性或者将其设定为“CLIENT”。然而,这个属性在客户端上是被忽略的,因此设置这个属性的唯一目的就是,在ASP页面使用<OBJECT>元素实例化服务器端的组件实例时防止混淆。
<OBJECT ID=”objThis” RUNAT=”CLIENT”
CLASSID=”clsid: 892D6DA7-E0F9-00105A42AF30”
CODEBASE=”http://http://www.zjjv.com///components/mycomponent.cab”>
<PARAM NAME=”param1” VALUE=”value1”>
<PARAM NAME=”param2” VALUE=”value2”>
</OBJECT>
注意,这里出现的CODEBASE属性,表示允许下载并安装来自URL的组件(如果该组件没有安装)。IE 3.0以上的版本有此功能。
对于使用<OBJECT>元素的方法、可使用的属性、在客户端使用的值,可查看网站http://http://www.zjjv.com///workshop/author/dhtml/reference/objects/OBJECT.asp,或者Windows 2000 Platform SDK文档中的<OBJECT> tags,或者看看《IE5 Dynamic HTML Programmer’ Reference》一书,ISBN 1-861001-74-6,Wrox出版社。
Scripting.Dictionary对象
许多Microsoft的编程语言,如Visual Basic、VBScript和Jscript,都提供集合(collection)。可以把集合想象为数组,可以使用其中内建的函数完成存储和操纵数据等基本任务。无须担心数据是在哪些行列,而是使用唯一的键进行访问。
VBScript和Jscript都提供类似的对象,通称Scripting.Dictionary对象或Dictionary对象。它类似于二维数组,把键和相关条目的数据存放在一起。然而真正的面向对象的方法,不应直接访问数据条目,必须使用Dictionary对象支持的方法和属性来实现。
本章提供了一些示例页面,允许试验脚本运行期对象的方法和属性。这些实例在下载的文件的文件的Chaper05子目录里。
5.3.1 创建和使用Dictionary对象
创建一个Dictionary对象的示例如下:
‘In vbscript:
Dim objMyData
Set objMyData = Server.CreateObject(“Scripting.Dictionary”)
//In jscript:
var objMyData = Server.CreateObject(‘Scripting.Dictionary’);
<!-- Server-Side with an OBJECT element -->
<OBJECT RUNAT=”SERVER” SCOPE=”PAGE” ID=”objMyData”
PROGID=”Scripting.Dictionary”>
</OBJECT>
Dictionary对象还可用于客户端的IE中。
1. Dictionary对象的成员概要
表5-2和表5-3列出了Dictionary对象的属性和方法及相应的说明。
当增加一个键/条目对时,如果该键已存在;或者删除一个键/条目对时,该关键字/条目对不存在,或改变已包含数据的Dictionary对象的CompareMode,都将产生错误。
表5-2 Dictionary对象的属性和说明
属 性 说 明
CompareMode (仅用于VBScript)设定或返回键的字符串比较模式
Count 只读。返回Dictionary里的键/条目对的数量
Item(key) 设定或返回指定的键的条目值
Key(key) 设定键值
表5-3 Dictionary对象的方法和说明
方 法 说 明
Add(key,item) 增加键/条目对到Dictionary
Exists(key) 如果指定的键存在,返回True,否则返回False
Items() 返回一个包含Dictionary对象中所有条目的数组
Keys() 返回一个包含Dictionary对象中所有键的数组
Remove(key) 删除一个指定的键/条目对
RemoveAll() 删除全部键/条目对
2. 对Dictionary中增加和删除条目
一旦得到一个新的(空的)Dictionary,可以对其添加条目,从中获取条目以及删除条目:
‘ In vbscript:
objMyData.Add “MyKey”, “MyItem” ‘Add Value MyItem with key MyKey
objMyData.Add “YourKey”, ”YourItem” ‘Add value YourItem with key YourKey
blnIsThere = objMyData.Exists(“MyKey”) ‘Returns True because the item exists
strItem = objMyData.Item(“YourKey”) ‘Retrieve value of YourKey
strItem = objMyData.Remove(“MyKey”) ‘Retrieve and remove YourKey
objMyData.RemoveAll ‘Remove all the items
在JScript中,等价的代码为:
// In JScript;
objMyData.Add (‘MyKey’, ‘MyItem’); //Add Value MyItem with key MyKey
objMyData.Add (‘YourKey’, ‘YourItem’); //Add value YourItem with key YourKey
var blnIsThere = objMyData.Exists(‘MyKey’); //Returns True because the item exists
var strItem = objMyData.Item(‘YourKey’); //Retrieve value of YourKey
var strItem = objMyData.Remove(‘MyKey’); //Retrieve and remove YourKey
objMyData.RemoveAll(); //Remove all the items
3. 修改键或条目的值
可以通过修改键的值,或通过修改与特定的键关联的条目的数据,来改变存储在Dictionary内的数据。下面的代码改变键为MyKey的条目中的数据。
ObjMyData.Item(“MyKey”) = “NewValue” ‘ In VBScript
ObjMyData.Item(‘MyKey’) = ‘NewValue’; // In JScript
如果指定的键在Dictionary未找到,将在Dictionary中创建一个以MyKey为键,以New Value为其条目值的新的键/条目对。有意思的是,如果使用一个不存在的键来检索条目,不仅得到一个空的字符串(这是可以想到的),而且还在Dictionary里添加一个新的键/条目对,键即是指定的键,但条目的数据为空。
可以使用Key属性仅改变键的值而不改变与之对应的条目的数据。将一个已存在的键MyKey改变为MyNewKey,可以用:
objMyData.Key(“MyKey”) = “MyNewValue” ‘ In VBScript
objMyData.Item(‘MyKey’) = ‘MyNewValue’; // In JScript
如果指定的键未找到,则产生运行期错误。
4. 设置比较模式
Dictionary的CompareMode属性仅适用于VBScript,不能在JScript中使用。当比较字符串键时,允许指定比较的方式。两个允许的值为BinaryCompare(0)和TextCompare(1)。BinaryCompare(0)为二进制数对照(即区分大小写);TextCompare(1)为文本对照(即不区分大小写)。
5. 遍历Dictionary
研究Dictionary时,有两个方法和一个属性需要特别注意,它们允许我们遍历存储在Dictionary里的所有键/条目对。Items方法用一个一维数组的形式返回Dictionary里所有的条目数据,而keys方法用一个一维数组返回所有已存在的键值。可以使用Count属性得到键或条目的数量。
例如,可以使用下列代码得到名称为objMyData的Dictionary中所有的键和条目值。注意,虽然Count属性保存了在Dictionary里的键/条目数量,但VBScript和JScript的数组总是从下标0开始的。因此,数组下标应从0到Count-1。
‘In vbscript:
arrKeys = objMyData.Keys ‘Get all the keys into an array
arrItems = objMyData.Items ‘Get all the items into an array
For intLoop = 0 To objMyData.Count –1 ‘Iterate through the array
StrThisKey = arrKeys(intLoop) ‘This is the key value
StrThisItem = arrItems(intLoop) ‘This is the item (data) value
Next
// In JScript
// Get VB-style arrays using the Keys() and Items() methods
var arrKeys = new VBArray(objMyData.Keys()).toArray();
var arrItems = new VBArray(objMyData.Items()).toArray();
for (intLoop = 0; intLoop < objMyData.Count; intLoop ) {
// Iterate through the arrays
strThisKey = arrKeys[intLoop>; // This is the key value
strThisItem = arrItems[intLoop>; // This is the item (data) value
}
在VBScript里也可以使用For Each … Next语句完成同样的功能:
‘ Iterate the dictionary as a collection in VBScript
For Each objItem in arrItems
Response.Write objItem & “ = “ & arrItems(objItem) & “<BR>”
Next
5.3.2 Dictionary对象示例
本书提供了一系列示例文件可用来试验脚本运行时间库的各种属性。
本章代码的缺省页面提供了一系列可使用的VBScript示例链接。有些示例对JScript同样有效。这些示例存放在Chapter05目录下相应的子目录里,显示的界面如图5-2所示:
要查看Dictionary对象的运行,在菜单页面点击第一个链接,打开名叫show_dictionary.asp的页面。这个页面显示了我们提供的Dictionary对象的内容,允许试验其属性和方法。屏幕如图5-3所示:
1. Dictionary的global.asa文件
随Dictionary对象示例页面提供的文件之一是global.asa。它创建并预先填充了一个会话层作用域的Dictionary对象,因此其内容在页面请求之间不会丢失。一般说来(考虑到可扩展性),这不是一个理想的做法。在这个例子里,可以看到Dictionary的属性和方法的效果。
如果在自己的服务器上下载并安装示例,必须创建一个基于此global.asa文件的虚拟应用程序。或者将其内容添加到缺省站点的根文件夹中的global.asa文件里。在第3章讲述了如何用向导创建虚拟应用程序。然而对于本示例,创建一个虚拟应用程序最简单的方法是在Chapter05示例文件夹内右击dictionary子文件夹,在Properties对话框的Home Directory选项卡里,点击Create按钮,如图5-4所示:
在这个global.asa文件里,代码使用<OBJECT>元素创建一个会话层作用域的Scripting.Dictionary对象实例。然后在Session_onStart事件处理程序里将一系列值用Add方法放入Dictionary中,并将对Dictionary对象的引用指定给ASP会话变量MyDictionary:
<OBJECT ID="objBookList" RUNAT="SERVER" SCOPE="SESSION"
PROGID="Scripting.Dictionary">
</OBJECT>
<SCRIPT LANGUAGE="VBScript" RUNAT="SERVER">
Sub Session_onStart()
objBookList.Add "2610", "Professional Active Server Pages 3.0"
objBookList.Add "1274", "Instant javascript"
objBookList.Add "2882", "Beginning ASP Components"
objBookList.Add "1797", "Professional ASP Techniques"
objBookList.Add "1835", "AD0 2.0 Programmer';s Reference"
Set Session("MyDictionary") = objBookList
End Sub
</SCRIPT>
2. Dictionary示例页面
在“Scripting.Dictionary Object”主页面里,首要的任务是得到一个会话层作用域的Dictionary对象实例的引用。注意,这个引用是一个对象变量,因此必须在VBScript里使用Set关键字。
然后,检查一下是否得到了一个对象(这是个好习惯),如果没有正确地建立包含global.asa文件的虚拟应用程序,检查一下问题出在哪里。你将看到我们自己的消息代替了ASP的错误消息(但是注意,对于这一操作必须关闭缺省的错误处理)。
<%
on error resume next '; turn off error handling to test if object exists
';retrieve Dictionary object from user';s session
Set objMyData = Session("MyDictionary")
If IsObject(objMyData) Then ';found Dictionary object in Session
…
%>
<P><DIV CLASS="subhead">Iterating the Dictionary with Arrays</DIV>
<%
arrKeysArray = objMyData.Keys ';get all the keys into an array
arrItemsArray = objMyData.Items ';get all the items into an array
For intLoop = 0 To objMyData.Count - 1 ';iterate through the array
Response.Write "Key: <B>" & arrKeysArray(intLoop) & "</B> Value: <B>" _
& arrItemsArray(intLoop)& "</B><BR>"
Next
%>
…
… Other code and controls go here …
…
<%
Else
';could not find Dictionary object in the session
Response.Write "Dictionary object not available in global.asa for session"
End If
%>
显示在页面上的Dictionary内容列表是使用Dictionary对象的Key和Items方法创建的两个数组,可使用前面的代码遍历它们。
3. Dictionary页面控件
在Dictionary的内容列表下是一系列的HTML控件,可用于设定Dictionary对象的某些属性和执行各种方法。这些控件全部在一个<FORM>内,其ACTION属性值是本页面,所以窗体的内容提交回本页面。在前面的章节的示例里使用了同样的技术。
在<FORM>段中,改变属性或执行一个方法是通过一个按钮(没有标题)实现的。用于属性和方法的值放入按钮旁的文本框或列表框中。
该页的第一个按钮用于设定Dictionary里的条目的Key属性。这里使用了一个下拉列表,可以选择一个已经存在的Key值。下面的代码创建了页面内该部分的控件。为了填充列表,使用了另外一个遍历Dictionary对象的技术,即For Each … Next语句。代码如下:
…
<FORM ACTION="<% = Request.ServerVariables("SCRIPT_NAME") %>" METHOD="POST">
<P><DIV CLASS="subhead">The Dictionary Properties</DIV>
<INPUT TYPE="SUBMIT" NAME="cmdChangeKey" VALUE=" ">
Dictionary.Key ("
<SELECT NAME="lstChangeKey" SIZE="1">
<%
For Each objItem in objMyData
Response.Write "<OPTION>" & objItem
Next
%>
</SELECT> ") = "
<INPUT TYPE="TEXT" NAME="txtChangeKey" SIZE="15" VALUE="New Key Name"> "
<BR>
…
… Other controls go here …
…
</FORM>
…
4. 使用Dictionary的属性和方法
在“Scription.Dictionary Object”页面,点击用来检查并改变条目的Key属性的按钮,如图5-5所示:
把窗体再次提交给页面。该页面包含一个脚本段,检查被点击的按钮的值。它通过在Resquest.Form集合里查找按钮的名字来断定单击的是哪个按钮。如果发现一个对应于cmdChangKey的值,则从列表中或文本框中得到相应的值并用来改变Key属性:
…
';look for a command sent from the FORM section buttons
If Len(Request.Form("cmdChangeKey")) Then
strKeyName = Request.Form("lstChangeKey") ';Existing key from list box
strNewKey = Request.Form("txtChangeKey") ';New key value from text box
objMyData.Key(strKeyName) = strNewKey ';Set key property of this item
End If
…
页面重新载入后,在Dictionary的内容列表里能看到相应的结果,如图5-6所示:
页面的其余代码用来设定一个条目的Item属性,或者执行Dictionary对象的方法。下面是这些操作的代码,每段代码与演示Key属性的代码非常类似。每次都将结果显示在Dictionary的内容列表中:
…
If Len(Request.Form("cmdChangeItem")) Then
strKeyName = Request.Form("lstChangeItem") ';Existing key from list box
strNewValue = Request.Form("txtChangeItem") ';New item value from text box
objMyData.Item(strKeyName) = strNewValue ';Set the Item property
End If
If Len(Request.Form("cmdAdd")) Then
strKeyName = Request.Form("txtAddKey") ';New key value from text box
strItemValue = Request.Form("txtAddItem") ';New item value from text box
objMyData.Add strKeyName, strItemValue ';Execute the Add method
End If
If Len(Request.Form("cmdRemove")) Then
strKeyName = Request.Form("lstRemove") ';Existion key from list box
objMyData.Remove strKeyName ';Execute the Remove method
End If
If Len(Request.Form("cmdRemoveAll")) Then
objMyData.RemoveAll ';Execute the RemoveAll method
End If
…
例如,如果现在点击Add方法的按钮,在Dictionary的内容列表里将增加一个新的条目,
可以在这个页面中试验Dictionary对象的属性和方法,你将会发现什么因素及在什么环境下能引起Dictionary对象错误。例如,尝试用与已经存在的一个条目相同的键值增加一个条目,看看会出现什么结果。
Scripting.FileSystemObject对象
FileSystemObject对象提供对计算机文件系统的访问,它允许我们在代码内操作文本文件、文件夹及驱动器。它是脚本运行期库提供的对象之一,对于服务器ASP页面内的VBScript和JScript都有效。如果页面的扩展名为.hta(表示它们是HTA的一部分),它也可用在客户端的IE 5中。本节仅讨论在服务器上的ASP脚本如何使用FileSystemObject对象。
超级文本应用程序(HTA)由指定的“受信任的”页面组成,在页面的<HEAD>段里包含<HTA: APPLICATION>元素。例如:
<HTA:APPLICATION ID=”objMyApp” APPLICATIONNAME=”myApp”>
这些页面可以使用客户端脚本引擎中的一些不常用特性,这些特性中有FileSystemObject对象和TextStream对象。关于超级文本应用程序的更多信息,请访问Microsoft Workshop网站。
可以使用下面的程序创建一个FileSystemObject对象实例:
‘ In vbscript:
Dim objMyFSO
Set objMyFSO = Server.CreateObject(“Scripting.FileSystemObject”)
// In jscript:
var objMyFSO = Server.CreateObject(‘Scripting.FileSystemObject’);
<!-- Server-side with an OBJECT element -->
<OBJECT RUNAT=”SERVER” SCOPE=”PAGE” ID=”objFSO”
PROGID=”Scripting.FileSystemObject”>
</OBJECT>
在ASP页面里,增加一个对于FileSystemObject类型库的引用是非常有用的。这允许使用它直接定义的内置常数,不用像过去那样用数字等效表达式代替。整个脚本运行期库的类型库可以增加到任何ASP页面中,代码如下:
<!-- METADATA TYPE=”typelib” FILE=”C:WinNTSystem32scrrun.dll” -->如果你是在另一个目录下安装Windows,必须编辑FILE的属性值。
5.4.1 FileSystemObject对象成员概要
FileSystemObject对象提供一个属性和一系列方法,可用它们来操纵FileSystemObject对象实现的一些从属对象。这里提供了全部的内容概要,然后介绍每一个从属对象。
1. FileSystemObject的属性
FileSystemObject对象只有一个属性,它用于得到当前机器上的所有有效驱动器的列表,如表5-4所示:
表5-4 FileSystemObject对象的属性及说明
属 性
说 明
Drivers
返回本地计算机可用的驱动器列表。
2. FileSystemObject的方法
FileSystemObject对象提供了使用从属对象的一系列方法,从属对象包括Drive、Folder和File等对象。它也实现了用于TextStream对象的两个方法:CreateTextFile和OpenTextFile。根据所使用的对象的类型,将方法划分为三类。
(1) 与驱动器有关的方法
与驱动器有关的方法如表5-5所示:
表5-5 与驱动器有关的方法及说明
方 法
说 明
DriveExists(drivespec)
如果在drivespec中指定的驱动器存在,则返回True,否则返回False。drivespec参数可以是一个驱动器字母,或者是文件、文件夹的完整绝对路径
GetDrive(drivespec)
返回drivespec指定的驱动器所对应的Drive对象。drivespec可以包含冒号、路径分隔符或者是网络共享名,即:“C”、“C:”、“C:”及“\machinesharename”
GetDriveName(drivespec)
用字符串返回drivespec指定的驱动器的名称。drivespec参数必须是文件或文件夹的绝对路径,或者仅仅是驱动器字母,例如:“c:”或“c”
(2) 与文件夹有关的方法
与文件夹有关的方法如表5-6所示:
表5-6 与文件夹有关的方法及说明
方 法
说 明
BuildPath(path,name)
在已有的路径path上增添名字为name的文件或文件夹,如果需要,则增添路径分隔符’’
CopyFolder(source,destination,overwrite)
从指定的源文件夹source(可以包含通配符)中复制一个或多个文件夹到指定的目标文件夹destination,包含了源文件夹中的所有文件。如果source包含通配符或destination末尾是路径分隔符(‘’),那么认为destination是要放置源文件夹的拷贝的文件夹。否则的话,认为destination是要创建的新文件夹的路径名。如果destination文件夹已经存在且overwrite参数设置为False,将产生错误,缺省的overwrite参数是True
CreateFolder(foldername)
创建一个路径名为foldername的文件夹。如果foldername已经存在将产生错误
DeleteFolder(folderspec,force)
删除由folderspec指定的一个或多个文件夹(可以在路径的最后部分包含通过配符)及文件夹中的所有内容。如果可选的force参数设置为true,那么即使文件夹包含的文件具有只读属性,也将删除该文件夹。缺省的force参数是False
FolderExist(folderspec)
如果folderspec指定的文件夹存在则返回True,否则返回False。folderspec参数可以包含文件夹的绝对或相对路径,或者仅仅是当前文件夹中看到的文件夹名
GetAbsolutePathName(pathspec)
返回明确指定文件夹的路径,其中要考虑到当前文件夹的路径。例如,如果当前文件夹是“c:docssales”,而pathspec是“jan”,返回的字符是“c:docssalesjan”。通配符、”..”和”\”路径操作符都是可以接受的
GetFolder(folderspec)
返回folderspec指定的文件夹对应的Folder对象。folderspec可以是文件夹的相对的或绝对的路径
GetParentFolderName(pathspec)
返回pathspec文件或文件夹的上一级文件夹。不检验该文件夹是否存在
GetSpecialfolder(folderspec)
返回一个特定的Windows文件夹相对应的Folder对象。参数folderspec的允许值是WindowsFolder(0)、SystemFolder(1)和TemporaryFolder(2)
MoveFolder(source,destination)
将source指定的一个或多个文件夹移动到destination指定的文件夹。在source里可以包含通配符,但在destination中不行。如果source包含通配符或destination末尾是路径分隔符(‘’),则认为destination是要放置源文件夹的文件夹,否则认为它是一个新文件夹的完整路径和名字。如果目的文件夹destination已经存在则产生错误
(3) 与文件有关的方法
与文件有关的方法如表5-7所示:
方 法
说 明
CopyFile(source,destination,
overwrite)
将source(可包含通配符)指定的一个或多个文件复制到指定的目标文件夹destination。如果source包含通配符或destination末尾是路径分隔符(‘’),那么认为destination是文件夹。否则认为destination为一新文件的完全路径和名称。如果目标文件夹已经存在且overwrite参数设置为False,将产生错误。缺省的overwrite参数是True
CreateTextFile(filename,overwrite,
unicode)
用指定的文件名filename在磁盘上创建一个新的文本文件,并返回与其对应的TextStream对象,如果可选的overwrite参数设置为True,则覆盖同一路径下已有的同名文件。缺省的overwrite参数是False。如果可选的unicode参数设置为True,则该文件的内容将存储为Unicode文本,缺省的unicode参数是False
DeleFile(filespec,force)
删除由filespec指定的一个或多个文件(可以在路径的最后部分包含通配符)。如果可选的force参数设置为true,那么也删除具有只读属性的文件。缺省的force参数是False
FileExists(filespec)
如果filespec指定的文件存在则返回True,否则返回False。filespec参数可以包含文件的绝对路径或相对路径,或者是当前文件夹中的文件名
GetBaseName(filespec)
返回filespec指定的文件的名称,即包含文件路径但去掉了文件的扩展名
GetExtensionName(filespec)
返回filespec指定的文件的扩展名
GetFile(filespec)
返回filespec指定的文件所对应的File对象。可以指定文件的相对或绝对路径
GetFileName(pathspec)
返回pathspec指定的文件的路径或文件名,如果没有文件名就返回最后的文件夹名。不检查该文件或文件夹是否存在
GetTempName()
返回一个随机产生的文件名,用于完成运算所需的临时文件或文件夹
MoveFile(source,destination)
将source指定的一个或多个源文件移动到destination指定的目的文件夹。在source里可以包含通配符,但destination不行。如果source包含通过配符或destination末尾是路径分隔符(‘’),那么认为destination是一文件夹。否则,认为destination是一新文件夹的完整路径和名称。如果目的文件夹已经存在则产生错误
OpenTextFile(filename,iomode,create,
format)
创建一个名叫做filename的文件,或打开一个现有的名为filename的文件,并且返回一个与其相关的TextStream对象。filename参数可以包含绝对或相对路径。iomode参数指定了所要求的访问类型。允许的数值是ForReading(1)(缺省)、ForWriting(2)、ForAppending(8)。当写入或追加到一个不存在的文件时,如果create参数设置为true,就将创建一个新文件。缺省的create参数是False。format参数说明对文件读或写的数据格式。允许数值是:TristatetFalse(0)(缺省),按照ASCII格式打开;TristatetTrue(-1),按照Unicode格式打开;TristateDefault(-2),用系统缺省格式打开
Unicode文件使用两个字节标识每个字符,取消了ASCII字符最多256个的限制。
5.4.2 使用驱动器
下面是使用FileSystemObject对象的简单例子,它使用DriveExists方法得到现有的驱动器字母的列表:
‘ In VBScript
Set objFSO = Server.CreateObject(“Scripting.FileSystemObject”)
For intCode = 65 To 90 ‘ANSI codes for ‘A’ to ‘Z’
strLetter = Chr(intCode)
If objFSO.DriveExists(strLetter) Then
Response.Write “Found drive “ & strLetter & “:<BR>”
End If
Next
或用JScript:
// In Jscript
var objFSO = Server.CreateObject(‘Scripting.FileSystemObject’);
for (var intCode = 65; intCode <= 90; intCode ) { //ANSI codes for ‘A’ to ‘Z’
strLetter = String.formCharCode(intCode);
If (objFSO.DriveExists(strLetter))
Response.Write (‘Found drive ‘ strLetter “:<BR>”);
}
这两个程序段的运行结果是相同的,如图5-9所示:
这一页面为driveexists_vb.asp,由本书的示例文件提供。
1. Drive对象
正如已经看到的,FileSystemObject对象包含一个属性——Drives,它返回一个包括本地计算机上所有可用驱动器的集合。
Drives集合里的每个条目是一个Drive对象。Drive对象的属性如表5-8所示:
表5-8 Drive对象的属性及说明
属 性
说 明
AvailableSpave
考虑了帐户定额和/或其他限制,返回驱动器上对于该用户可用的空间的大小
DriveLetter
返回驱动器的字母
DriveType
返回驱动器的类型。返回值可以是Unknown(0)、Removeable(1)、Fixed(2)、Network(3)、CDRom(4)和RamDisk(5)。然而需要注意的是当前版本的scrrun.dll不支持预定义常数Network,必须使用十进制3来代替
FileSystem
返回驱动器文件系统的类型。返回值包括“FAT”、“NTFS”和“CDFS”
FreeSpace
返回驱动器上可用剩余空间的总量
IsReady
返回一个布尔值表明驱动器是否已准备好
Path
返回一个由驱动器字母和冒号组成的驱动器路径,即“C:”
RootFolder
返回代表的驱动器根目录文件夹的Folder对象
SerialNumber
返回一个用于识别磁盘卷的十进制的序列号
ShareName
如果是一个网络驱动器,返回该驱动器的网络共享名
TotalSize
返回驱动器的总容量(以字节为单位)
VolumeName
设定或返回本地驱动器卷名
因此,通过使用Drives集合里的Drive对象,可以在服务器上产生一个驱动器列表,与通过检查每个可能的驱动器字母来判别驱动器是否存在的方法相比,效率更高。我们也可以得到关于该驱动器的信息。在VBScript里,代码如下:
'; In vbscript:
'; create a FileSystemObject instance
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
'; create a Drives collection
Set colDrives = objFSO.Drives
'; iterate through the Drives collection
For Each objDrive in colDrives
Response.Write "DriveLetter: <B>" & objDrive.DriveLetter & "</B> "
Response.Write "DriveType: <B>" & objDrive.DriveType
Select Case objDrive.DriveType
Case 0: Response.Write " - (Unknown)"
Case 1: Response.Write " - (Removable)"
Case 2: Response.Write " - (Fixed)"
Case 3: Response.Write " - (Network)"
Case 4: Response.Write " - (CDRom)"
Case 5: Response.Write " - (RamDisk)"
End Select
Response.Write "</B> "
If objDrive.DriveType = 3 Then
If objDrive.IsReady Then
Response.Write "Remote drive with ShareName: <B>" & objDrive.ShareName & "</B>"
Else
Response.Write "Remote drive - <B>IsReady</B> property returned_
<B>False</B><BR>"
End If
Else If objDrive.IsReady then
Response.Write "FileSystem: <B>" & objDrive.FileSystem & "</B> "
Response.Write "SerialNumber: <B>" & objDrive.SerialNumber & "</B><BR>"
Response.Write "Local drive with VolumeName: <B>" & _
objDrive.VolumeName & "</B><BR>"
Response.Write "AvailableSpace: <B>" & FormatNumber( _
objDrive.AvailableSpace / 1024, 0) & "</B> KB "
Response.Write "FreeSpace: <B>" & FormatNumber( _
objDrive.FreeSpace / 1024, 0) & "</B> KB "
Response.Write "TotalSize: <B>" & FormatNumber(_
objDrive.TotalSize / 1024, 0) & "</B> KB"
End if
Response.Write "<P>"
End if
Next
注意,不能用预定义常数Network比较驱动器的DriveType属性,因为(至少在scrrun.dll的当前版本中)在类型库中省略了Network常数,因此不再作为公用的常数使用。
在JScript中,该程序是:
// In jscript:
// create a FileSystemObject instance
var objFSO = Server.CreateObject(';Scripting.FileSystemObject';);
// create a Drives collection
var colDrives = new Enumerator(objFSO.Drives);
for (; !colDrives.atEnd(); colDrives.moveNext()) {
objDrive = colDrives.item();
Response.Write(';DriveLetter: '; objDrive.DriveLetter ';<BR>';);
Response.Write(';DriveType: '; objDrive.DriveType ';<BR>';);
if (objDrive.DriveType == 3)
if (objDrive.IsReady)
Response.Write(';Remote drive with ShareName: ';
objDrive.ShareName ';<BR>';)
else
Response.Write(';Remote drive - IsReady property returned False<BR><BR>';);
else if (objDrive.IsReady) {
Response.Write(';Local drive with VolumeName: ';
objDrive.VolumeName ';<BR>';);
Response.Write(';FileSystem: '; objDrive.FileSystem ';<BR>';);
Response.Write(';SerialNumber: '; objDrive.SerialNumber ';<BR>';);
Response.Write(';AvailableSpace: '; objDrive.AvailableSpace '; bytes<BR>';);
Response.Write(';FreeSpace: '; objDrive.FreeSpace '; bytes<BR>';);
Response.Write(';TotalSize: '; objDrive.TotalSize '; bytes<P>';);
}
}
在系统上运行这段程序以前有一点要注意。如果在A驱动器里没有磁盘,或CD-ROM驱动器里没有光盘,将得到一个错误提示:“Disk Not Ready”。除了DriveLetter属性和DriveType属性外,在使用其他属性和方法前,通过检查每个驱动器的IsReady属性,可以保护该页面。
当在服务器上运行以上VBScript代码时,运行结果如图5-10所示。这一页面为drivescollection_vb.asp,来自本书提供的示例文件。
2. 文件系统定位
FileSystemObject的几个方法可用于得到其他对象的引用,因此可以在服务器的文件系统和任何网络驱动器中定位。事实上,在ASP代码里使用的所有对象或组件中,除了ActiveX Data Object组件,FileSystemObject对象很可能是最复杂的对象之一。
这种复杂性是由于对如何访问文件系统的不同部分,要求有极高的灵活性。例如,可以从FileSystemObject向下通过使用各种从属对象定位一个文件。其过程是从Drives集合开始,到一个Drive对象,再到驱动器的根Folder对象,然后到子Folder对象,再到文件夹的Files集合,最后到集合内的File对象。
另外,如果已知要访问的驱动器、文件夹或文件。可以直接对其使用GetDrive、GetFolder、GetSpecialFolder和GetFile方法。图5-11有助于理解所有文件系统定位相关的组件、对象、方法和属性之间的关系。
1. Folder对象
Driver对象的RootFolder属性返回一个Folder对象,通过该对象可访问这个驱动器内的所有的内容。可以使用这个Folder对象的属性和方法遍历驱动器上的目录,并得到该文件夹和其他文件夹的属性。
(1) Folder对象的属性
Folder对象提供一组属性,可用这些属性得到关于当前文件夹的更多信息,也可以改变该文件夹的名称。其属性及说明如表5-9所示:
表5-9 Folder 对象的属性及说明
属 性
说 明
Attributes
返回文件夹的属性。可以是下列值中的一个或其组合:Normal(0)、ReadOnly(1)、Hidden(2)、System(4)、Volume(名称)(8)、Directory(文件夹)(16)、Archive(32)、Alias(64)和Compressed(128)。例如,一个隐藏的只读文件,Attributes的值为3
DateCreated
返回该文件夹的创建日期和时间
DateLastAccessed
返回最后一次访问该文件夹的日期和时间
DateLastModified
返回最后一次修改该文件夹的日期和时间
Drive
返回该文件夹所在的驱动器的驱动器字母
Files
返回Folder对象包含的Files集合,表示该文件夹内所有的文件
IsRootFolder
返回一个布尔值说明该文件夹是否是当前驱动器的根文件夹
Name
设定或返回文件夹的名字
ParentFolder
返回该文件夹的父文件夹对应的Folder对象
Path
返回文件夹的绝对路径,使用相应的长文件名
ShortName
返回DOS风格的8.3形式的文件夹名
ShortPath
返回DOS风格的8.3形式的文件夹的绝对路径
Size
返回包含在该文件夹里所有文件和子文件夹的大小
SubFolers
返回该文件夹内包含的所有子文件夹对应的Folders集合,包括隐藏文件夹和系统文件夹
Type
如果可能,返回一个文件夹的说明字符串(例如,“Recycle Bin”)
(2) Folder对象的方法
Folder对象提供一组可用于复制、删除和移动当前文件夹的方法。这些方法的运行方式与FileSystemObject对象的CopyFolder、DeleFolder和MoveFolder方法相同,但这些方法不要求source参数,因为源文件就是这个文件夹。这些方法及说明如表5-10所示:
表5-10 Folder对象的方法及说明
方 法
说 明
Copy(destination,overwrite)
将这个文件夹及所有的内容复制到destination指定的文件夹。如果destination的末尾是路径分隔符(‘’),那么认为destination是放置拷贝文件夹的一个文件夹。否则认为destination是要创建的新文件夹的路径和名字。如果目标文件夹已经存在且overwrite参数设置为False,将产生错误,缺省的overwrite参数是True
Delete(force)
删除文件夹及里面的所有内容。如果可选的force参数设置为True,即使文件夹设置为只读或含有只读的文件,也将删除该文件夹。缺省的force是False
Move(destination)
将文件夹及里面所有的内容移动到destination指定的文件夹。如果destination的末尾是路径分隔符(‘’),那么认为destination是放置移动文件夹的一个文件夹。否则认为destination是一个新的文件夹的路径和名字。如果目标文件夹已经存在,则出错
CreateTextFile
(filename,overwrite,unicode)
用指定的文件名在文件夹内创建一个新的文本文件,并且返回一个相应的TextStream对象。如果可选的overwrite参数设置为True,将覆盖任何已有的同名文件。缺省的overwrite参数是False。如果可选的unicode参数设置为True,文件的内容将存储为unicode文本。缺省的unicode是False
在文件夹之间可以使用当前文件夹的ParentFolder属性,返回到父目录。当到达一个文件夹时,如果IsRootFolder属性是True,就停下来。离开驱动器的根目录,沿目录树向下,可遍历或访问在Folders集合(由当前文件夹的SubFolders属性返回)内的指定文件夹。
下列程序遍历了驱动器C根目录内的所有文件夹,并显示各个文件夹的有关信息。
VBScript程序如下:
';In vbscript:
'; Create a FileSystemObject instance
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
'; Get a reference to drive C
Set objDriveC = objFSO.GetDrive("C:")
'; Get a reference to the root folder
Set objRoot = objDriveC.RootFolder
'; Get a reference to the SubFolders collection
Set objFolders = objRoot.SubFolders
'; Get a reference to the first folder in the SubFolders collection
For Each objFolder In objFolders
Set objFolder1 = objFolders.Item((objFolder.Name))
Exit For
Next
'; Iterate through all the files in this folder
For Each objFile in objFolder1.Files
Response.Write "Name: " & objFile.Name & " "
Response.Write "ShortName: " & objFile.ShortName & " "
Response.Write "Size: " & objFile.Size & " bytes "
Response.Write "Type: " & objFile.Type & "<BR>"
Response.Write "Path: " & objFile.Path & " "
Response.Write "ShortPath: " & objFile.ShortPath & "<BR>"
Response.Write "Created: " & objFile.DateCreated & " "
Response.Write "LastModified: " & objFile.DateLastModified & "<P>"
Next
JScript程序如下:
//In jscript:
// Create a FileSystemObject instance
var objFSO = Server.CreateObject(';Scripting.FileSystemObject';);
// Get a reference to drive C
var objDriveC = objFSO.GetDrive(';C:';);
// Get a reference to the root folder
var objRoot = objDriveC.RootFolder;
// Get a reference to the first folder in the SubFolders collection
var colAllFolders = new Enumerator(objRoot.SubFolders);
var objFolder1 = colAllFolders.item();
// Get a reference to the Files collection for this folder
var colFiles = new Enumerator(objFolder1.Files);
// Iterate through all the files in this collection
for (; !colFiles.atEnd(); colFiles.moveNext()) {
objFile = colFiles.item()
Response.Write(';Name: '; objFile.Name '; ';);
Response.Write(';ShortName: '; objFile.ShortName '; ';);
Response.Write(';Size: '; objFile.Size '; bytes ';);
Response.Write(';Type: '; objFile.Type ';<BR>';);
Response.Write(';Path: '; objFile.Path '; ';);
Response.Write(';ShortPath: '; objFile.ShortPath ';<BR>';);
Response.Write(';Created: '; objFile.DateCreated '; ';);
Response.Write(';Accessed: '; objFile.DateLastAccessed '; ';);
Response.Write(';Modified: '; objFile.DateLastModified ';<P>';);
}
该VBScript程序在服务器上运行时的结果如图5-12所示。该页面为folderscollection_vb.asp,来自本书提供的示例文件。
(3) 使用特殊文件夹
GetSpecialFolder是FileSystemObject对象的方法之一,它返回计算机上三个“特殊文件夹”对应的Folder对象:
· WindowsFolder:%Windows%目录,缺省为WinNT(或Windows,在非NT/2000计算机上)目录。
· SystemFolder:%System%目录,缺省为WinNTSystem32(或WindowsSystem,在非NT/2000计算机上)目录。
· TemporaryFolder:%Temp%目录,缺省为WinNTTemp(或WindowsTemp,在非NT/2000计算机上)目录。
为得到对特殊文件夹的引用,我们提供相应的预定义常数作为GetSpecialFolder方法的参数:
'; In vbscript:
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetSpecialFolder(WindowsFolder)
Response.Write "GetSpecialFolder(WindowsFolder) returned:<BR>"
Response.Write "Path: " & objFolder.Path & "<BR>"
Response.Write "Type: " & objFolder.Type & "<P>"
Set objFolder = objFSO.GetSpecialFolder(SystemFolder)
Response.Write "GetSpecialFolder(SystemFolder) returned:<BR>"
Response.Write "Path: " & objFolder.Path & "<BR>"
Response.Write "Type: " & objFolder.Type & "<P>"
Set objFolder = objFSO.GetSpecialFolder(TemporaryFolder)
Response.Write "GetSpecialFolder(TemporaryFolder) returned:<BR>"
Response.Write "Path: " & objFolder.Path & "<BR>"
Response.Write "Type: " & objFolder.Type & "<P>"
或用JScript:
// In jscript:
var objFSO = Server.CreateObject(';Scripting.FileSystemObject';);
var objFolder = objFSO.GetSpecialFolder(WindowsFolder);
Response.Write(';GetSpecialFolder(WindowsFolder) returned - ';);
Response.Write(';Path: '; objFolder.Path '; ';);
Response.Write(';Type: '; objFolder.Type ';<BR>';);
var objFolder = objFSO.GetSpecialFolder(SystemFolder);
Response.Write(';GetSpecialFolder(SystemFolder) returned - ';);
Response.Write(';Path: '; objFolder.Path '; ';);
Response.Write(';Type: '; objFolder.Type ';<BR>';);
var objFolder = objFSO.GetSpecialFolder(TemporaryFolder);
Response.Write(';GetSpecialFolder(TemporaryFolder) returned - ';);
Response.Write(';Path: '; objFolder.Path '; ';);
Response.Write(';Type: '; objFolder.Type ';<BR>';);
该VBScript程序在服务器上运行时的结果如图5-13所示。该页面名为specialfolder_vb.asp,来自本书提供的示例文件。
2. File对象
File对象提供了对文件的属性的访问,通过它的方法能够对文件进行操作。每个Folder对象提供了一个Files集合,包含文件夹中文件对应的File对象。还可以直接地从FileSystemObject对象中通过使用GetFile方法得到一个File对象引用。
(1) File对象的属性
File对象有一系列的属性,类似于Folder对象的属性,如表5-11所示:
表5-11 File对象的属性及说明
属 性
说 明
Attributes
返回文件的属性。可以是下列值中的一个或其组合:Normal(0)、ReadOnly(1)、Hidden(2)、System(4)、Volume(名称)(9)、Directory(文件夹)(16)、Archive(32)、Alias(64)和Compressed(128)
DateCreated
返回该文件夹的创建日期和时间
DateLastAccessed
返回最后一次访问该文件的日期和时间
DateLastModified
返回最后一次修改该文件的日期和时间
Drive
返回该文件所在的驱动器的Drive对象
Name
设定或返回文件的名字
ParentFolder
返回该文件的父文件夹的Folder对象
Path
返回文件的绝对路径,可使用长文件名
ShortName
返回DOS风格的8.3形式的文件名
ShortPath
返回DOS风格的8.3形式的文件绝对路径
Size
返回该文件的大小(字节)
Type
如果可能,返回一个文件类型的说明字符串(例如:“Text Document”表示.txt文件)
(2) File对象的方法
同样类似于Folder对象,File对象的方法允许复制、删除以及移动文件。它也有一个使用文本流打开文件的方法。File对象的方法及说明如表5-12所示:
表5-12 File对象的方法及说明
方 法
说 明
Copy(destination,overwrite)
将这个文件复制到destination指定的文件夹。如果destination的末尾是路径分隔符(‘’),那么认为destination是放置拷贝文件的文件夹。否则认为destination是要创建的新文件的路径和名字。如果目标文件已经存在且overwrite参数设置为False,将产生错误,缺省的overwrite参数是True
Delete(force)
删除这个文件。如果可选的force参数设置为True,即使文件具有只读属性也会被删除。缺省的force是False
Move(destination)
将文件移动到destination指定的文件夹。如果destination的末尾是路径分隔符(‘’),那么认为destination是一文件夹。否则认为destination是一个新的文件的路径和名字。如果目标文件夹已经存在,则出错
CreateTextFile
(filename,overwrite,unicode)
用指定的文件名创建一个新的文本文件,并且返回一个相应的TextStream对象。如果可选的overwrite参数设置为True,将覆盖任何已有的同名文件。缺省的overwrite参数是False。如果可选的unicode参数设置为True,文件的内容将存储为unicode文本。缺省的unicode是False
OpenAsTextStream
(iomode,format)
打开指定文件并且返回一个TextStream对象,用于文件的读、写或追加。iomode参数指定了要求的访问类型,允许值是ForReading(1) (缺省值)、ForWrite(2)、ForAppending(8)。format参数说明了读、写文件的数据格式。允许值是TristateFalse(0)(缺省),说明用ASCII数据格式;TristateTrue(-1)说明用Unicode数据格式;TristateUseDefault(-2)说明使用系统缺省格式
因此给定一个File对象后,可以使用ParentFolder属性得到包含该文件的Folder对象的引用,用来在文件系统中导航。甚至可以用Drive属性获得相应的Drive对象的引用,并得到各种Folder对象以及所包含的File对象。
另外,给定一个Folder对象以及对应的Files集合后,可以通过遍历该集合检查这一文件夹中的每个文件。还可以使用File对象的各种方法以一定方式处理该文件,如复制、移动或删除。下面的代码给出了C驱动器的第一个文件夹的文件列表:
'; In vbscript:
'; Create a FileSystemObject instance
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
'; Get a reference to drive C
Set objDriveC = objFSO.GetDrive("C:")
'; Get a reference to the root folder
Set objRoot = objDriveC.RootFolder
'; Get a reference to the SubFolders collection
Set objFolders = objRoot.SubFolders
'; Get a reference to the first folder in the SubFolders collection
For Each objFolder In objFolders
Set objFolder1 = objFolders.Item((objFolder.Name))
Exit For
Next
'; Iterate through all the files in this folder
For Each objFile in objFolder1.Files
Response.Write "Name: " & objFile.Name & " "
Response.Write "ShortName: " & objFile.ShortName & " "
Response.Write "Size: " & objFile.Size & " bytes "
Response.Write "Type: " & objFile.Type & "<BR>"
Response.Write "Path: " & objFile.Path & " "
Response.Write "ShortPath: " & objFile.ShortPath & "<BR>"
Response.Write "Created: " & objFile.DateCreated & " "
Response.Write "LastModified: " & objFile.DateLastModified & "<P>"
Next
注意,不能使用数字索引来定位Folders或Files集合里的条目,因此必须使用For Each … Next语句遍历该集合直到最初的条目,然后使用该条目的Name属性。也不得不使用嵌套的圆括号强迫其作为值(字符串)传送给该Folders集合的Item方法。
用下面的JScript程序可完成同样的工作:
// In jscript:
// Create a FileSystemObject instance
var objFSO = Server.CreateObject(';Scripting.FileSystemObject';);
// Get a reference to drive C
var objDriveC = objFSO.GetDrive(';C:';);
// Get a reference to the root folder
var objRoot = objDriveC.RootFolder;
// Get a reference to the first folder in the SubFolders collection
var colAllFolders = new Enumerator(objRoot.SubFolders);
var objFolder1 = colAllFolders.item();
// Get a reference to the Files collection for this folder
var colFiles = new Enumerator(objFolder1.Files);
// Iterate through all the files in this collection
for (; !colFiles.atEnd(); colFiles.moveNext()) {
objFile = colFiles.item()
Response.Write(';Name: '; objFile.Name '; ';);
Response.Write(';ShortName: '; objFile.ShortName '; ';);
Response.Write(';Size: '; objFile.Size '; bytes ';);
Response.Write(';Type: '; objFile.Type ';<BR>';);
Response.Write(';Path: '; objFile.Path '; ';);
Response.Write(';ShortPath: '; objFile.ShortPath ';<BR>';);
Response.Write(';Created: '; objFile.DateCreated '; ';);
Response.Write(';Accessed: '; objFile.DateLastAccessed '; ';);
Response.Write(';Modified: '; objFile.DateLastModified ';<P>';);
}
两个程序的结果是相同的,如图5-14所示。该页面为filescollection_vb.asp,来自本书提供的示例文件。
在C驱动器上的第一个文件夹可能是CAConfig,缺省情况下该文件夹是空的。这种情况下,可先向该文件夹复制一些文件,完成实验以后再删除这些文件。
Scripting.TextStream对象
FileSystemObject、Folder和File对象的一些方法都与通过TextStream对象创建、读取或写入文件有关。
虽然TextStream对象定义为FileSystemObject对象的一个独立的附属对象,但我们不得不使用FileSystemObject对象或其附属对象来创建一个TextStream对象并访问磁盘文件的内容。
5.5.1 创建TextStream对象的方法
有三个常用方法用于创建或打开一个文本文件并返回TextStram对象,如表5-13所示:
表5-13 创建TextStream对象的方法及说明
方 法
说 明
CreateTextFile
(filename,overwrite,unicode)
在磁盘上用指定的文件名filename创建一个新文本文件,并返回一个与该文件对应的TextStream对象。如果可选的overwrite参数设置为True,将覆盖具有同样路径的同名文件。缺省的overwrite是False。如果可选的unicode参数设置为False,该文件的内容将存储为Unicode格式。缺省的unicode是False
OpenTextFile
(filename,iomode,create,format)
打开或创建(如果不存在)一个名为filename的文件,并且返回与该文件对应的TextStream对象。filename参数可以包含绝对或相对路径。iomode参数说明需要的访问类型。容许值是ForReading(1)(缺省)、ForWriting(2)、ForAppending(8)。写入或追加到一个不存在的文件时,如果create参数设置为True,将创建一个新文件。缺省的create是False。format参数说明了读或写文件时的数据格式。容许值是TristateFalse(0)(缺省),说明用ASCII数据格式;TristateTrue(-1)说明用Unicode数据格式;TristateUseDefault(-2)说明数据使用系统缺省的格式
OpenAsTextStream
(iomode,format)
打开一个指定的文件并且返回一个TextStream对象,可用于对该文件的读、写或追加。iomode参数说明了需要的访问类型。容许值是ForReading(1)(缺省)、ForWriting(2)、ForAppending(8)。Format参数说明了读写文件的数据格式。容许值是TristateFalse(0)(缺省),说明用ASCII数据格式;TristateTrue(-1)说明用Unicode数据格式;TristateUseDefault(-2)说明使用系统缺省的格式
上面列出的方法在FileSystemObject、Folder和File对象中的实现有所不同。如表5-14所示:
表5-14 三个对象中包含的方法
方 法
FileSystemObject对象
Folder对象
File对象
CreateTextFile
有
有
有
OpenTextFile
有
无
无
OpenAsTextStream
无
无
有
因此,可以使用这些方法创建一个新的文本文件,或者打开一个已存在的文件。则可得到与该文件相应的一个TextStream对象,可以使用TextStream对象的属性和方法操作文件。
1. 创建新的文本文件
可以用CreateTextFile方法创建新的文本文件,或覆盖一个已存在的文件。返回的TextStream对象可用来读写文件。
首先创建一个FileSystemObject对象,用来创建TextStream对象。下面这个例子是用VBScript创建一个“普通的”(即非Unicode)名为MyFile.txt的文件,并覆盖已存在的同名文件:
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
Set objTStream = objFSO.CreateTextFile("C:TextFilesMyFile.txt", True, False)
这同样可用JScript实现:
var objFSO = Server.CreateObject(';Scripting.FileSystemObject';);
var objTStream = objFSO.CreateTextFile(';C:TextFilesMyFile.txt';, true, false);
一旦创建了该文件,就可以使用objTStream(它是对一个TextStream对象的引用)对文件进行操作。
2. 打开已存在的文本文件
OpenTextFile方法用于打开一个已有的文本文件。它返回一个TextStream对象,可用这个对象对文件读或追加数据。
同样,首先创建一个FileSystemObject对象,然后用其创建一个TextStream对象。下面的VBScript程序例子打开一个名为MyFile.txt的文件,准备读出其内容:
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
Set objTStream = objFSO.OpenTextFile("C:TextFilesMyFile.txt", ForReading)
用JScript:
var objFSO = Server.CreateObject(';Scripting.FileSystemObject';);
var objTStream = objFSO.OpenTextFile(';C:TextFilesMyFile.txt';, ForReading);
为了写入一个文件或创建一个不存在的文件,可以用以下代码:
'; In vbscript:
Set objTStream = objFSO.OpenTextFile("C:TextFilesMyFile.txt", ForWriting, True)
// In jscript:
var objTStream = objFSO.OpenTextFile(';C:TextFilesMyFile.txt';, ForWriting, true);
如果要打开一个已有的Unicode文件,准备对其追加数据,但是不创建不存在的文件,可以用:
'; In vbscript:
Set objTStream = objFSO.OpenTextFile("C:TextFilesMyFile.txt", ForReading, _
False, TristateTrue)
// In jscript:
var objTStream = objFSO.OpenTextFile(';C:TextFilesMyFile.txt';, ForReading, _
fasle, TristateTrue);
3. 作为一个TextStream对象打开一个File对象
可用File对象的OpenAsTextStream方法打开与该对象相应的文件,并且返回一个能对该文件进行读、写和追加的TextStream对象。所以,给定一个File对象(这种情况下不是FileSystemObject对象)——objFileObject,可作为一个“普通的”(非Unicode)TextStream对象打开它,以供追加文件内容:
'; In vbscript:
Set objTStream = objFileObject.OpenAsTextStream(ForAppending, False)
// In jscript:
var objTStream = objFileObject.OpenTextFile(ForAppending, false);
注意,使用这种方法不需要文件名,因为程序的执行通过引用File对象进行,并且没有create参数,因为该文件必须已存在,如果想从一个新的空的文件开始,可以用:
'; In vbscript:
Set objTStream = objFileObject.OpenAsTextStream(ForWriting)
// In jscript:
var objTStream = objFileObject.OpenTextFile(ForWriting);
如果想读取该文件:
'; In vbscript:
Set objTStream = objFileObject.OpenAsTextStream(ForReading)
// In jscript:
var objTStream = objFileObject.OpenTextFile(ForReading);
5.5.2 TextStream对象成员概要
表5-15和表5-16是TextStream对象的全部属性和方法的列表。下面将简短介绍各个重要的成员的细节。
1. TextStream对象的属性
TextStream的属性提供有关文件内文件指针当前位置的信息,如表5-15所示。注意,所有的属性是只读的。
表5-15 TextStream对象的属性及说明
属 性
说 明
AtEndOfLine
如果文件位置指针在文件中一行的末尾则返回True
AtEndOfStream
如果文件位置指针在文件的末尾则返回True
Column
从1开始返回文件中当前字符的列号
Line
从1开始返回文件中当前行的行号
AtEndOfLine和AtEndOfStream属性仅对以iomode参数为ForReading的方式打开的文件可用,否则将会出错。
2. TextStream对象的方法
TextStream对象的方法如表5-16所示:
表5-16 TextStream对象的方法及说明
方 法
说 明
Close()
关闭一个打开的文件
Read(numchars)
从文件中读出numchars个字符
ReadAll()
作为单个字符串读出整个文件
ReadLine()
作为一个字符串从文件中读出一行(直到回车符和换行)
Skip(numchars)
当从文件读出时忽略numchars个字符
SkipLine()
当从文件读出时忽略下一行
Write(string)
向文件写入字符串string
WriteLine(string)
向文件写入字符串string(可选)和换行符
WriteBlankLines(n)
向文件写入n个换行符
3. 写文本文件
一旦使用CreateTextFile、OpenTextFile或OpenAsTextStream方法以及ForWriting或ForAppending参数,创建一个对应于某个文件的TextStream对象,可以用下面的VBScript程序写文件和关闭文件:
'; In vbscript:
objTStream.WriteLine "At last I can create files with VBScript!"
objTStream.WriteLine
objTStream.WriteLine "Here are three blank lines:"
objTStream.WriteBlankLines 3
objTStream.Write "... and this is "
objTStream.WriteLine "the last line."
objTStream.Close
或者用JScript:
// In jscript:
objTStream.WriteLine(';At last I can create files with JScript! ';);
objTStream.WriteLine();
objTStream.WriteLine(';Here are three blank lines: ';);
objTStream.WriteBlankLines(3);
objTStream.Write(';... and this is ';);
objTStream.WriteLine(';the last line. ';);
objTStream.Close();
4. 读文本文件
一旦使用CreateTextFile、OpenTextFile或OpenAsTextStream方法以及ForReading参数,创建一个对应于某个文件的TextStream对象,可以用下面的VBScript程序读文件和关闭文件:
'; In vbscript:
';read one line at a time until the end of the file is reached
Do While Not objTStream.AtEndOfStream
';get the line number
intLineNum = objTStream.Line
';format it as a 4-character string with leading zeros
strLineNum = Right("000" & CStr(intLineNum), 4)
';get the text of the line from the file
strLineText = objTStream.ReadLine
Response.Write strLineNum & ": " & strLineText & "<BR>"
Loop
objTStream.Close
或用JScript:
// In jscript:
// read one line at a time until the end of the file is reached
while (! objTStream.AtEndOfStream) {
// get the line number
intLineNum = objTStream.Line;
// format and convert to a string
strLineNum = ';000'; intLineNum.toString();
strLineNum = substr(strLineNum, strLineNum.length – 4, 4)
// get the text of the line from the file
strLineText = objTStream.ReadLine();
Response.Write(strLineNum ';: '; strLineText ';<BR>';);
}
objTStream.Close();
5.5.3 TextStream对象举例
为了了解使用TextStream对象操作磨灭文件的几种方式,本书提供了一个VBScript示例页面,该页使用了大量的上述的代码。从示例的Chapter05主菜单页,选择链接“Working With the TextStream Object”打开show_textstream.asp页面。
该页显示了存储在磁盘上名为MyFile.txt的文件的文本内容。在<TEXTAREA>控件里显示的文本内容允许进行编辑,并且下面有三个按钮。三个按钮的作用分别是用<TEXTAREA>控件的内容更新(即取代)最初的文本,在已有文件内容的后面添加文本,或用初始的缺省内容重写文件,如图5-15所示:
1. 读取已存在的文本文件的内容
每次载入该页面,将打开文本文件并将读取的内容置入<TEXTAREA>控件。注意我们如何使用Server.MapPath方法得到文件MyFile.txt的绝对物理路径,该文件与示例页面在同一目录下。下面的程序创建FileSystemObject实例:
<%
strTextFile = Server.MapPath("MyFile.txt")
';create an instance of a FileSytemObject object
Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
…
与书中本部分的大多数其他示例一样,该页包含一个<FORM>段以保存该页面的HTML控件。ACTION是当前页面,因此窗体的内容送回到同一页面。
每次载入该页面,<TEXTAREA>控件用文本文件的当前内容填充:
…
<FORM ACTION="<% = Request.ServerVariables("SCRIPT_NAME") %>" METHOD="POST">
The contents of the disk file <B><% = strTextFile %></B> are:<P>
<TEXTAREA NAME="txtContent" ROWS="10" COLS="50" WRAP="PHYSICAL">
<%
';open the text file as a TextStream object
Set objTStream = objFSO.OpenTextFile(strTextFile, ForReading)
';read one line at a time until the end of the file is reached
Do While Not objTStream.AtEndOfStream
';get the line number
intLineNum = objTStream.Line
';format and convert to a string
strLineNum = Right("00" & CStr(intLineNum), 3)
';get the text of the line from the file
strLineText = objTStream.ReadLine
Response.Write strLineNum & ": " & strLineText & vbCrLf
Loop
objTStream.Close
%>
</TEXTAREA><P>
由上面程序可知道如何打开文本文件进行读取,遍历整个文件每次读取一行(而不是作为一个字符串读出整个文件)。这是因为要添加自己的行号,行号不属于该文件的文本。对从该文件读出的每行(读之前),检索并且格式化Line属性并创建一个三位数字的行号。然后把行编号和文本行放置页面的<TEXTAREA>控件内。
2. 更新文本文件的内容
当点击页面的Update按钮时(一般是在编辑了<TEXTAREA>控件里的文本以后),将把<TEXTAREA>控件里的内容重新写入到该文本文件内。为此,该页有相应的一些ASP代码,在创建HTML控件以前检验Request.Form集合,查看点击的是哪一个按钮(如果有的话),然后就重新载入该页。
如果点击Update按钮,搜集<TEXTAREA>控件的内容作为一个字符串,分离这个字符串使之成为独立文本选择的数组,并且打开文本文件准备重写其内容,然后遍历刚刚创建的数组,按行号循环写入该行的内容:
…
';look for a command sent from the FORM section buttons
If Len(Request.Form("cmdUpdate")) Then
';Get contents of TEXTAREA control
strNewText = Request.Form("txtContent")
';Split it into an array of lines at each carriage return
arrLines = Split(strNewText, vbCrLf)
';Open the text file for writing, which replaces all existing content
Set objTStream = objFSO.OpenTextFile(strTextFile, ForWriting)
For intLine = 0 To UBound(arrLines)
strThisLine = arrLines(intLine)
';Write out each line in turn as long as it’s got a line number
If Len(strThisLine) > 4 Then objTStream.WriteLine Mid(strThisLine, 6)
Next
objTStream.Close
End If
…
HTML<TEXTAREA>控件可在返回的Value中增加额外字符,这依赖于原始HTML页内的内容格式和WRAP属性的设置。特别是应在ASP脚本结束定界符“%>”后立即写上</TEXTAREA>标记,以防止增加一个额外的回车符号。即使用:
%></TEXTAREA><p>
而不使用:
%>
</TEXTAREA><P>
3. 向文本文件追加内容
当点击Append按钮时,可对已有的文件追加内容,与修改该文件内容类似,如图5-16所示。区别是打开该文件是为了追加而不是为了改写文件。调用OpenTextFile方法时可增加额外参数,防止在指定的文件2不存在时创建新文件。
…
If Len(Request.Form("cmdAppend")) Then
';append contents of TEXTAREA to file
strNewText = Request.Form("txtContent")
arrLines = Split(strNewText, vbCrLf)
Set objTStream = objFSO.OpenTextFile(strTextFile, ForAppending, False)
For intLine = 0 To UBound(arrLines)
strThisLine = arrLines(intLine)
If Len(strThisLine) > 4 Then objTStream.WriteLine Mid(strThisLine, 6)
Next
objTStream.Close
End If
…
4. 重写缺省内容
最后,Restore按钮用来将初始缺省内容简单地重写回文本文件。代码与用TextStream的方法写一个文本文件类似:
…
If Len(Request.Form("cmdDefault")) Then
';write out default contents to file
Set objTStream = objFSO.CreateTextFile(strTextFile, True, False)
objTStream.WriteLine "At last I can create files with VBScript!"
objTStream.WriteLine
objTStream.WriteLine "Here are three blank lines:"
objTStream.WriteBlankLines 3
objTStream.Write "... and this is "
objTStream.WriteLine "the last line."
objTStream.Close
End If
5.6 小结
本章讲述了在ASP页面中使用对象和组件的强大能力。首先讨论对象和组件的一般特性,以及它们的类型。然后集中论述了如何在ASP(及客户端)脚本代码内创建对象实例。
在页面上使用的许多对象可能都是“外部”组件,这些组件安装在服务器上,独立于ASP。本章所讨论的对象,当ASP使用一种缺省的脚本语言(如VBScript或JScript)时,总是可用的。其实现是通过scrrun.dll文件里的脚本运行期库完成的。
这些对象是指Dictonary对象、FileSystemObject对象和TextStream对象。
Dictionary对象为我们提供了存储值的一种有效方式,可根据名字进行索引和访问,而不是根据一个数字进行访问。这是存储名字/值对这样的数据的理想方式。
FileSystemObject对象和TextStream对象相互之间联系密切,可使用它们访问服务器或网络(映射)的磁盘驱动器的目录。FileSystemObject对象提供对驱动器、文件夹(目录)和文件的存取,并提供了用于对于获得更多的信息或移动、复制、删除它们的属性及方法。
可以创建对应于系统内的任何文件的TextStream对象,并通过该对象对文件进行读取和写入。对于读写过程它作为文本文件操作,甚至可以处理Unicode格式的文件。对文件系统的导航和读写能力的结合允许对服务器文件系统进行极其复杂的控制。还可以在客户端脚本代码中使用对象(有一定限制)。
脚本对象与服务器组件的比较
在前几章中,我们已经研究了两类不同的对象:一类是ASP对象模型的内置部分,另一类是脚本运行期库提供的对象。本章将介绍第三类能用于ASP的对象,取名为活动服务器组件(或者就称为服务器组件)。
这些组件在很多场合下就如同在浏览器或在像Visual Basic编程环境中用于客户端脚本中的传统ActiveX控件一样,但是这些组件可在服务器上运行,而不是一个只能运行在客户机上的对象。
现在的问题是:这些组件来自哪里?其中一部分是由常规的ASP/IIS安装程序提供的,而另一部分可以从Web上免费或有偿获得。本章讨论ASP的各种组件的主要用途,然后介绍可得到的其他组件的几个范例。一旦感觉到使用这些活动服务器组件得心应手,你就会毫不费力将其他供应商产品嵌入到自己的ASP页面中。本章后面有一个第三方组件的列表,本书后面还将介绍如何创建自己的组件。
还有一些用于ASP的组件,但本章不准备讨论。动态Web网站技术的基本用途之一是发布直接从数据库管理系统和其他类型的数据存储获得的信息,这一用途和从数据库或其他数据存储中收集数据和存入数据的需求,促使程序员开发服务器端编程。为了使用ASP完成这些任务,我们可以充分利用称为ActiveX数据对象(ActiveX Data Objects,ADO)库的组件集。由于它涉及的内容很多,很难在本章中阐述清楚,还要考虑的其他的组件,本书从第8章开始讲述相关的内容。
本章要讨论的内容有:
· 服务器组件与在ASP中使用的其他对象的区别。
· 简要说明如何实例化对象,并得到服务器组件的引用。
· ASP 3.0和IIS提供的可安装组件,例如AD Rotator、Content Linking、Page Counter和Tools等组件。
· 一些第三方提供的组件。
6.1 脚本对象与服务器组件的比较
前一章已说明了各种脚本对象,这些对象对于ASP/IIS来说都是从脚本运行库(scrrun.dll)中得到的,重要的是不要把这些对象与服务器组件(即本章的主题)混淆一起。
服务器组件通常在其自己的DLL或可执行文件中实现,例如本章将要讨论的“Content Linking”组件,就是由nextlink.dll实现的。一旦在服务器安装和注册这个DLL文件,那么其提供的对象就可以在ASP所支持的任何脚本语言中使用。组件与系统环境的关系如图6-1所示:
使用组件服务器组件
前一章已经介绍了对象和组件在页面中通用的实例化方法,使用服务器组件和使用脚本运行期库对象的方法完全一致,唯一的区别是,不像脚本对象,服务器组件通常作为单独的DLL文件实现,而且可能要求在使用前先运行一个安装程序,或者手工注册该组件(如果这些组件在ASP缺省安装时未注册)。
1. CreateObject方法
一般说来,可以在一个ASP页面中使用ASP内置的Server对象的CreateObject方法创建一个对象,例如:
Set objThis = Server.CreateObject("this.object") '; in VBScript
或:
var objThis = Server.CreateObject(';this.object';); // in JScript
这样就在变量objThis中创建了该对象的引用,然后可以在脚本中使用该对象;换句话说,可以在代码中根据需要控制其属性及调用其方法。
2. 使用<OBJECT>元素
同样可以使用常规的<OBJECT>元素创建一个对象该方法同样适用于在Web浏览器页面中创建对象。ASP支持HTML<OBJECT>元素的特殊实现,而且我们可以使用这一方法在页面中放置一个对象。要在普通的.asp文件中定义一个组件或脚本对象的实例,使用下面语句:
<OBJECT RUNAT="SERVER" SCOPE="PAGE" ID="objThis"
CLASSID="CLSID:OACE4881-8305-11CF-9427-444553540000">
</OBJECT>
我们使用SCOPE属性来设定对象的范围,选项是“SESSION”、“APPLICATION”和“PAGE”。如果在普通的.asp文件中使用<OBJECT>元素,那么必须同时使用“PAGE”属性,并忽略SCOPE属性,因为创建的对象只能在它被实例化的页面中使用。如果在global.asa文件中使用<OBJECT>元素,则所创建的对象可在当前会话或整个应用程序中使用,因此SCOPE属性可设置为“SESSION”和“APPLICATION”。
3. 检测对象实例的存在
经常出现的错误是企图创建一个未安装(或未注册)的对象的实例,或者在CreateObject方法或<OBJECT>元素中使用错误的对象ProgID。缺省状态下,ASP会出现错误并停止页面的执行。
给页面添加一些程序代码,在访问对象之前进行检测是必要的。在VBScript中,可以做到这一点,方法是:关闭缺省的错误处理,然后使用IsObject函数查看对象是否真的引用了一个对象:
'; in vbscript:
…
On Error Resume Next ';turn off default error handling
Set objThis = Server.CreateObject("this.object")
If IsObject(objThis) Then
';the CreateObject method succeeded
On Error Goto 0 ';turn the default error handling back on
…
';rest of the script goes here
…
Else
Response.Write "Sorry, this page cannot be accessed at present"
Response.Flush
Response.End
End If
…
在JScript语言中,可以使用JScript 5.0脚本引擎中新的错误处理特性,这在第1章中介绍过。
// in jscript:
…
try {
var objThis = Server.CreateObject(';this.object';);
…
// rest of the script goes here
}
catch (exception) {
Response.Write (';Sorry, this page cannot be accessed at present';);
Response.Flush();
Response.End();
}
…
好多啊,一时也看不完,先顶一下!
天涯大哥,你能不能打一个包啊?看的我眼都花了!