打印本文 打印本文  关闭窗口 关闭窗口
ASP与SQL网站数据库程序设计(10)
作者:. 文章来源:科学出版社 点击数: 更新时间:2004/1/25 0:04:07

 

Response,Request对象的基础与应用

9-1 Response对象的基础与应用

9-2 Request对象的基础与应用

9-3 Session对象的基础与应用

9-4 Application对象的基础与应用

9-5 Global.asa对象的基础与应用


Active Server Page属于面向对象程序语言(Object Oriented Language)的一种,它提供了五个重要内建对象供程序设计人员使用。当程序设计人员在使用对象时,无需了解各对象内部复杂的传递及运算过程,这也是面向对象程序设计的好处之一。

以下是这五种ASP对象的名称与简述对照表。

9-1  ASP对象的名称与简述

l      对 象 名 称

l      功 能 简 述

l      Response

l      用来传输信息到客户端流览器

l      Request

l      可用来读取客户端流览器的信息

l      Server

l      提供一些Web Server端的相关信息

l      Session

l      用来存储不同用户的信息

l      Application

l      用来存储所有用户共享的信息

9-1  Response对象

ASP程序中的Response对象,主要功能是将ASP程序执行的结果输出到浏览器。对于Response对象而言,WriteEndRedirect三种方法大约占了80%的应用,其他方法和属性的使用率并不会超过整体应用的20%。以下是Response对象的语法以及“集合”、“属性”、“方法”的详细说明:

Response对象的语法:

Response.collection|property|method

