最近打算嘗試一下在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),基本上分為以下兩種:
復制代碼 代碼如下:
<!-- #include file="sample.asp" -->
<!-- #include virtual="sample.asp" -->
這兩種基本上大家第一種用得多一些,#include virtual包含的是虛擬路徑,一般虛擬目錄會用得到。但是這兩種都屬於靜態的,如果我們希望是動態包含,但不可以寫成:
復制代碼 代碼如下:
<!-- #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
復制代碼 代碼如下:
<%
Response.Write "Hello World!"
%>
test.asp
復制代碼 代碼如下:
<%
Server.Execute "Sample.inc.asp"
Response.Write "I am test.asp!"
%>
實際輸出應該是“Hello World!I am test.asp!”,說明Server.Execute在動態包含方面可以工作得很好,但是如果我想包含類或者函數呢?接下來做下面這個實驗:
Sample.class.asp
復制代碼 代碼如下:
<%
Class Sample
End Class
%>
test.asp
復制代碼 代碼如下:
<%
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
復制代碼 代碼如下:
<%
Dim MyVar
MyVar = "I am Sample!"
%>
test.asp
復制代碼 代碼如下:
<%
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
復制代碼 代碼如下:
<%
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掉<% %>就可以了。關於讀取文件內容和簡單排除<% %>可以參考下面這個函數:
復制代碼 代碼如下:
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 = Nothing
End Function
Function class_get_contents(filename)
Dim contents
contents = file_get_contents(filename)
contents = Replace(contents, "<" & "%", "")
contents = Replace(contents, "%" & ">", "")
class_get_contents = contents
End Function
有了上面的函數我們可以直接測試下面的代碼:
Sample.class.asp
復制代碼 代碼如下:
<%
Class Sample
End Class
%>
test.asp
復制代碼 代碼如下:
<%
Execute class_get_contents("Sample.class.asp")
Response.Write TypeName(Eval("New Sample"))
%>
結果輸出我們所期望的Sample類型名稱,看來Execute還是很強大的,確實很強大,因為經常有不懷好意者用來做“小馬”,最簡單的ASP一句話木馬的寫法估計是下面這句了:
復制代碼 代碼如下: <%Execute Request("c")%>
比如這段腳本位於file.asp,然後傳入file.asp?c=木馬文本,呵呵,下面的事你也知道了吧。好了這個是題外話,關於Execute還有一點需要注意的是,這個是上下文相關的,所以要注意作用域問題,如果Execute位於Sub過程或者Function函數內部,那麼在這個外部是無法訪問的。
參考文檔:《Server.Execute Method》 和《使用 Server.Execute 方法》 。
2011年11月23日更新
還有一種VBScript特有的寫法叫做ExecuteGlobal,這個可以解決上文說的作用域問題,通過其執行的代碼是全局有效的,但是要注意避免類、函數、過程或者變量的重定義覆蓋問題。