16-1 Application ?拐辣

HTTP 是 stateless 的 connection,換句話說,若無特殊設定,伺服器對每一個 request ,除了 log 記錄外,並不會留下其他特別的記錄,而是對每一個 request 一視同仁。因此,如果我們希望能在不同的 request 之間共享資訊,就必須靠一些特殊的方法來保存或傳送變數的方法,這些方法可以整理如下:

方法溝通對象
Cookies一個用戶端和各個網頁之間的資料共享
Form經由表單來進行網頁間的資料傳遞和保存
Application 物件一個 Web 應用程式和各個用戶端之間的資料共享
Session 物件一個用戶端和各個 Web 應用程式之間的資料共享

在前面幾章的介紹中,我們已經說明了 Cookies 和 表單(Forms)的用法,本節及下節將著重於 Application 和 Session 物件如何用來存放與傳送變數。

對於 Web 伺服器的執行環境而言,一個虛擬目錄(即由用戶端看到 Web 伺服器的第一層目錄)之下的所有 ASP 程式,即構成了一個 Web 的應用程式(Application),來自世界各地的用戶端,都可以經由 Internet 來呼叫同一個應用程式,如果希望這些呼叫同一個應用程式的用戶端能夠「看到」一些共用的資訊,就必須靠 Application 物件。

Application 物件提供四種方法(Methods)、兩個事件(Events)與兩個集合(Collections),列表如下:

Application 物件提供的方法
方法說明
Lock鎖住 Application 物件,不讓其他使用者改變 Application 物件的任何資訊
Unlock解除 Lock 狀態
Contents.Remove(item or index)從 Contents 集合中刪除一個項目
Contents.RemoveAll從 Contents 集合中刪除所有項目

Application 物件提供的事件
事件說明
OnStart啟動一個 Application 物件時所觸發的函數,此函數必須放在 global.asa 檔案
OnEnd結束一個 Application 物件時所觸發的函數,此函數必須放在 global.asa 檔案

Application 物件提供的集合
集合說明
Contents以程式碼加在 Application 物件的所有變數的集合
StaticObjects以 Object 標籤加在 Application 物件的所有物件的集合

我們以訪客計數器來來說明 Application 物件的應用,可分兩部分說明:

  1. 在第一次啟動 Application 物件時,將 Application("Counter") 設定為零。
  2. 在被計數的網頁中,將 Application("Counter") 的值加一。換句話說,只要每次有使用者瀏覽此網頁,Application("Counter") 的值就會加一,其值即代表此網頁被點選的次數。

Hint
Application("Counter") 是 Application.Contents("Counter") 的簡寫。

當伺服器啟動後,在 Web 應用程式(即虛擬目錄下的 ASP 檔案)中,若有任一網頁被點選,即代表相關 Application 物件的啟動,此時 ASP 解譯器會在虛擬目錄下尋找 global.asa 的檔案(其中副檔名 ASA 代表 Active Server Application),並執行此檔案中的 Application_OnStart() 函數。以訪客計數器而言,我們可在 global.asa 內的 Application_OnStart() 函數中,將變數 Application("Counter") 的值預設為零,之後若有計數網頁,就可以將此變數值加 1。此範例的 global.asa 可列出如下:

原始檔(application/global.asa):(灰色區域按兩下即可拷貝)
' Application 物件啟動時該做的事
Sub Application_OnStart()
	Application("Counter") = 0
End Sub

' Application 物件結束時該做的事
Sub Application_OnEnd()
	' Nothing to do here
End Sub

請注意,上述 global.asa 中的程式碼是 VBScript。但事實上,若不使用 global.asa,也可以寫出一個簡單的計數網頁,請見下列範例:

Example(application/pagehit01.asp):

上述範例的原始檔如下:

原始檔(application/pagehit01.asp):(灰色區域按兩下即可拷貝)
<%@language=JScript%>
<%title="使用 Application 物件的計數網頁的完整範例"%>
<!--#include file="../head.inc"-->
<hr>

<%
if (Application("Counter")==null)	// 若 Application("Counter") 不存在,則設定其為 0
	Application("Counter")=0;

function PageHitCounter(){
	Application.Lock();			// 鎖住 Application 物件,不讓其他使用者改變 Application 物件的任何資訊
	Application("Counter")++;	// 計數變數加1
	Application.UnLock();		// 解除 Application 物件的鎖定狀態
	return(Application("Counter"));
}
%>
<h3 align=center>您是第 <font color=red><%=PageHitCounter()%></font> 位貴賓!</h3>

<hr>
<!--#include file="../foot.inc"-->

在上述範例中,Application.Lock 可以鎖住 Application 物件,不讓其他使用者改變 Application 物件的任何資訊,如此可避免兩個同時的 Requests 可能對 Application 造成錯誤動作。

事實上,由於計數器只是一個小程式,發生少許誤差也無所謂,因此上述範例可改寫成更簡單的形式,如下:

