前面的章节中已经多次使用过web应用程序(web application)术语,所指的既不是一个真正意义上的web网站,又不是一个传统的应用程序。换句话说,而是认为它是一些web网页和用来完成某些任务的其他资源的一个集合。它隐含这样一层意思:有一个预定义的路线贯穿于网页之中,用户可做出选择或提供信息使任务能够完成。 例如,一个在线商店,你为了购买货物,进行反复的观察和选择,浏览一系列网页,收集所需要的信息,支付相应的费用,最后发出定单。也可能是一个“软件升级向导”,指导用户完成下载和安装新软件的过程,或者可能是一个基于intranet的报价单或销售报告的生成工具。 所有这些不同于“标准”的web网站,一般的web站点使用一系列菜单或导航栏以预先未定义的路径漫游该站点。但是一个web应用程序远不只是受控制的导航器。自由地漫游于一个web网站时,可以进行无状态的和匿名的访问,但web应用程序一般不接受。 3.2.1 asp应用程序的定义 上述内容可以认为是术语“web应用程序”的一个合理的一般定义,但遗憾的是,在谈论有关“asp应用”时,仅这些还不够。回答什么是“web应用”可以是主观的,而回答什么是“asp应用”则需要从技术上的解释。在asp中术语“应用程序”有自己特定的含义,在讨论如何实现之前,弄懂这个概念至关重要。 asp应用程序与两个主要的内容有关: · 全局范围的规定,具有一个全局可访问的变量存储区域。 · 通过com+与iis的集成,可更好地管理组件。 下面讨论这些内容。第二个内容涉及到其他asp对象,其覆盖范围相应广泛。在下一章研究asp server对象时,将对这部分内容进行详细地讨论。 1. 提供web应用程序的全局范围 asp提供一个application对象,基本上与前面讨论的session对象相当。但是,这是在应用程序层而不是在用户层。换句话说,该对象是全局的,不是对单独用户的,而是对应用程序的所有用户,其作用域不限制为单独用户的访问。这与在一个正常的可执行应用程序中的全局(或public)变量相同。application对象可用于在全局环境中存储变量和信息(即状态),该应用程序内的任何asp网页中运行的脚本都可访问这些值,而不管是哪个访问者发出的请求。 但是,这没有回答主要问题:什么是一个asp应用程序?为此,需要研究asp内部的一些情况。 当用户请求一个asp网页时,iis通过实例化asp.dll(用来实现asp)创建一个环境(如第1章所述)。将该页面解释为服务器端脚本,相应的脚本引擎的实例用来执行该脚本。 实例化的asp.dll初如事件启动一个asp应用程序,创建一个application对象。然后,为这个用户启动一个会话,并创建单独的session对象。当更多的会话启动时,这个application对象保留在作用域中(即已经实例化和可用)。一旦最后保持活动的会话结束,该应用程序就结束,并取消相应的application对象。 (1) 缺省的asp应用 windows 2000在安装iis和asp时,创建一个缺省的web站点。它被配置成一个asp应用程序,涉及到在properties对话框中针对站点根目录文件夹(缺省为c:inetpubwwwroot)的一些设置。图3-2所示是缺省web站点的properties对话框的屏幕。 涉及到asp应用程序的文件之一是global.asa。这个文件用于定制应用程序行为的方式。放置在应用程序的根目录下,可用于该目录下的所有子目录。因此,如果它放置在整个web站点的根目录下,则定义整个网站作为缺省的asp应用程序的一部分。 在本章后面有关应用程序和会话事件的部分中,将看到这个文件及其使用方法。 (2) asp虚拟应用程序 如同在设置过程中创建缺省的应用程序一样,可以在该web网站的任何子目录中创建属于自己的asp虚拟应用程序。这个应用程序包含作为“应用程序目录”而定义的目录中所有的子目录。并且,这个目录和子文件夹也都是缺省应用程序的一部分,共享由缺省的application对象创建的全局空间。 事实上,在缺省的应用程序中存储的所有变量在子目录中的应用程序中也都是可用的。然而,如果该子目录应用程序中的一个asp网页把一个值写入application对象,而application对象与缺省(根)应用程序中已存在的一个值有相同的名字,那么,原先的值在子目录应用程序中就不能再用。但是在其他的应用程序或asp网页中,将保留原有的值,因为根目录的应用程序不能访问子目录应用中的值。 从一个子程序或函数的变量的角度考虑这个问题。如果定义一个变量intmyvalue为public或全局的变量,可以从任何的子程序或函数内部访问该变量。但是,如果又声明一个具有相同名字的局部变量,并在该子程序或函数内对该变量进行引用,则得到此变量的局部值。不能再访问原先的值。当子程序或函数结束,局部值被撤消,全局变量原有的值仍然保留着: public intmyvalue = 42 function dosomething() response.write intmyvalue ‘gives 42 from global variable dim intmyvalue intmyvalue = 17 response.write intmyvalue ‘gives 17 from new local variable,but ‘the global value of myvalue is still 42 end function (3) 创建自己的asp虚拟应用程序 为了建立一个新的虚拟应用程序,使用internet services manager应用程序或具有相同功能的html web manager网页。在internet services manager中,在要创建的虚拟应用程序的目录上单击右键,并选择new,接着选择virtual directory,屏幕如图3-3所示: 这个操作启动new virtual directory wizard,该向导的开始屏幕提供有关向导的操作信息。单击next,在第二页中键入新的虚拟应用程序的名字(或别名)。这个名字与internet services manager中选择的目录的路径联合起来,将成为该应用程序的url。屏幕如图3-4所示: 为了把一个现有的目录转换为与该目录具有相同名字的一个应用程序,选择包含想要转换的目录,并在向导的virtual directory alias页中使用该目录名。例如把已有的test目录转换为一个虚拟应用程序,应该在internet services manager中选择default web site条目,并提供一个别名“test”。 再单击next,指定包含该应用程序的内容(页面)的路径。单击browser选择一个已有目录。这个目录是新的虚拟应用指向的目录。屏幕如图3-5所示: 单击next打开access permissions页,选择给予这个应用程序的所有用户的权限。缺省值是read和run scripts,对大多数用户而言是适合的。屏幕如图3-6所示: 如果想编写用户可直接执行的、定制的编译的cgi应用程序,只选择“execute”:例如,用户通过在请求的url中指定相应名字的方法执行的一个.exe文件,像“http://mysite.com/.../test application/create_user.exe?user=jjones”。 单击next,向导创建虚拟应用程序。在图3-7所示的屏幕中,可在左边的列表栏中看到带有一个包含一些填充物的打开的小盒子图标。 如果现在用右键单击新的应用程序并选择properties,可以看到向导已经选择的设置。在这里可根据需要修改访问权限、“local path”和“application settings”。同时会看到一个remove按钮,可以用来删除该虚拟应用程序,如图3-8所示: (4) 删除虚拟应用程序 单击“remove”按钮不会真正地删除internet services manager中的该条目。而是把现有的虚拟应用程序转换为一个虚拟目录。这个目录有一个带有蓝色球的“文件夹”图标,该图标表示这并不是web网站中一个真正的目录,而是对磁盘上另一个文件夹的一个重定向。对它进行的访问方式与创建它的虚拟应用程序相同(即使用相同的url),但是不作为一个应用程序。换句话说,它不支持自己的application对象,而是继承缺省web网站的application对象,或者是在该目录的父目录中的另一个应用程序的application对象。 为了删除一个虚拟应用程序,可简单地在internet services manager中的对象上单击右键,在弹出的菜单中选择delete。 (5) 应用程序存储的内容 asp application对象提供的全局存储空间可以用来存储下列内容: · 简单变量,例如字符串和数字(存储为variant,类似于asp脚本变量)。 · variant类型数组,一维或多维。 · 对一个com对象实例的变量引用(类型于variant)。 (6) variant的定义 variant是在vbscript脚本引擎中为asp(和internet explorer)提供的唯一变量类型,与在vb和vba中定义的数据类型variant相类似。与大多数常用的基本数据类型(例如字符串或整数)相比,尽管它不能使存储单元最高效地存储变量,需要进行额外的处理,但variant提供许多用处。 从内部来variant数据类型存储数值作为独立的子类型。它能自动地处理隐含的类型转换,允许使用如下的代码: strstring = ”30” intinteger = 12 response.write strstring & intinteger ‘writes 3012 in the resulting page response.write strstring + intinteger ‘writes 42 in the resulting page 隐含的数据类型转换允许忽略html广西的值和列表控件是字符串的情况,并且如果需要的话可以把它们当成数字(假设字符串确实包含一个有效的数字)。也可以随意地做一些其他的事情,诸如使用vbscript的len方法检查request.form集合中并不存在的一个值的长度。len函数试图把从request.form的调用的值转换成一个字符串。如果该值丢失,返回空(empty),转换为带有””的一个字符串,因此长度是零。 (7) vbscript数据类型及转换 也可以进行显式的数据类型转换。在vbscript中,vartype方法返回一个整数,表明variant当前保存的子类型: select case vartype(varmyvalue) case 0: response.write “empty (uninitialized)” case 1: response.write “null (no valid data)” case 2: response.write “integer” case 3: response.write “long integer” case 4: response.write “single-precision floating-point number” case 5: response.write “double-precision floating-point number” case 6: response.write “currency” case 7: response.write “date” case 8: response.write “string” case 9: response.write “automation object reference” case 10: response.write “error” case 11: response.write “boolean” case 12: response.write “variant (used only with items in arrays of variants)” case 13: response.write “data-access object” case 17: response.write “bytes” case 8192: response.write “variant array” end select 还有一些函数,如isarray、isdate、isempty、isnull、isnumeric和isobject,对于特定的子类型返回一个boolean结果。一旦知道了数据的子类型,如果包含的数据合适的话,就可以将之转换成不同的variant子类型。这对代码的透明性和检查变量是否包含合法值都是有用的,非法的转换将导致运行期错误,转换如下所示: blnboolean = cbool(varvariant) ‘converts to a variant of subtype boolean bytbyte = cbyte(varvariant) ‘converts to a variant of subtype byte curcurrency = ccur(varvariant) ‘converts to a variant of subtype currency dtmdate = cdate(varvariant) ‘converts to a variant of subtype date dbldouble = cdbl(varvariant) ‘converts to a variant of subtype double intinteger = cint(varvariant) ‘converts to a variant of subtype integer inglong = clng(varvariant) ‘converts to a variant of subtype long sngsingle = csng(varvariant) ‘converts to a variant of subtype single strstring = cstr(varvariant) ‘converts to a variant of subtype string (8) jscript数据类型及转换 在jscript中,其他一些变量类型类似于vbscript,但是没有代表对象的variant。所有值都是对象,类型是下列六种数据类型之一: · undefined:只有单个值,用于表明请求中的变量没有声明和创建,或者若是隐含创建但还未分配任何值。类似于vbscript的empty。 · null:变量不包含一个有效的值。类似于vbscript的nothing。 · boolean。 · string。 · number。 · object。 jscript提供了一个typeof函数,返回表示数据的类型的字符串,例如: strstring = ‘30'; intinteger = 12; response.write(typeof(strstring)); //write ‘string' in the resulting page response.write(typeof(intinteger)); //write ‘number' in the resulting page 每种数据类型都有tostring方法和valueof方法,tostring方法将变量的值转为一个字符串返回,valueof方法把变量的值作为它的原有数据类型返回。 (9) jscript的级联和加法问题 jscript在许多方面不同于vbscript,在一定程度上是因为没有“&”级联运算符可用。当使用加法运算符时,它先检查变量的数据类型以决定要做什么。如果两个变量都是数值,结果是数值的和。如果一个或两个是字符串,结果是字符串的级联: strstring = ‘30'; intinteger = 12; response.write(intinteger + intinteger); //writes 24 in the resulting page response.write(strstring + intinteger); //writes 3012 in the resulting page response.write(intinteger + strstring); //writes 1230 in the resulting page 如果进行加法的值超过两个,要看执行的顺序。下面的代码进行相应的示范: intinteger = 12; response.write(intinteger + intinteger + “ ”); //result is ‘24 ' response.write(“ ” + intinteger + intinteger + “ ”); //result is ‘ 1212 ' 在第一种情况下,两个数相加,结果被转换为一个字符串并与“ ”字符串进行级联。在第二种情况下,第一个运算符是一个字符串和一个数的级联,所以数被转换为一个字符串。而后,所有的运算符都是级联运算符。为了避免这个问题,可以使用括号强制第一个运算符是两个数值的加法: response.write(“ ” + (intinteger + intinteger) + “ ”); //result is ‘ 24 ' 1. web应用程序中的组件管理 在asp中使用虚拟应用程序的第二个主要方面是:具有较好的对在脚本代码内实例化和执行的组件进行管理的能力。先不讨论有关它怎样进行工作以及为什么会如此有用的详细情况,在学习asp组件的部分时再讨论相关内容。 这里非常简要地介绍在asp网页(此asp网页在该应用程序中)中使用组件时,如何对一个虚拟应用程序在properties对话框(在internet services manager中)进行设置。 在一个虚拟应用程序的properties对话框的home directory页的底部,有两个组合框,为execute permissions和application protection,如图3-9所示: 应用的保护和执行设置 因为在本章中,不讨论在一个web网页内如何创建组件的实例,因此这里先列出这些选项,在创建应用程序时可能要对此进行设置。execute permissions 选项如表3-1所示: 表3-1 execute permissions的选项及说明 选 项 说 明 none 在这个虚拟应用程序中不能运行脚本或可执行文件。实际上,提供了禁止一个应用程序的快速和简单的方法 scriptsonly 只允许脚本文件(例如asp、idc或其他的)在这个虚拟应用程序中运行,不能运行可执行文件 scriptsand executables 允许任何的脚本和可执行文件在这个虚拟应用程序内运行 execute permissions选项控制可在该虚拟应用程序中执行的类型,而application protection选项影响可执行文件和组件运行的方式。在第1章中已经讨论过可用的选项,但是在这里再重复一次,application protection选项如表3-2所示: 表3-2 application protection的选项及说明 选 项 说 明 low(iis process) 带有这种设置的asp虚拟应用程序的所有可执行文件和组件运行在web服务器的可执行文件(inetinfo.exe)的进程(即内存空间)中。因此,如果可执行文件或组件之一失败的话,web服务器处于危险状态。这提供了最快和以最少的资源执行的选项 medium(pooled) (缺省)带有这种设置的asp虚拟应用程序的所有应用程序的可执行文件和组件是运行在dllhost.exe的单个共享实例的进程(即内存空间)中。这就防止了web服务器可执行文件(inetinfo.exe)受可执行文件或组件失败的影响。然而,一个失败的可执行文件或组件可能引起dllhost.exe进程失败,以及所有其他驻留其中的可执行文件和组件失败 high(isolated) 带有这种设置的asp虚拟应用程序的所有应用程序的可执行部分和组件是运行在dllhost.exe的单个共享实例的进程(即内存空间)中,但是每个asp应用程序都有自己的dllhost.exe实例,该实例对该应用程序是独占的。这就防止了web服务器可执行文件(inetinfo.exe)受可执行文件或组件失败的影响,并防止虚拟应用的单个共享实例受另一个虚拟应用程序的一个可执行文件或组件失败的影响。microsoft建议最多有十个这样的虚拟应用程序驻留在一个web服务器上 3.2.2 asp会话的定义 asp会话引入了一个web应用程序中粒度的下一层。asp的application对象可用来存储对于“正在运行此应用程序”的所有用户都是全局的和可访问的状态(即简单变量、对象、数组等)。换句话说,用于响应这个应用程序内所有访问者的请求的全部asp代码能够对这些值进行访问(假设已经建立了一个有效的会话,稍后将看到)。 但是在多数情况下这还不够。需要具备存储指定给每个用户的值的能力,而不必通过给这些值分配名字指明其隶属于哪个用户。例如,下列值很可能弄乱应用程序的全局存储空间: mikejones003preffgcolor = “darkblue” mikejones003prefbgcolor = “white” mikejones003preflinkclolr = “green” priscilladelores001preffgcolor = “red” priscilladelores001prefbgcolor = “darkgrey” ... etc. 从载入系统资源和要求一些代码访问每个用户的相应会话的角度来看,还有另外的不足之处。只要有访问者,该应用程序就一直存在,这意味着应用程序的全局存储空间需要不断增大,除非在用户离开该网站时采取步骤删除这些值。 在web应用程序中提供用户层作用域 除了使用全局变量的存储以外,应该为每个访问者分配他们自己的私有变量存储空间,使其对指定访问者载入的所有页面都可用的。这种情况下,可以对每个变量使用相同的名字,使asp代码非常简单地得以实现。这些相同的代码对每个用户将透明地进行工作,因为访问的只是访问者拥有的私有存储区域: preffgcolor = “darkblue” prefbgcolor = “white” preflinkcolor = “green” 这就是session对象产生的地方。 (1) 会话存储的内容 会话存储的内容对指定的访问者是全局的,而对其他访问者来讲是私有的,这使得asp的会话非常有用。可以用来存储在asp application对象中存储的相同类型的数据,即: · 简单变量,例如字符串和数值(像所有的asp脚本变量一样存储为variant)。 · variant数组,一维或多维。 · 对一个com对象的实例的变量引用(如同variant)。 (2) 会话带来的问题 会话提供了一个存储每个用户特定的值的方法。然而有几个意想不到的问题要注意: · 记住一些浏览器和web服务器对url、路径和文件名的大小写形式是敏感的(例如navigator和基于unix/linux的服务器)。如果把一个超级链接放置在网页的一个url上,并且它们不是同样的书写形式,则在浏览器中被认为是不相同的。同样,如果路径和文件名的书写形式不是相同的,则浏览器认为是不同的路径或文件。对于在服务器上的定位资源来讲,这并不重要,因为iis对书写形式不敏感,可以接受大写形式和小写形式的任何组合,并返回书写形式不同的具有相同字符的文件。然而,如果一个cookie已经指定了一个路径,而且与在超级链接中所指定的路径在书写形式上不同,浏览器可能不会把它以及相应目录的页面一起返回给服务器。这有可能找不到依赖于这个cookie的一个用户会话,并且session对象将不会在作用域中(即其中的任何变量都是不可用的)。因此,在所有的目录和网页名字中,坚持都采用小写形式或者是比较明显的混合字母形式,是一个好办法。 · 在iis和asp的早期版本中,对于嵌套的应用程序还有一些小的“bug”,有时,当用户离开嵌套的asp虚拟应用程序并返回到缺省的asp应用程序层时,与嵌套应用程序内部已经定义的局部变量相同的名字的任何全局变量不能重新显现。还有,当会话使用session.abandon方法(稍后将会看到)终止时,global.asp文件中的代码将执行失败。在asp 3.0中已经解决了这些问题。 · 记住会话依赖于cookie。如果访问者已经禁止使用cookie或者浏览器不支持cookie,将不能启动一个会话,并且不能访问session对象。 (3) 禁止会话 虽然状态提供了有益环境,但让用户门户大开。如果不需要保留状态,可以禁止会话以节约计算机的处理时间。例如,在一个不需要跟踪访问者或不需要为访问者保留全局值的web网站上,可以防止会话启动,通过在internet services manager中设置属性,或为不需要状态的独立网页增加代码(稍后将看到)。 为了禁止整个web网站的所有会话,可编辑缺省web网站应用程序的属性。为了禁止一个指定应用程序的会话,可编辑相应虚拟应用程序的属性。打开相应的应用程序的properties对话框,在home directory页上单击configuration按钮,如图3-10所示: 在出现的configuration对话框中,打开app option。这里可以允许或禁止整个应用程序的会话(在本例中是整个缺省web站点),也可以修改缺省的会话timeout值。在图3-11中可以看到timeout设置为20min。在asp的早期的版本中这是缺省值,可根据要求设置相应的值(在asp 3.0中,缺省值是10min)。 如果要禁止一个指定网页的会话,同时允许它们在同一个应用程序的其他网页中创建和使用,可为该页面增加一条asp处理指令。它跟在指定缺省语言的语句后(如果没指定一个缺省语言,可单独使用该指令): <%@language=”vbscript” enablesessionstate=”false”%>到此为止,大致介绍了asp应用程序和session对象的一些情况,下面详细地进行讨论。