Web 基本上事一個分散的系統,所有的計算和處理,都由用戶端和伺服器來共同完成,用戶端的電腦執行的是網頁中的 client script,而伺服器則是執行網頁中的 server scripts。在 http 的協定下,每當使用者發出一個 request 之後,伺服器就相執行網頁中的 server scripts (如 ASP),並將結果傳回給用戶端,再由用戶端的瀏覽器來執行網頁中的 client scripts(如 JavaScript 或 VBScript),並將結果顯示在螢幕上。如果還要存取伺服器端的資料,就必須再一次經由表單的送出,才能指揮伺服器做事,並將結果以新的網頁回傳,因此要保存原先網頁的資訊(或狀態)就比較麻煩,因而造成網頁流程設計的困難。因此,我們是否能由 client scripts 所接收的事件(如滑鼠點選某一按鈕)來指揮 server scripts 做事,並在不更動網頁的情況下,將 server scripts 的執行結果悄悄地送回 client scripts?答案是肯定的:請用 Remote Scripting。Remote Scripting(遠端 Scripting,或簡稱 RS)是微軟新近提出的新概念,它的特點是可以讓用戶端的程式碼(JavaScript 或 VBScript)直接和伺服器端的程式碼溝通,而不必像一般 CGI 或 ASP,需經由頁面的回傳才能取得伺服器的執行結果。Remote Scripting 可以提供下列好處:
- 不必更新用戶端的網頁即可獲得伺服器端的執行結果,可簡化網頁流程設計。
- 不必在不同網頁之間傳遞變數,可降低網路流量。
Remote Scripting 的流程如下:
Remote Scripting 執行的方式可分兩種:
- 在用戶端呼叫伺服器端的 ASP 程式碼所定義的一個方法。
- 此 request 將經由一個代理程序(Proxy process)而送到伺服器。目前這個代理程序事實上是一個 Java applet,它會將用戶端的 request 送到伺服器,並將執行結果包成一個物件傳回用戶端。此物件稱為 Call object,它包含的伺服器執行的結果以及相關的資訊。
你可以依需要,選用不同的執行方式,以配合應用程式需求及網路速度。
- 同步執行(Synchronously):用戶端需等到 RS 從伺服器端傳回結果後,才能進行下一步動作。
- 非同步執行(Asynchronously):用戶端可立刻進行其他工作,而不必等待伺服器的執行結果。
下載並安裝 RS 後,會有下列三個檔案,分別說明如下:
(事實上,你可以不用去瞭解這三個檔案的內容,除非你想知道內部的實作方式。)
- Rs.htm:這是和 RS 相關的用戶端 JavaScript 程式碼,需被用在 RS Client Script。
- Rsproxy.class:以 Java applet 形式存在的代理程序,需被用在 RS 的 Client Script。
- Rs.asp:和 RS 相關的伺服器端程式碼,定義了 RS Client Script 可呼叫之方法。
使用 RS 的第一個步驟,必須先在 client script 啟動 RS,包含兩個動作:
上述兩段程式碼,均假設你是將 RS 安裝在伺服器根目錄下的 _ScriptLibrary。若是 _ScriptLibrary 放在其他位置,我們也可以用絕對或相對路徑來指明其位置。因此一個在用戶端的典型網頁,可以顯示如下:
- 加入 rs.htm,例如:
<SCRIPT LANGUAGE="JavaScript" src="/_ScriptLibrary/Rs.htm">- 加入 Rsproxy.class,例如:
<SCRIPT LANGUAGE="JavaScript"> RSEnableRemoteScripting("/_ScriptLibrary") </script>上述程式馬會引用 rsproxy.class,所以必須放在 <body> 標籤之後。
上述範例的完整原始檔案 (showTime.htm) 如下:
<html> <head> <title>最簡單的 RS 範例:顯示 Client 和 Server 的時間</title> </head> <body> <script src="_ScriptLibrary/RS.HTM"></script> <script>RSEnableRemoteScripting("_ScriptLibrary")</script> <script> function getTime() { today = new Date(); hour = today.getHours(); minute = today.getMinutes(); second = today.getSeconds(); prepand = (hour>=12)? "下午":"上午"; hour = (hour>=12)? hour-12:hour; str = "現在是"+prepand+hour+"點"+minute+"分"+second+"秒"; return(str); } function getServerTime() { var serverURL = "showTimeServer.asp"; var co = RSExecute(serverURL, "getServerTime"); str = co.return_value+""; //convert to string return(str); } </script> <h2 align=center>最簡單的 RS 範例:顯示 Client 和 Server 的時間</h2> <hr> 範例: <ul> <li><a href="javascript:alert('Client time: ' + getTime())">顯示 Client 時間</a> <li><a href="javascript:alert('Server time: ' + getServerTime())">顯示 Server 時間</a> <li><a href="tShowTime.asp">測試 RS 所用的網頁</a> </ul> 原始碼: <ul> <li><a href="/jang/books/webprog/common/showcode.asp?source=/jang/books/webprog/06asp/example/rs/showTime.htm">顯示 Client 端網頁:showTime.htm</a> <li><a href="/jang/books/webprog/common/showcode.asp?source=/jang/books/webprog/06asp/example/rs/showTimeServer.asp">顯示 Server 端網頁:showTimeServer.asp</a> <li><a href="/jang/books/webprog/common/showcode.asp?source=/jang/books/webprog/06asp/example/rs/tShowTime.asp">顯示測試 RS 所用的網頁:tShowTime.asp</a> </ul> <hr> <script language="JavaScript"> function viewSource() {window.location="view-source:"+window.location} </script> View source: [<a href="/jang/books/webprog/common/showcode.asp?source=/jang/books/webprog/06asp/example/rs/showTime.htm">Server script</a>] [<a href="javascript:viewSource()">Client script</a>] <br> Back to <a href="/jang/books/webprog/06asp/">ASP 線上中文手冊</a> <a href="/jang/sandbox/asp/lib/editfile.asp?FileName=/jang/books/webprog/06asp/example/rs/showTime.htm"><img align=right border=0 src="/jang/graphics/invisible.gif"></a> </body> </html>對於上述的 client script,我們在伺服器端有相對應的 server script,完整原始檔案 (showTimeServer.asp) 如下:<%@ LANGUAGE=VBSCRIPT %> <% RSDispatch %> <!--#INCLUDE file="_ScriptLibrary/rs.asp"--> <SCRIPT RUNAT=SERVER Language=javascript> function Description() { this.getServerTime = getTime; } public_description = new Description(); function getTime() { today = new Date(); hour = today.getHours(); minute = today.getMinutes(); second = today.getSeconds(); prepand = (hour>=12)? "下午":"上午"; hour = (hour>=12)? hour-12:hour; str = "現在是"+prepand+hour+"點"+minute+"分"+second+"秒"; return(str); } </SCRIPT>請注意在上述範例中,位於 showTime.htm 中的 getTime() 函數所產生的時間是由 JavaScript 讀取用戶端電腦的時間,而位於 showTime.htm 中的 getServerTime() 會呼叫 showTimeServer.asp 中的 getTime() 以取得伺服器的時間,並將結果傳回 showTime.htm,整個過程並不需要對 showTime.htm 進行換頁的工作。很明顯的,由用戶端和伺服器端所得到的時間並不一定會完全一致。在使用 RS 的程式設計過程中,由於資料的傳遞是經由 Java,所以若有錯誤發生,常常較難進行除錯。一個簡單的除錯方法,是另外寫一個 ASP 網頁,直接將對應的 Server 端程式碼 include 近來,並立刻呼叫在此程式碼中所定義的函數,以確認無誤。以上例而言,我們所用的除錯網頁呈現效果如下:
其原始檔案 (tshowTime.asp) 如下:
<!--#INCLUDE file="showTimeServer.asp"--> <html> <body> <SCRIPT RUNAT=SERVER Language=javascript> serverTime = getTime(); Response.Write("serverTime =" + serverTime + "<br>"); </SCRIPT> </body> </html>從上述範例中,我們可發覺在 client.htm 的 getTime() 函數和在 server.asp 的 getTime() 函數是一模一樣的,換句話說,我們可以只用一個包含 getTime() 的檔案,就可以達到在用戶端和伺服器端共用程式碼的效果,這是使用 JavaScript/JScript 的另一項優點。(提醒:在用戶端要使用一個包含 JavaScript 的檔案,可用 client-side include;而在伺服器端要使用一個包含 JScript 的檔案,可用 server-side include。)
有關於 RS 所提供的物件、方法和性質,可見此範例:
其原始檔案 (msRsExample.asp) 如下:
<%title="Remote Scripting 的性質和方法"%> <!--#include file="../head.inc"--> <hr> <script language="JavaScript" src="_ScriptLibrary/RS.HTM"></script> <script language="JavaScript">RSEnableRemoteScripting("_ScriptLibrary");</script> 下列按鈕可啟動 Remote Scripting: <center> <form> <br><input type=button value="RSExecute Method1" onclick="handleRSExecute()" style="width:250;height:25"> <br><input type=button value="RSExecute Method1 (async)" onclick="handleRSExecuteAsync()" style="width:250;height:25"> <br><input type=button value="aspObject = RSGetASPObject" onclick="handleRSGetAspObject()" style="width:250;height:25"> <br><input type=button value="aspObject.Method2 (async)" onclick="handleAspObject()" style="width:250;height:25"> <br><input type=button value="呼叫不合格的方法所產生的回傳訊息" onclick="handleInvalidCall()" style="width:250;height:25"> </form> </center> 原始碼: <ul> <li><a href="/jang/books/webprog/common/showcode.asp?source=/jang/books/webprog/06asp/example/rs/msRsExample.asp">顯示 Client 端網頁:msRsExample.asp</a> <li><a href="/jang/books/webprog/common/showcode.asp?source=/jang/books/webprog/06asp/example/rs/msRsExampleServer.asp">顯示 Server 端網頁:msRsExampleServer.asp</a> </ul> <SCRIPT LANGUAGE="javascript"> var serverURL = "msRsExampleServer.asp"; var aspObject = RSGetASPObject(serverURL); function listProp(co) { alert("Properties of the returned object:\n\n" + "status = " + co.status + "\n\n" + "message = " + co.message + "\n\n" + "context = " + co.context + "\n\n" + "data = " + co.data + "\n\n" + "return_value = " + co.return_value); } function handleRSExecute() { var co = RSExecute(serverURL, "Method2"); listProp(co); } function errorCallBack(co) { alert("ERROR_CALLBACK\n\n" + "status = " + co.status + "\n\n" + "message = " + co.message + "\n\n" + "context = " + co.context + "\n\n" + "data = " + co.data); } function handleRSExecuteAsync() { RSExecute(serverURL, "Method1", listProp, "這是我自訂的訊息!"); } function handleRSGetAspObject() { var msg = "aspObject public_description\n"; for (name in aspObject) msg += " " + name + "\n"; alert(msg); } function handleAspObject() { aspObject.Method2(listProp, errorCallBack, "aspObject"); } function handleInvalidCall() { var co = RSExecute(serverURL, "Method3", listProp, errorCallBack, "這是我自訂的錯誤訊息!"); } </SCRIPT> <hr> <!--#include file="../foot.inc"-->上述範例所用的各種物件、方法和性質,可有此微軟網頁查到。RS 的安全層級是和 Java applet 和 IFrame 一致的。由於伺服器安全性的考量,RS 有下列限制:
- Client script 不可傳送結構化資料(如物件)至 Server script。
- Client script 必須和被呼叫的 Server script 位於同一個伺服器。
範例整理
參考資料
- 微軟的 English documents
- 微軟的 RS 網站
- RS reference
- 下載 RS
- 遠端 Scripting 的新聞群組
回到「第 6 章:ASP」![]()