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对象的属性及说明 属 性 说 明
<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://yoursite.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://myserver.com/mypage.asp?title=Instant Jscript>Instant Jscript</A> 一些浏览器(例如Internet Explorer)可以对此进行处理,因为它们在把HTTP请求发送到服务器之前,自动地执行必要的转换。然而,许多其他的浏览器不进行这种转换,并导致了URL通常在第一个空格或非法字符处被截断。这样在Navigator中,上面给出的链接要求的网页变为http://myserver.com/mypage.asp?title=Instant。在服务器上,title名字/值对的丢失部分会使代码失败。 考虑到HTTP协议定义的限制,必须从作为HTTP请求中的URL使用的字符串中删除非法的字符(非法字符是所有那些ANSI代码在126之上的字符和ANSI代码在126以下的某些字符)。 ANSI代码大于126的字符必须用百分号后跟十六进制形式的ANSI代码进行替换。这样,版权字符?变成%A9。ANSI代码在126之下在URL中不合法的字符,同样使用相应的替代字符串;如表4-6所示: 表4-6 字符与HTTP/URL代替物的关系 字 符 HTTP/URL代替物 字 符 HTTP/URL代替物
空格 +
%5C
‘ %27 ] %5D
! %21 ^ %5E
# %23 ` %60
$ %24 { %7B
% %25 | %7C
& %26 } %7D
( %28 + %2B
) %29 < %3C
/ %2F = %3D
: %3A > %3E
; %3B Chr(10) 忽略
[[/TD] %5B Chr(13) [TD] %0D
4. Server对象的URLEncode方法 Server对象提供了可以用来把任意字符串转换成相应的合法HTTP URL的方法。可以利用示例网页对这个名为URLEncode的方法进行练习,如图4-29所示: 这里,输入的值作为URL是非法的,它包含了空格和ANSI代码大于126的字符。对这个值,使用URLEncode方法的结果是所有的空格被替换成一个加号,版权符号被替换为 %A9,如图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://mysite.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://mysite.com/books.asp?title=Active+Server+Pages%A9> Active Server Pages?</A> 注意,我们不仅仅使用Server.URLEncode方法来建立一个合法的URL字符串,而且还对链接的文本使用了Server.HTMLEncode方法,以确保把所有非法的字符转换为合适的HTML等价实体。 和HTMLEncode方法一样,不用反译码ASP网页中的URL编码值。IIS自动地实现URL编码字符串的转换,该字符串在HTTP请求中转换为它们原先格式,使得它们在内置对象中是可用的。