1. 集合(Collection

Response对象中属于集合的只有“Cookies”一种。对于Cookies而言,有一点是非常重要的,Cookies的写入只有在任何数据输出到浏览器之前才能发挥它的作用。同样,Cookies也有其专用的属性,如表9-2所示。

9-2  Cookies属性说明

l        

l      说  明

l      Expires

l      指定Cookies存放在客户端浏览器的有效期限,格式为“yyyy/mm/dd”

l      Domain

l      指定只有在进入该网时,Cookies的值才有效


续表

l        

l      说  明

l      Path

l      指定浏览器的网页必须在指定的路径下,该Cookies才允许被读取

2. 属性(Properties

Response对象中共有四个属性,以下是这些属性的内容。

9-3  Response对象的属性说明

l        

l         

l      Buffer

l      Buffer被设置为true(通常是在.ASP文件的最前面完成的),整个网页会在结果送回客户端之前处理完毕。换句话说,所有在网页里面的脚本在处理完毕之后,才会将结果送回客户端浏览器上显示。当设为false时,则编译即发送

l      ContentType

l      指定即将开始被送至客户端浏览器的信息种类,默认值是text/HTML。举例来说,如果它被设为text/gif,那么送出去的文件数据将会被解释成gif格式的图形文件

l      Expires

l      用来决定暂存在客户端的网页其时间的长短,并且Expires变量将会检查它是否已经被更新过,Expires数字以秒为单位。“0”表示该网页被设置为不会暂存在客户端浏览器中,另一种的替代方法是使用ExpiresAbsolute属性来指定该网页精确的到期日以及时间

l      Status

l      一个用来显示状态的三位数字。这些状态码都已经被定义在HTTP规格之中

3. 方法(Methods

Response对象中共有八种方法。使用这些方法可以为用户提供更便于创建网页的方法。

9-4  Response对象的方法说明

l        

l         

l      AddHeader

l      AddHeader会在网页中加入HTML的标头以及一个数值,必须在任何网页标记被送出之前使用AddHEader


续表

l        

l         

l      AppendToLog

l      在一次请求中,对主机端的记录文件附加一段最多80个字符的字符串至记录文件中,在使用这项变量中,不能在字符串中使用逗号

l      BinaryWrite

l      将信息以二进制的形式送到HTTP输出通道。如果用户有一个自行开发的应用程序必须在客户端执行,并且该程序是利用HTTP通道与服务器作通信用时,使用这个方法相当有用

l      Clear

l      使用这个方法会将当前在HTML缓冲区里的Response的信息内容全部清除,但是它不会清除HTML的标头部分,通常是在错误状态下使用这个方法

l      End

l      立即停止当前正在处理的.ASP文件,并且将会返回当前缓冲区里的所有信息,如果用户不想返回任何信息,请先使用Clear,再使用End这个方法

l      Flush

l      立即将当前缓冲区里所有信息送至客户端

l      Redirect

l      将客户端浏览器的网页立即引至新的网页/程序上

l      Write

l      HTML的形式将信息写入到当前网页,一般来说,用户能够将用户的HTML写在<%%>符号之外,或是使用<% =变量%>来作同样的事情,不过Write方法特别适用于子程序或函数的内部

9-1-1 Write方法的基础与应用

首先,笔者先介绍有关Response.Write方法。Response.Write方法可以在ASP程序的执行过程中,将任何数据类型输出到浏览器上显示。换句话说,输出的内容可以是字符串、HTML标记或变量,而且无论变量的内容是什么,都是以字符串的形式输出到浏览器上,如下所示:

' 输出变量的内容

Strings = “Active Server Pages SQL 2000 的应用

Response.Write(Strings & "<br>")

' 直接输出字符串

Response.Write("Active Server Pages SQL 2000 的应用 <br>")

上述程序以变量或是字符串的方式将结果输出到浏览器上,其输出的字符串内含HTML标记。此外Response.Write方法还有另外一种方法,如下所示:

<%=Strings%>

上述“<%=”、“%>”符号间就是输出Strings变量的内容,这属于Response.Write方法另一种程序编写方式。

程序范例:Ch9-1.asp

ASP程序中,使用三种Response.Write方法,将文字输出到浏览器上,并且显示输出的内容

上述图例虽然输出的字符串相同,但是第一行为输出变量值,第二行直接输出字符串和标记,最后则使用“<%=”和“%>”符号输出变量内容。

程序内容

01: <! -- 范例程序:EX09-1.asp -->

02: <HTML>

03: <HEAD>

04: <META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">

05: <TITLE>EX09-1.ASP</TITLE>

06: </HEAD>

07: <BODY>

08: <%

09: Dim Strings

10: Strings = "Active Server Pages SQL 2000 的应用"

11: ' 输出变量

12: Response.Write(Strings & "<br>")

13: ' 直接输出字符串

14: Response.Write("Active Server Pages SQL 2000 的应用<br>")

15: %>

16: <%=Strings%><br>

17: </body>

18: </html>

程序说明

9行:定义变量Strings

10行:设置字符串变量Strings的内容。

12行:使用Response.Write方法输出变量的内容,并且使用字符串连接运算符结合HTML标记。

14行:使用Response.Write方法直接输出字符串及HTML标记。

16行:使用“<%=”、“%>”符号输出字符串变量Strings

9-1-2 Redirect方法的基础与应用

Response.Redirect方法可以在执行ASP程序时,直接将网页重定向到其他的网址或网站,其他网页或ASP程序。使用这个方法会将当前的URL转到其他的网页,因此在执行此方法后,相关的命令或是HTML标记将不会显示,如下所示:

Response.Redirect("EX09-1.asp")

ASP程序执行到上述程序时,会放弃此行后面所接的相关程序,并且转而执行网站上相应目录下的“EX09-1.asp”。同时对于Response.Redirect的参数而言,允许使用完整的URL网址或同一个网站相对路径,并且也可以是HTML文件,如下所示:

Response.Redirect("../EX10/EX10-1.asp")

Response.Redirect("http://www.seed.net")

上述两行程序代码中,第一行所指的是转到相同网站上的EX10子目录下,并且执行该子目录下的EX10-1.asp程序,第二行则是将当前网页直接转到其他网站的首页。

程序范例:EX09-2.asp

在地址栏输入“http://localhost/ex09/EX09-2.asp

当用户按下Enter键之后,用户可以看到网页内容已经改为EX09-3.asp,并非是EX09-2.asp的内容

9-3  直接由ex09-2.asp跳到09-3.asp的画面

由上面的演示,即可发现Response.Redirect方法在实际应用上的作用。

程序内容

01: <%

02: Response.Buffer = True

03: %>

04: <!-- EX09-2.asp 转到其他的网页或ASP程序 -->

05: <HTML>

06: <HEAD>

07: <META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">

08: <TITLE>EX09-2.asp 转到其他的网页或ASP程序 </TITLE>

09: </HEAD>

10: <BODY>

11: <% Response.Redirect "EX09-3.asp" %>

12: </BODY>

13: </HTML>

程序说明:

2行:如果网站是PWSIIS 4,用户就必须加上缓冲区的设置。有关详细缓冲区的详细信息,将在后面章节中详细说明。

11行:直接将执行后的网页重定向到EX09-3.asp

程序内容:

01: <!-- EX09-3.asp Response.Redirect 网页转向范例 -->

02: <%

03: Response.Buffer = True

04: %>

05: <HTML>

06: <HEAD>

07: <META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">

08: <TITLE>EX09-3.asp Response.Redirect 网页转向范例</TITLE>

09: </HEAD>

10: <BODY ALIGN=CENTER>

11: <FONT SIZE=6>

12: Response.Redirect 转向范例

13: </FONT>

14: <HR WIDTH="85%">

15: <FONT SIZE=5>

16: 这是 ASP 对象中的 Response.Redirect 方法的范例<BR>

17: 用途是将当前的网页内容转到其他的网页或ASP程序

18: </FONT>

19: </BODY>

20: </HTML>

程序说明:

这个程序可以说是纯HTML文件,其内容仅仅为单纯网页内容的显示。因此笔者在此不多做说明。

9-1-3 END方法的基础与应用

ASP程序主要是使用Response.Write方法输出网页内容,如果程序需要中断输出,只需在程序的断点插入Response.End方法即可,其程序代码如下:

其他的程序代码

Response.End

其他的程序代码

当程序执行到上述代码时,Response.End方法之后的程序代码或HTML标记就不会执行或显示,如果设置为使用缓冲区,此方法就会将缓冲区的内容送到浏览器。

程序范例:EX09-4.asp

ASP程序中插入Response.End代码,则只输出此代码前的网页内容,如图9-4所示。

由上图可知,在Response.End之后的程序没有执行,后半段程序的文字就没有继续输出。

程序内容:

01: <!-- EX09-4.asp Response.End 中断程序代码执行的范例 -->

02: <HTML>

03: <HEAD>

04: <META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">

05: <TITLE>EX09-4.asp Response.End 中断程序代码执行的范例</TITLE>

06: </HEAD>

07: <BODY>

08: <CENTER>

09: <FONT Size=6 >

10:中断程序代码执行的范例

11: </FONT>

12: <HR WIDTH="85%" ALIGN="center">

13: <FONT SIZE=3>

14:这是 ASP 对象中的 Response.End 方法的范例<br>

15: <%

16: Response.End

17: %>

18:用途是中断当前 ASP 程序代码的执行

19: </FONT>

20: </CENTER>

21: </BODY>

22: </HTML>

程序说明

10~14行:示范文字。

16行:使用Response.End方法中断网页的输出。

18行:这是不会输出的HTML文件内容。

9-1-4 Cookies的基础与应用

ASP Response对象中的Cookies主要是在客户端浏览器上,存储一些网站上的相关信息。例如,保存用户访问本网站的次数,以下是Response.Cookies程序代码的范例:

其他的程序代码

Response.Cookies(“MySite”)

其他的程序代码

上述代码主要是将存放于用户端的Cookies内容显示在浏览器上。并且在显示的同时,将只显示变量名称为“Mysite”的内容。

程序范例:EX09-5.asp

在此,用户可以看到整个浏览器的内容是空的,同时并没有出现错误信息。事实上,当Cookies写入到客户端时,浏览器并不会做出任何响应,这是因为Cookies的写入是在任何的文字(包含HTML的标题)输出到浏览器之前。如果用户是在程序的中间才写入Cookies,最好能够确定在写入之前没有任何文字输出到浏览器,否则客户端将会得到一个错误的响应信息。有关错误的Cookies写入方式,将会在下一个范例中为用户展示。

程序内容:

01: <%

02: Response.Cookies("MySite") = "MySite Cookies"

03: %>

04: <!-- EX09-5.asp Response.Cookies 的写入执行范例 -->

05: <HTML>

06: <HEAD>

07: <META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">

08: <TITLE>EX09-5.asp Response.Cookies 的写入执行范例 </TITLE>

09: </HEAD>

10: <BODY>

11: </BODY>

12: </HTML>

程序说明:

2行:本行程序代码将会在客户端写入一个名为“MySite”的Cookies变量,同时此变量的内容为“MySite Cookies”。

在浏览器执行上述程序代码时,并不会在客户端确实留下Cookies的相关信息,这是因为用户并未指定这个Cookies的有效期限,因此,这个Cookies的有效期仅限于当前这个会话期(Session)。当这个程序执行结束时,Cookies的内容也就跟着消失。如果用户希望将这个Cookies的内容确实写入客户端,用户就必须指定这个Cookies的有效期限。根据上述程序代码的修改范例如下所示:

<%

Response.Cookies("MySite") = "MySite Cookies"

Response.Cookies(“Mysite”).Expires = “2001/07/30”

%>

程序范例:EX09-6.asp

这是个错误的Cookies写入范例,笔者在此不提供详细的说明,请读者自行参考程序代码的内容,再参照先前的说明就会明白产生错误的原因

程序内容:

01: <%

02: Response.Buffer = false

03: %>

04: <!-- EX09-6.asp 错误的 Response.Cookies 写入范例 -->

05: <HTML>

06: <HEAD>

07: <META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">

08: <TITLE>EX09-6.asp 错误的 Response.Cookies 写入范例</TITLE>

09: </HEAD>

10: <BODY>

11:这是 Response.Cookies("MySite") 错误的写入范例

12: <%

13: Response.Cookies("MySite") = "MySite"

14: %>

15: </BODY>

16: </HTML>

9-1-5 服务器缓冲区处理

Response对象的属性主要用于控制输出的数据类型和缓冲区,这一节先来讨论缓冲区的处理问题。

1. ASP缓冲区的用途

Web服务器在解释ASP脚本的过程中,可以选择将结果立即输出到客户端的浏览器上,或是将结果存放在缓冲区之中,等到所有的ASP脚本执行完毕之后,才将完整的结果输出到浏览器上。以下所列的是三种将ASP的执行结果送到浏览器的处理方法。

2. ASP程序代码完全解释完成之后

执行到Response.End方法时,将会中断ASP程序的执行,并且将已经解释的部分送到浏览器上显示。

执行到Response.Flush方法时,立即将缓冲区的内容送出。

Response对象另外提供了Clear方法可以清除缓冲区的内容,将当前已经输出的缓冲区的数据都清除掉。

3. 设置是否使用缓冲区

Response.Buffer属性可以设置ASP程序在输出时,是否使用缓冲区,以下是在ASP的不同版本上,缓冲区的默认值列表,如表9-5所示。

9-5  缓冲区的默认值列表

l          ASP 版本

l      Buffer 默认值

l      Web服务器

l      2.0

l      False

l      PWS/IIS 4.0

l      3.0

l      True

l      IIS 5.0

ASP 2.0中,如果需要使用缓冲区,请在ASP程序开头设置Buffer属性,其值为True,如下所示:

Response.Buffer = True

下面笔者以EX09-6.asp为例,仅将第2行中的“False”改为“True”。因此,对于程序代码的内容将不提供详细的说明。

以上所述多半是Response对象较为常用的方法、属性以及集合。Response对象所提供的其他内容并非完全没有使用价值,但是就笔者接触ASP程序设计多年的经验,其他的方法、属性以及集合几乎很少使用。如果本节开始时所介绍的无法满足用户的需求时,用户可以参考微软网站上所公布的相关信息。

9-2  Request对象

Request对象与Response对象事实上是相辅相成的。就上一节的内容而言,Response对象是将ASP程序的执行结果送到浏览器上显示,而Request对象则相反,Request对象可以取得浏览器上相关信息。Request对象的语法如下:

Response对象的语法:

Request[.collection\property\method](variable)

9-6Request对象集合的相关变量。

9-6  Request对象集合的相关变量

l        

l      说  明

l      ClientCertificate

l      这些值存放在客户端浏览器的认验证里,它可以激活并且识别存取该网页的授权以及评核工作

l      Cookies

l      取得存放在客户端中Cookies的内容

l      Form

l      存取用户在窗体中输入的相关字段的值

l      QueryString

l      作为数据库查询用的变量值,这个信息通常跟随在URL的“?”符号之后,一般使用在页面的参数传递中

l      ServerVariables

l      关于主机端的相关环境变量

以下是Request对象集合的标准语法范例:

<%

Request.Cookies(“ProductInfo”)

Request.QueryString(“Name”)

%>

另外,用户也可以使用类似快捷方式的方法来存取这些字段。因此,Request的语法范例也可以被改写为如下所示的程序代码:

<%

Request (“ProductInfo”)

Request (“Name”)

%>

但是在使用这种方法时,虽然简单,但同时也存在相当大的风险。因为在程序设计过程中,很有可能碰到有两个不同种类的字段但却使用相同的名称。以下是笔者不建议使用简易的Request语法的原因:

对于ASP默认的脚本语言VBScript而言,寻找变量的种类是按照下列顺序:QueryStringFormCookiesServerVariables,最后才是ClientCertificate

当程序运行时,程序设计人员不易区分当前所取得信息的种类。

9-2-1 ClientCertificate变量

ClientCertificate这个变量可以取得客户端浏览器的身份确认信息。但是希望利用此方法取得客户端浏览器的验证之前,首先必须确认客户端浏览器是否支持SSL3.0PCT1协议。假若客户端浏览器尚未具备身份确认信息或Web Server也尚未设置向客户端要求身份确认时,那么ClientCertificate将会返回“EMPTY”值。

9-2-2 Cookies集合

Request对象的Cookies集合是用于读取存放于客户端浏览器上的Cookies内容。Request对象的Cookies集合与Response对象的Cookies集合的使用时机不相同。Response对象的Cookies的使用时机限定在送出任何HTML标记之前,而Request对象的Cookies集合则可以灵活地运用在ASP程序代码的任何部分。最为常用的例子是使用在购物车的设计中或是网站广告上。以下是Cookies对象的语法:

Request.Cookies(名称)[(关键字)|.属性]

·       名称,Cookies变量名称。

·       关键字,用来恢复子关键字的值。

·       属性值,用来指定想要的Cookies值。

由于Cookie必须先写入客户端中才能从客户端取出,而有关Cookies的写入范例部分,在上一节中笔者已经详细介绍过,因此请读者自行参考上一节的范例说明。

以下是Cookie的读写范例执行的结果与程序内容说明。

程序范例:EX09-7.asp

程序内容

01: <%

02: Response.Buffer = False

03: ' 将变量内容为 "MySite Cookies 测试" "MySite" Cookies 变量写入到客户端

04: Response.Cookies("MySite") = "MySite Cookies 测试"

05: %>

06: <!-- EX09-7.asp Cookies 的读写执行范例 -->

07: <HTML>

08: <HEAD>

09: <META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">

10: <TITLE>EX09-7.asp Cookies 的读写执行范例</TITLE>

11: </HEAD>

12: <BODY>

13: Cookie 的内容为 "

14: <%

15: Response.Write Request.Cookies("MySite")

16: %>"

17: </BODY>

18: </HTML>

程序说明

15行:笔者使用Response.Write方法将Cookie的内容以Request.区性Cookies的方法读出并且显示在浏览器上。

9-2-3 Form集合

通常我们使用Form变量的目的是用来取回客户在窗体中填写的相关信息。但是在使用Request.Form的对象时,HTML标记中的<FORM>必须将方法(Method)指定为“POST”,否则将无法取得窗体中的字段信息。因此,HTML标记中的<FORM>必须写成以下的形式:

<Form Method=post>

请输入姓名:<Input type=text Name=”Name” Value=”” >

<Input type=submit value=“开始测试”>

</Form>

输入的姓名是:<% = Request.Form(“Name”) %>

从以上的范例程序代码中,用户可以清楚地了解到这是一个与窗体结合相当密切的对象。在Internet上各种输入数据的窗体中,都可以由Request对象的Form集合取回相关信息。

程序范例:EX09-8.asp

 

程序内容

01: <!-- EX09-8.asp Request.Form 对象的读取执行范例 -->

02: <HTML>

03: <HEAD>

04: <META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">

05: <TITLE>EX09-8.asp Request.Form 对象的读取执行范例</TITLE>

06: </HEAD>

07: <BODY>

08: <Form Method=post>

09: 请输入姓名:<Input type=text Name="Name" Value="" >

10: <Input type=submit value="开始测试">

11: </Form>

12: 用户输入的姓名是:

13: <FONT Color=Blue>

14: <% = Request.Form("Name") %>

15: </Font>

16:

17: </BODY>

18: </HTML>

程序说明

8行:HTML<FORM>标记在没有指定其“Action”属性的文件名称时,将会自动重新执行当前所在的网页内容。

14行:由于<FORM>标记的Method属性使用的是POST,因此将可以直接使用Request对象Form属性,将窗体中的Name字段读出。

程序范例:EX09-9.asp

程序内容

01: <!-- EX09-9.asp Request.Form 对象的读取执行范例 2 -->

02: <HTML>

03: <HEAD>

04: <META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">

05: <TITLE>EX09-9.asp Request.Form 对象的读取执行范例 2</TITLE>

06: </HEAD>

07: <BODY>

08: <Form Method=post id=form1 name=form1>

09: 请输入姓名:<Input type=text Name="Name" Value="" ><BR>

10: 请选择性别:<Select Name="Gender">

11: <Option "">

12: <Option "">

13: </Select>

14: <Input type=submit value="开始测试" id=submit1 name=submit1>

15: </Form>

16: 用户输入的姓名是:

17: <FONT Color=Blue>

18: <% = Request.Form("Name") %><BR>

19: </Font>

20: 用户的性别是:

21: <FONT Color=Blue>

22: <% = Request.Form("Gender") %><BR>

23: </Font>

24: </BODY>

25: </HTML>

程序说明

1013行:这是HTML文件中,标准的下拉菜单编写方法。

22行:笔者以Request对象Form集合,将窗体中的“Gender”字段的选项值读出,并且显示在浏览器上。

9-2-4 QueryString变量

QueryString变量一般是应用于网页间参数的传递。简单地说,URL参数和窗体的Get方法都是使用QueryString数据集合,如果窗体采用Post方法就是使用Form数据集合。通常在地址栏直接传送数据都是以QueryString变量的方法传送变量名及数值。并且,变量的名称与变量的内容必须接在“?”符号之后。例如:

http://localhost/EX09/EX09-10.asp?Strings=ASP & SQL网页数据库程序设计”

范例程序:EX09-10.asp

程序内容

01: <!-- EX09-10.asp Request.queryString 对象的读取执行范例 1 -->

02: <HTML>

03: <HEAD>

04: <META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">

05: <TITLE>EX09-10.asp Request.Form 对象的读取执行范例 1</TITLE>

06: </HEAD>

07: <BODY>

08:

09: 用户发送的字符串变量的名称 Strings ,内容是:

10: <FONT Color=Blue>

11: <% = Request.QueryString("Strings") %><BR>

12: </Font>

13: </BODY>

14: </HTML>

程序说明

11行:笔者直接用Request对象QueryString集合,将传递的参数直接读出并且显示在浏览器上。

可是在上图的执行结果中,明明输入的是“http://localhost /EX09/EX09-10.asp?Strings=ASP & SQL网页数据库程序设计”,在执行之后却变成了“http://localhost/EX09/EX09-10.asp?Strings =ASP%20&%20SQL网页数据库程序设计”。还有,“Strings”变量的输入内容与显示的不符,这是怎么回事呢?事实上,虽然用户输入的是空格,但是Web服务器在接收到空格时,将会自动解释成“%20”。另一个问题,输入与显示的结果不符,这又是怎么一回事呢?事实上,这是误打误撞的结果。在使用URL作为参数传递时,是可以允许同时传送多组变量的。而这些变量与变量之间使用的分隔符即为“&”。在上述范例中,“&”符号之后并未指定变量的名称;同时即使指定了变量名,但是在程序中并没有读出变量内容的程序代码,因此将无法正常显示。

范例程序:EX09-11.asp

程序内容

01: <!-- EX09-11.asp Request.queryString 对象的读取执行范例 2 -->

02: <HTML>

03: <HEAD>

04: <META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">

05: <TITLE>EX09-11.asp Request.Form 对象的读取执行范例 2</TITLE>

06: </HEAD>

07: <BODY>

08: 用户发送的字符串变量的名称 Strings1 ,内容是:

09: <FONT Color=Blue>

10: <% = Request.QueryString("Strings1") %><BR>

11: </Font>

12: 用户发送的字符串变量的名称 Strings2 ,内容是:

13: <FONT Color=Blue>

14: <% = Request.QueryString("Strings2") %><BR>

15: </Font>

16: </BODY>

17: </HTML>

程序说明

815行:在使用多组变量传递时,只要使用多组读取、显示的程序代码,即可将变量的内容正确地显示出来。

这种读取方式,在以后的实务设计中,笔者使用得非常频繁。因此,请读者务必要了解这种参数传递的运行原理。

9-2-5 ServerVariables集合

ServerVariables是用来取得各项环境变量信息的(包含主机端及客户端)。由于ServerVariables内部有许多的变量名称,笔者利用下面的程序代码将这些变量全部列出来:

范例程序:EX09-12.asp

程序内容

01: <!-- EX09-12.asp 显示 ServerVariables 集合的内容 -->

02: <HTML>

03: <HEAD>

04: <META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">

05: <TITLE>EX09-12.asp 显示 ServerVariables 集合的内容</TITLE>

06: </HEAD>

07: <BODY>

08: <FORM>

09: <TABLE BORDER=1>

10: <% FOR EACH Vars IN Request.ServerVariables %>

11: <TR>

12: <TD><% = Vars %></TD>

13: <TD><% = Request.ServerVariables(Vars) %> </TD>

14: </TR>

15: <% NEXT %>

16: </TABLE>

17: </BODY>

18: </HTML>

由于ServerVariables的内容非常多,因此笔者只取部分画面。其余部分请读者自行执行范例“EX09-12.asp”,即可在浏览器中得到详细信息。对于ServerVariables变量,笔者分为以下几项:

详细信息:ALL_HTTPALL_RAW

 

主机信息:APPL_MD_PATHAPPL_PHYSICAL_PATH

 

用户登录信息:AUTH_PASSWORDAUTH_TYPEAUTH_USER

 

客户端的验证信息:CERT_COOKIECERT_FLAGSCERT_ISSUERCERT_KEYSIZECERT_SECRETKEYSIZECERT_SERIALNUMBERCERT_SERVER_ISSUERCERT_SERVER_SUBJECTCERT_SUBJECT

 

主机端的验证信息:HTTPSHTTPS_KEYSIZEHTTPS_SECRETKEY-SIZEHTTPS_SERVER_ISSUERHTTPS_SERV-ER_SUBJECT

 

其他信息:CONTENT_LENGTHCONTENT_TYPEGATEWAY_IN-TERFACEINSTANCE_IDINSTANCE_META_PATHLOCAL_ ADDRLOGON_USERPATH_INFOPATH_TRANSLATEDQUERY_ STRING

 

客户端信息:REMOTE_ADDRREMOTE_HOSTREMOTE_USERREQUEST_METHODSCRIPT_NAME

 

主机端信息:SERVER_NAMESERVER_PORTSERVER_PORT_SE-CURESERVER_PROTOCOLSERVER_SOFTWAREURLHTTP_ ACCEPTHTTP_ACCEPT_LANGUAGEHTTP_CONNECTIONHTTP_HOSTHTTP_USER_ AGENTHTTP_COOKIEHTTP_ACCEPT_ENCODING

 

9-3  Session对象的基础与应用

Session对象为用户在不同ASP程序之间,所使用的信息共享对象。在用户浏览网页期间,每一个ASP程序就如同一个程序或函数;同时Session对象所创建的变量就如同全局变量一样,在每个ASP程序中都可以直接读取。

9-3-1 Session对象的基础

如果客户端请求来自ASP应用程序中的某个Web页时,该用户还没有会话,Web服务器就会自动创建一个Session对象,并且指定唯一的Session ID,在执行第一个ASP程序以及在网站内浏览其他ASP程序的整个过程期间,称为一个“Session期间”。

每一位用户都拥有其专属IDSession对象,并且只允许Session的拥有者使用,因此,即使在Web服务器上同时有多个用户同时执行相同的ASP程序,在这些用户之间的Session对象内容,都不允许其他的用户使用。

Session对象存在的时间与Session期间的时间长短是一致的。服务器根据读取的Session ID,来判断Session是否仍然属于有效期。而Session ID是否有效,以及Session期间是否有效,完全根据Session对象TimeOut属性的设置,TimeOut的默认值为20分钟。因此,使Session失效的方法共有两种,修改TimeOut以及执行“Session.Abandon”命令,这两种情况都会使Session对象失去作用。

Session对象还有另外一个特点,每当读取新的ASP程序,TimeOut属性都会归零重新计算。因此,可利用Session的这个特点,限制一般用户的Idle时间以及加长系统管理员的Idle时间。

Session对象可以根据需要,创建不同的Session变量。Session变量属于局部变量,而Application变量则是属于全局变量,其差别仅限于允许用户存取的范围不同而已。

9-3-2 Session变量的使用

Session变量存在于进入Web应用程序的用户Session期间,每一位用户都拥有各自专属的Session变量。虽然每位用户的Session变量名称可能相同,但这些Session变量的内容可能完全不同,并且只有该用户执行ASP程序时,可以存取自己专属的Session变量。

以下是创建以及使用Session对象的方法:

Session(“Counters”) = Session(“Counters”) + 1

这个例子说明的是Session对象与其他变量一样,使用相同的运算法则。上述表达式的执行结果会将原有Seesion变量Counters的数值加1,并且将结果存储到“Session (“Counters”)”中。例如:在执行前Counters的内容为2,在执行完之后,Session(“Counters”)的内容即为3

以下是Session对象的语法:

Session.collection|property|method

此外,Session对象拥有两个事件,Session_OnEndSession _OnStart,这两个事件是在Global.asa文件中定义。以下所示,属于Session对象相关的“集合”、“属性”以及“方法”的说明列表。

1. 集合(Collection

9-7  Session对象的集合项目

l        

l      说  明

l      Content

l      打开一个对主机端组件的请求

l      StaticObject

l      HTML文件进行加密处理。例如:“<”将被编译为“&lt;”,“>”将被编译为“&gt;

2. 属性(Property

Session对象共具有两种属性:SessionID以及Timeout

9-8  Session对象的两种属性

l          属性

l          说  明

l      SessionID

l      系统用来存放并且识别该连接期间所使用的唯一识别码,它的数据类型是长整数并且是只读的

l      Timeout

l      用来存放系统等待用户继续操作的时间。如果用户在系统默认的时间内并未继续操作时,当设置的时间一到便会自动结束此次连接。不过,如果用户有任何改变网页的动作时,Timeout将会自动归零,Timeout是使用分钟作为时间单位的,并且其默认值是20分钟

3. 方法(Methods

当程序设计人员希望在程序中能释放Session对象时,此时Session. Abandon是唯一可以达成目的的方法。但是请读者特别注意,使用这个方法会将所有正在连接期间的Session对象全部释放,并且将会释放所有占用的系统资源。

此外,在使用Session对象期间,Session对象还有另一个特点,即在第一次取得连接的浏览器上,打开另一个新的窗口时,Session对象将具有继承性。关于这个特点,可以由两个步骤来取得验证,“EX09-13.asp Session 对象的创建与 Session 变量的设置”以及“EX09-14.asp Session 变量的内容显示”。以下是这两个范例程序的执行过程与结果:

范例程序:EX09-13.aspEX09-14.asp

程序内容

01: <!-- EX09-13.asp Session 对象的创建与 Session 变量的设置 -->

02: <HTML>

03: <HEAD>

04: <META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">

05: <TITLE>EX09-13.asp Session 对象的创建与 Session 变量的设置</TITLE>

06: </HEAD>

07: <BODY>

08: 当前的 SessionID 为:

09: <% = Session.SessionID %><BR>

10: 创建一个 Session("Counters") 变量,并且将内容设置为 2

11: <%

12: Session("Counters") = 2

13: %>

14: </BODY>

15: </HTML>

程序说明

9行:取得当前此窗口所使用的SessionID,并且将SessionID的编号显示出来,以便与下一个新增窗口的SessionID比较。

12行:将名为“Counters”的Session变量其值设置为2

程序内容

01: <!-- EX09-14.asp Session 变量的内容显示 -->

02: <HTML>

03: <HEAD>

04: <META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">

05: <TITLE>EX09-14.asp Session 变量的内容显示</TITLE>

06: </HEAD>

07: <BODY>

08: 当前的 SessionID 为:

09: <% = Session.SessionID %><BR>

10: 原始 Session("Counters") 变量的数值累加 1 ,其结果为

11: <% = Session("Counters") + 1%>

12: </BODY>

13: </HTML>

程序说明

9行:取得新增窗口所使用的SessionID,并且显示其内容。

11行:将原窗口中的Session(“Counters”)变量的数值累加“1”,并且显示累加之后的结果。

此外,利用Session对象时间的限制,对有特殊管理目的的PID(程序识别码)设计时,笔者常会对具有系统管理员身份的用户进行网页自动更新的设计。例如:某位管理员的浏览器必须长时间的打开,以监控当前的相关信息。但是由于每次登录所产生的PID皆不相同,使用Session对象存放这个PID序号时,笔者利用网页自行更新的能力,并且在更新的同时保持该Session对象的延续性。使用这种方法的目的在于:

·        防止利用重新输入URL的方法直接进入系统的管理核心。

·        防止同一管理员帐号在两台不同的计算机上同时执行。

一般用户因未采用这种方法,当PID不存在时,系统将会自动强迫该用户离线,以免因用户太久未操作而导致系统资源的浪费。

9-3-3 Session变量的使用限制

如果ASP程序使用Session变量传递数据,在Session变量的使用上将会有一些限制,这些限制如下所示。

如果浏览器版本为Internet Explorer 4.0以前的版本,则必须打开Cookies功能。虽然Session变量存储在服务器之中,但是用户Session ID仍然要使用Cookies作为存储方式。

ASP程序如果执行Response.Redirect转向命令,Session对象只限于在同一个Web服务器上使用,不能转到其他的Web服务器上。

如果是一个大型的网站应用程序,并且程序存放在许多子目录之中,子目录ASP程序所创建的Session变量无法在其他子目录中存取。但是如果是以“新增窗口”的方式执行ASP程序,则不在此限。

由于每一位用户都必须创建一份其专属的SessionID,因而Session变量在使用上较浪费内存。如果是一个相当热门的网站,登录的人数非常多时,服务器所使用的内存将会十分惊人。

因此,为了避免上述Session变量在使用上的限制,除了用户的重要信息之外,笔者建议用户尽量使用窗体隐藏字段的方式,取代Session变量在网页间传递所需的数据。

9-4  Application对象

ASP技术拥有HTTP协议的特性,这意味着ASP程序并非在客户端的计算机上执行,而是在Web服务器上执行。而客户端浏览器主要的用途仅为阅读网页内容,或是提供给用户输入数据的接口而已。

下面将讨论的重点转回到Web服务器上。由于连到网站上的用户各自拥有不同的SessionID(在上一节之中已经验证过),同时由于网站的各网页之间并没有绝对的关联性存在,因而在这些用户之间的ASP程序,其执行中的各项信息将无法共享。

9-4-1 用户ASP程序之间信息的共享

当用户使用浏览器访问网站时,从网站管理角度考虑,同时也应该决定允许用户浏览网页的权限。但是对于服务器而言,根本无从得知这些浏览不同网页的用户状态。如果在一个聊天室中,缺乏这些相关信息,用户与用户之间将无法得知在同一聊天室还有哪些用户正在线上进行对话。

因此网站所使用的服务器不仅要允许多位用户同时进入网站,对于网站的应用程序系统而言,还需考虑到数据的共享,如下所示:

·        共享给网站所有的用户——Application变量:例如,当前在线上的用户人数。

·        每位用户的专属信息——Session变量:例如,登录网站的用户数据。

在同一目录及其子目录下的所有ASP文件构成了ASP应用程序。用户可以使用Application对象,在给定的ASP应用程序的所有用户之间共享信息。

那么Session对象与Application对象有什么不同?事实上,这两者的差异点如下:

ApplicationApplication对象是存放给应用程序使用的变量,并且是全部的用户不论何时皆可存取使用。Application对象最大的特点是没有所谓的生命周期。所谓的生命周期是指不论客户端的浏览器是否被关闭,Application对象仍然存在于主机上。

SessionSession对象所保留的信息是只供给当前的用户在连接期间内(user’s session)使用。一但用户关闭浏览器,跟随的Session对象也会相对失效。

如果要产生一个Application变量,用户所需要做的事情只是引用它并且为它设置一个值。以下是Application变量的产生与设置方法:

<%

Application(InputDatas) = “请输入信息”

Application(“Answers”) = 60

Application(“counters”) = Application (“counter”) + 1

%>

不过当用户在创建或是存取全局变量时,有一件事是非常重要的:用户当前所使用的变量并不一定是服务器上正在执行的唯一程序。此话怎讲呢?在服务器上可能会有多人同时使用并且同时存取网页。因此,一旦用户企图进行全局数据的存取时,在这种情况下便会引起互锁的问题。如果有两个程序试着同时改变同一数据,那么其中的一个程序便会遗失改变的结果,这就是为什么Application对象提供必须提供“Lock”以及“UnLock”两个方法的重要原因。

使用Lock方法时,单一程序将会锁住全局变量并且禁止其他程序的存取。因此,当用户使用Lock方法将数据锁住时,这时用户便可以自由地修改这项数据。但是在用户修改完成这些全局变量的同时,请特别记得使用Unlock方法将全局变量释放出来供其他程序继续进行修改。

如果在用户执行Lock方法之后忘了使用UnLock方法将资源释放出来时,这将会使得其他需要存取Application对象的程序无法完成以致于失效。最终的复原方法是重新激活服务器或是重新激活系统的服务。

那么利用Application对象做些什么呢?一般大多数网站都是使用这种方法来记录访客的人数,即计数器。只要在用户的ASP程序里面加入以下的程序代码,便可在网页中加入一个计数器:

<%

Application.Lock

Application("Visitors") = Application("Visitors") + 1

Application.Unlock

%>

当前到访的人数:<% = Application(Visitors) %> 人次

不过在使用Application对象作为网页计数器时,用户必须特别注意的一点是不要在框架(frame)中使用它。原因是如果用户在具有框架功能的网页中采用以上的程序代码作为计数器,这时将会有额外计数的情况发生。

9-4-2 Application语法

ApplicationMthod

以下是Application对象所有相关的信息:

9-9  Application对象的集合

l       

l      说 明

l      Contents

l      包含所有已经被加入Application对象的项目

l      StaticObjects

l      包含所有已经被加入到Session对象的<Object>标记

9-10  Application对象的方法

l       

l      说  明

l      Lock

l      用来防止其他客户端的浏览器更改Application对象的内容

l      UnLock

l      允许客户端的浏览器更改Application内容

9-11  Application对象的事件

l       

l      说 明

l      Application_OnStart

l      Application“开始前”,调用该参数

l      Application_OnEnd

l      Application“结束后”,调用该参数

在了解了ASPApplication对象之后,读者可能会有一个疑问,Application对象的正确使用方法是什么呢?基本上可以分为两个部分:

·       方法(Method):用于一般的ASP程序中。

·       集合以及事件:用于Golbal.asa文件中。

9-4-3  Application变量的使用

不论网站的Web应用程序同时有多少位用户进入访问,对于每一个Application变量,服务器端都只会在内存保留一份,变量的格式如下:

Application("Page_Counter")=0

上述的变量名称和一般的ASP程序使用的变量不太一样,这是一个字符串“PageCounter”,加上Application( )表示为数据集合,上述程序代码将Application变量设为零。

在避免Application对象产生数据冲突方面,如果两个用户同时读取Application变量那大家相安无事。但如果一位更改,一位读取Application变量,在这种情况下冲突就会发生,为了避免这种情况,Application的两个方法就可以保障同一时间内只允许一位用户存取Application变量,其程序代码如下所示:

Application.Lock

Application("PageCounter")=Application("PageCounter")+1

Application.Unlock

对于Application变量的使用,在更改变量前需执行Lock,以免其他用户更改此变量的内容(如果只是读取就不需要),在改变之后,也就是将Application变量PageCounter的数值累加“1”,即可开锁,以便让其他用户更改Application变量。

范例程序:EX09-15.asp

程序内容

01: <!-- EX09-15.asp Application变量的使用 -->

02: <HTML>

03: <HEAD>

04: <META NAME="GENERATOR" Content="Microsoft Visual Studio 6.0">

05: <TITLE>EX09-15.asp Application变量的使用</TITLE>

06: </HEAD>

07: <BODY>

08: <%

09: ' Application变量初始化

10: If isEmpty(Application("Counters")) Then

11: Application.Lock

12: ' 到访人数初始化

13: Application("Counters")=0

14: Application.Unlock

15: End If

16: Application.Lock

17: ' 进入网页的次数加一

18: Application("Counters")=Application("Counters")+1

19: Application.Unlock

20: %>

21: 到访人数共计: <%=Application("Counters")%> 人次<BR>

22: </BODY>

23: </HTML>

程序说明

9~14行:使用isEmpty()函数检查Application变量是否为空的,如果为空,即立即初始化Application变量。

12~13行:初始化Application变量之前使用Lock,在初始化之后使用Unlock

16~19行:将Application变量Counters1,同时也使用LockUnlock方法,以免数据冲突。

21行:显示Application变量的数值。

9-5  Global.asa文件

使用Global.asa文件的必备条件是必须将该文件存放在网站的根目录中。对于Global.asa文件而言,它可以说是SessionApplication两大对象的精华所在。

Global.asa文件是用来进行任何ASP应用程序期间的SessionApplication事件程序,当SessionApplication被第一次调用或结束时,就会执行该Global.asa文件内的对应程序。

Global.asa主要包含了Application事件、Session事件以及<Object>定义三项工作。在Global.asa中,所有的程序代码必须编写在<Script></Script>之内。当然用户必须使用ASP所支持的脚本语言并且定义在<Script>标记之内。另外在Global.asa中不得定义非SessionApplication对象的模板,否则将造成执行上的错误。

当修改完Global.asa文件之后,Web Server会先将这个应用程序的相关工作全部处理完毕,再重新编译该应用程序。当Web Server在编译该文件的同时,任何与这个应用程序相关的客户端浏览器的请求都将不会被处理,这种情况一直到该应用程序被重新激活。

对于SessionApplication对象来说,修改Global.asa之后,Web Server将会先调用Session_OnEndApplication_OnEnd事件程序。等到重新编译之后,将会调用Application_OnStartSession_OnStart事件程序。

对于每一个服务器上的应用程序而言,用户可以为它创建一个Global.asa文件,ASAActive Server Application的缩写。Global.asa可以用于以下事件的处理:

·         定义即将被用于Application的对象。

·         ApplicationSession开始及结束时所要执行的Script写入。

Global.asa文件虽然是可选的,但是它却可以提供一个便利的使用方法来处理使用其他程序所不易做到的事情。如果用户决定使用Global.asa文件,它应该放在用户网页应用程序的根目录里,并且必须要将它命名为Global.asa

9-5-1 Global.asa文件的结构

Global.asa文件主要部分就是<script>标记内的4个事件处理程序,如下所示:

<script language="VBScript" runat="Server">

Sub Application_OnStart

…….

End Sub

Sub Application_OnEnd

.……

End Sub

Sub Session_OnStart

…….

End Sub

Sub Session_OnEnd

…….

End Sub

</script>

上述程序代码就是一个Global.asa文件的标准结构,<script>标记的属性,如表9-12所示。

9-12  Script标记的属性

l         

l      说  明

l      Language

l      设置使用的Script语言,例如:VBScript

l      Runat

l      在客户端或服务器端执行,Server为服务器端

Global.asa文件拥有四个Sub程序,这就是ApplicationSession对象的四个事件处理程序。

9-5-2 全局对象

Global.asa文件里产生的对象,可以提供给所有网页应用程序里的对象使用。但是Global.asa文件的语法与其他ASP文件是不太相同的。以下是个例子:

<OBJECT RUNAT=Server SCOPE=Session ID=WebPrg PROGID=”MSWC.BrowserType”>

这一段程序在语法上,与先前所使用过的语法具有相同的效果:

<% Set BC = Server.CreateObject(“MSWC.BrowserType”) %>

在上述的语法中,RUNAT这个保留字是必需的,并且它的值一定要设为Server。以下是这段程序代码的详细说明:

RUNAT:这个保留字的主要目的是指这个“Object”仅能在服务器上执行。

SCOPE:用来决定本对象是只给这次联机期间以及该用户使用,或本对象要给所有登录(login)的用户使用,由于属于浏览器对象,因此只能在Session中使用。如果用户需要一个可以在所有联机期间使用的对象,请将Session改为Application

ID:用来存放用户要给这个对象的名称,这个名称是让接下来的Script引用的。

PROGID:用来让系统识别该对象。除了PROGID之外,当然用户也可以利用CLASSID来取代它,只是这时用户要自行记忆那么长的ActiveX编码。

Global.asa文件中定义了一个对象之后,在应用程序的任何位置便可以直接使用该对象。

9-5-3 Application起始及结束事件

如果用户想编写一个程序,使它在该Application第一次启动时或是在该Application结束时自动激活。这时,用户可以直接将下列程序代码编写在一个名为GLOBAL.ASPASP文件中。

<SCRIPT LANGUAGE=VBScript RUNAT=Server>

Sub Application_OnStart

在这里编写当Application对象激活时,第一次执行时的程序代码

End Sub

 

Sub Application_OnEnd

在这里编写当Application对象结束时必须执行的程序代码

End Sub

</SCRIPT>

Application_OnStart:这个事件将会在第一位用户打开所有具有Application对象的任一网页时自动激活。并且,Application_OnStart将会在Session_OnStart事件发生之前激活。

Application_OnEnd:这个事件会在服务器停机之前以及Session_OnEnd事件发生前自动执行。在这两个事件里仅有的是Application以及Session内建对象。

9-5-4 ApplicationSession对象的事件处理程序

ApplicationSession对象共拥有四个事件处理程序,其执行的顺序是当第一位用户向服务器请求执行ASP程序时,这位用户就创建了Session期间和Application对象,在创建好Application对象后,检查Web应用程序是否有Global.asa文件。

如果Global.asa文件存在,在实际执行ASP程序前,将会先执行Global.asa文件的Application_OnStart事件程序,接着创建Session对象,因为Global.asa文件存在,接着就执行Session_OnStart事件处理程序。

Session期间超过TimeOut属性的设置或执行Abandon方法,就会执行Session_OnEnd事件处理程序,这个处理程序会在关闭Session对象前执行。当Web服务器停止服务时,将会在关闭Application对象前执行Application_OnEnd的事件处理程序,当然也会结束所有用户的Session期间,执行所有用户的Session_OnEnd事件处理程序。

ApplicationSession对象四个事件处理程序的说明,如下所示:

Application_OnStart:当Web应用程序的第一位用户执行第一个ASP程序时,Application_OnStart事件就会触发,在触发后不论有多少位用户进入应用程序都不会再度触发,直到Web服务器停止服务,我们可以在此事件处理程序中初始化系统设置的Application变量,例如:初始的到访人数计数器。

Application_OnEnd:当Web服务器停止服务时,就会触发此事件。

Session_OnStart:每一位用户在执行ASP程序前,就会触发此事件,如果有50位用户就会触发50次事件处理程序,每个事件都是独立触发,并不会互相影响,可以用来初始一些用户专属的Session变量。

Session_OnEnd:任何用户在TimeOut属性默认的20分钟内,没有执行其他的ASP程序时,就会触发此事件,在事件处理程序中并没有办法处理已经离开的用户。

9-5-5 Global.asa文件的使用

如果用户希望在Web服务器上使用Global.asa文件,则必须注意以下事项:

IIS 5.0版的Web服务器:每一个Web应用程序的根目录下,都可以存放一个Global.asa文件。

PWS:主目录下可以存放一个Global.asa文件,同时自行创建的虚拟目录就如同IISWeb应用程序,也可以拥有Global.asa文件。

由于Global.asa文件事件处理程序与创建的Application以及Session变量在使用上会根据Web服务器的目录结构而提供不同的变量共享范围。因此,在使用Global.asa文件之前,必须了解服务器的目录结构。

换言之,存放于Web最上层目录中的Global.asa文件,其内部所含的Application以及Session变量的内容,其涵盖的有效范围将可提供给整个Web服务器所有的ASP应用程序使用。



打印本文 打印本文  关闭窗口 关闭窗口