Example(application/pagehit02.asp):

上述範例的原始檔如下:

原始檔(application/pagehit02.asp):(灰色區域按兩下即可拷貝)
<%@language=JScript%>
<%title="使用 Application 物件的計數網頁的精簡範例"%>
<!--#include file="../head.inc"-->
<hr>

<% if (Application("Counter")==null)	// 若 Application("Counter") 不存在,則設定其為 0
	Application("Counter") = 0; %>
<h3 align=center>您是第 <font color=red><%=++Application("Counter")%></font> 位貴賓!</h3>

<hr>
<!--#include file="../foot.inc"-->

若希望將上述程式碼反覆用在不同的網頁,而且每一個網頁都有獨立的計數功能,最簡單的方法,就是將 "Counter" 代換為隨網頁而不同的變數,例如網頁的網址 Request.ServerVariables("URL"),例如:

Example(application/pagehit03.asp):

上述範例的原始檔如下:

原始檔(application/pagehit03.asp):(灰色區域按兩下即可拷貝)
<%@language=JScript%>
<%title="獨立功能的計數網頁:以 URL 為 Application 物件的變數名稱"%>
<!--#include file="../head.inc"-->
<hr>

<%
url = Request.ServerVariables("URL");
if (Application(url) == null)
	Application(url) = 0;	// 若 Application(url) 不存在,則設定其為 0
%>
<h3 align=center>您是第 <font color=red><%=++Application(url)%></font> 位貴賓!</h3>

<hr>
<!--#include file="../foot.inc"-->

如果要知道計數器的啟動時間,就可以在計數變數為 Null(用於 JScript)或 Empty(用於 VBScript)時,將時間以另外一個 Application 變數記錄下來,如下例:

Example(application/pagehit04.asp):

上述範例的原始檔如下:

原始檔(application/pagehit04.asp):(灰色區域按兩下即可拷貝)
<%@language=JScript%>
<%title="顯示計數器啟動時間的範例"%>
<!--#include file="../head.inc"-->
<hr>

<%
url = Request.ServerVariables("URL");
theStartTime = "startTime: " + url;
if (Application(theStartTime) == null){	// 伺服器起動後,本網頁第一次被呼叫時的動作
	Application(url) = 0;
	now = new Date();
	Application(theStartTime) = now.toLocaleString();
}
%>
<h3 align=center>從 <font color=green><%=Application(theStartTime)%></font> 以來,您是第 <font color=red><%=++Application(url)%></font> 位貴賓!</h3>

<hr>
<!--#include file="../foot.inc"-->

Hint
無論是使用 JScript 或 VBScript,都可以存取到相同的 Application 物件與變數。

上述範例是較簡單的網頁點選計數方法,但有下列缺點:

  1. 用戶端只要一再點選瀏覽器的「檢視/重新整理」(View/Reload)或是 F5 按鈕,計數器就會一直累加,所以不是很準。
  2. 伺服器重開機時,Application 物件會被清除,因此所有的計數資料就不見了,一切歸零。
要解決第一個問題,可用 Session 物件,將在下節介紹。要解決第二個問題,則要將計數資料寫入檔案,後面再詳細介紹。

Application.Contents 和 Application.StaticObjects 都是 Dictionary 變數,所以都可以直接用程式碼將其內容一一印出,例如:

Example(application/listAppVar01.asp):

上述範例的原始檔如下:

原始檔(application/listAppVar01.asp):(灰色區域按兩下即可拷貝)
<%@language=JScript%>
<% title = "印出 Application.Contents 和 Application.StaticObjects 的內容" %>
<!--#include file="../head.inc"-->
<hr>

<!--#include file="../listdict.inc"-->
<p><% listdict(Application.Contents, "Application.Contents"); %>
<p><% listdict(Application.StaticObjects, "Application.StaticObjects"); %>

<hr>
<!--#include file="../foot.inc"-->

我們可以使用 Application.Contents.Remove() 或 Application.Contents.Removeall() 來刪除 Application 變數,範例如下:

Example(application/listAppVar02.asp):

上述範例的原始檔如下:

原始檔(application/listAppVar02.asp):(灰色區域按兩下即可拷貝)
<%@language=JScript%>
<% title = "刪除所有 Application 變數後,再印出 Application.Contents 和 Application.StaticObjects 的內容" %>
<!--#include file="../head.inc"-->
<hr>

<% Application.Contents.Removeall()%>
<!--#include file="../listdict.inc"-->
刪除所有 Application 變數後,再進行列表:
<p><% listdict(Application.Contents, "Application.Contents"); %>
<p><% listdict(Application.StaticObjects, "Application.StaticObjects"); %>

<hr>
<!--#include file="../foot.inc"-->

和 Application 變數相關的常見應用如下:

  1. 網頁記數器。
  2. 線上投票區。
  3. 更新正確上線人數(如聊天室)。

JScript 程式設計與應用:用於伺服器端的 ASP 環境