ASP中Server.Execute与动态包含(include)脚本尽管使用结果看起来差不多,但两者仍然是有差别的: include是把另一个程序的代码插入到当前这个文件中,然后再运行;而execute是当前程序运行到这个地方时,转去另一个程序运行,运行完毕再转回当前程序继续运行。
打个比方:有一件任务要甲完成,但中途有部分任务需要乙去做的,而乙在另一个房间,此时有两种解决方法: 1、Include法:把乙叫到甲的房间,两人共同完成;
2、execute法:甲先做,然后把资料带到乙的房间,等乙做完自己的任务,甲再把资料带回自己房间继续完成。 呵呵,这么说应该明白了吧。至于这两种方法对运行结果会带来什么不同,这就要结合实际才能看出来了。
补充一下,Include是在的asp程序代码还未运行的时候就被执行的,也就是说当它被插入asp时,是不受asp本身的其他代码影响的,比如下面代码是不正确的:
<!--#include file="<%=aspfilename%>"--> 同时它也是不受条件判断语句左右的: <%if a=1 then%> <!--#include file="myinc.asp"--> <%end if%> 不管a是不是等于1,myinc.asp中的代码都会被插入当前程序中的。 而execute是在当前asp程序运行过程中被执行的,所以它是可以受当前程序代码的控制的,所以以下代码是有效的:
<% if a=1 then aspfilename="myinc.asp" server.execute(aspfilename) end if %>
另外,Include所引用的文件可以是一个不完整的asp文件,或者说它可以只是程序片段,通俗点说就是补丁;而execute因为是需要单独调用的文件,所以它必须是个完整的asp文件。
最近打算尝试一下在ASP中实现MVC架构,肯定有人问我:ASP都淘汰了,为什么还研究?这点我也知道,自从微软放弃ASP 3.0转向ASP.NET后,ASP已经远远落后于和它几乎同时开始的PHP和JSP,开源比闭源的好处就像PHP和ASP一样,ASP说淘汰就淘汰,谁也救不了,但是值得注意的是ASP在中国市场还是蛮广泛的,尤其是一些中小企业的一些应用,简单的CMS不在话下,而且部署简单,在一些老旧的Windows系统上,不需要安装.NET Framework基本上就可以直接运行了,所以准备一个框架,还是有必要的,不过我这个是实验性框架,只是验证ASP究竟能不能实现类似PHP的MVC架构。
好了,说了这么多,下面直接转入正题吧。这个问题的缘由是因为我需要动态包含ASP文件,大家知道在ASP中只有一种include方法,那就是SSI(Server Side Include),基本上分为以下两种:
1 2 | <!-- #include file="sample.asp" --><!-- #include virtual="sample.asp" --> |
这两种基本上大家第一种用得多一些,#include virtual包含的是虚拟路径,一般虚拟目录会用得到。但是这两种都属于静态的,如果我们希望是动态包含,但不可以写成:
1 2 | <!-- #include file="<%=MyVar%>" --> <!-- #include virtual="<%=MyVar%>" --> |
上面的写法是错误的,可以理解为,#include指令是在ASP启动脚本引擎执行ASP<% %>标记之间脚本之前执行的,也就是说#include不是ASP的工作,而是服务端程序,如IIS的翻译工作,所以就不会理会你的ASP代码了。
如何实现类似于PHP的include、include_once、require、require_once动态包含脚本方法呢?下面再来看ASP Server对象的一个方法:Server.Execute,搜索所有的ASP特性,可以发现这个功能最类似于动态include,我们可以做个实验:
Sample.inc.asp
1 2 3 | <% Response.Write "Hello World!"%> |
test.asp
1 2 3 4 | <% Server.Execute "Sample.inc.asp" Response.Write "I am test.asp!"%> |
实际输出应该是“Hello World!I am test.asp!”,说明Server.Execute在动态包含方面可以工作得很好,但是如果我想包含类或者函数呢?接下来做下面这个实验:
Sample.class.asp
1 2 3 4 | <% Class Sample End Class%> |
test.asp
1 2 3 4 | <% Server.Execute "Sample.class.asp" Response.Write TypeName(Eval("New Sample"))%> |
直接运行,出现错误“Microsoft VBScript 运行时错误 错误 ‘800a01fa’ 类没有被定义: ‘Sample’”,结果很令人失望,为什么会出现这种情况呢?查阅了MSDN,找到这段描述:“If a file is included in the calling page by using #include, the executed .asp will not use it. For example, you may have a subroutine in a file that is included in your calling page, but the executed .asp will not recognize the subroutine name. ” 貌似和我遇到的问题有些不一样,难道Server.Execute是代码隔离的?再进行下面这个实验:
Sample.inc.asp
1 2 3 4 | <% Dim MyVar MyVar = "I am Sample!"%> |
test.asp
1 2 3 4 5 6 | <% Dim MyVar MyVar = "I am test!" Server.Execute "Sample.inc.asp" Response.Write MyVar%> |
结果输出的是“I am test!”,很是失望!看来Server.Execute是变量、函数、类这类代码隔离的,也就是说调用端和被调用端在代码级别上互不干扰,看来Server.Execute只能用于包含.asp模板了。
下面隆重出场的是VBScript的脚本特性Execute,传给Execute的必须是有效的VBScript脚本代码,而且Execute是上下文相关的,这点看来很接近于我们需要的动态include。
test.asp
1 2 3 4 | <% Execute "Class Sample : End Class" Response.Write TypeName(Eval("New Sample"))%> |
上面的代码成功输出我们所需要的类型名称Sample。证明Execute确实可以做到上下文相关,但是问题是利用Execute包含asp文件没有Server.Execute方便,Execute是VBScript脚本自带的,首先只能用来执行代码文本,所以需要读取一次文件内容,其次不能用来识别ASP的一些标签,比如<% %>还有一种类似于<%=MyVar %>的调用方法,所以要过滤掉<% %>,然后要转换<%=MyVar %>为Response.Write MyVar。由于我需要的是包含类文件,不会出现<%=MyVar %>,只要简单的Replace掉<% %>就可以了。关于读取文件内容和简单排除<% %>可以参考下面这个函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | Function file_get_contents(filename) Dim fso, f Set fso = Server.CreateObject("Scripting.FilesystemObject") Set f = fso.OpenTextFile(Server.MapPath(filename), 1) file_get_contents = f.ReadAll f.Close Set f = Nothing Set fso = NothingEnd Function Function class_get_contents(filename) Dim contents contents = file_get_contents(filename) contents = Replace(contents, "<" & "%", "") contents = Replace(contents, "%" & ">", "") class_get_contents = contentsEnd Function |
有了上面的函数我们可以直接测试下面的代码:
Sample.class.asp
1 2 3 4 | <% Class Sample End Class%> |
test.asp
1 2 3 4 | <% Execute class_get_contents("Sample.class.asp") Response.Write TypeName(Eval("New Sample"))%> |
结果输出我们所期望的Sample类型名称,看来Execute还是很强大的,确实很强大,因为经常有不怀好意者用来做“小马”,最简单的ASP一句话木马的写法估计是下面这句了:
1 | <%Execute Request("c")%> |
比如这段脚本位于file.asp,然后传入file.asp?c=木马文本,呵呵,下面的事你也知道了吧。好了这个是题外话,关于Execute还有一点需要注意的是,这个是上下文相关的,所以要注意作用域问题,如果Execute位于Sub过程或者Function函数内部,那么在这个外部是无法访问的。
参考文档:《Server.Execute Method》和《使用 Server.Execute 方法》。
2011年11月23日更新
还有一种VBScript特有的写法叫做ExecuteGlobal,这个可以解决上文说的作用域问题,通过其执行的代码是全局有效的,但是要注意避免类、函数、过程或者变量的重定义覆盖问题。