WebSnap 控件組簡介
Jimmy Tharpe(翻譯:天津財經學院->葉明)
WebSnap提供的豐富接口使得常用功能的實現變得易如反掌,例如,WebSnap提供的IwebUserList接口不但可以跟蹤用戶名、口令、權限和顯示名稱,而且可以對內存中的數據進行基本的跟蹤。如果我們需要開發數據庫應用呢?通過再實現(re-implementing)IwebUserList接口同樣可以實現,例如我在例子中使用的IwebUserList組件,它同WebSnap 組中的其他控件具有很好的集成性。
下面,我將和大家一同感受WebSnap的強大功能,我們將再實現一些接口,以獲得一些還未經公開的功能特性。
TDBWebUserList
為創建一個具備數據庫功能的萬維網用戶列表,必須實現IwebUserList接口。
IWebUserList = interface
[{0877DEAF-AB5D-11D4-A503-00C04F6BB853}]
function ValidateUser(Strings: TStrings): Variant;
function CheckAccessRights(UserID: Variant; Rights: string): Boolean;
end;
如果我們仔細研究WebSnap對這個接口的實現,我們就會發現存在一個不足之處:無法取得用戶的顯示名稱!唉!我們就此放棄了這一功能嗎?當然不會!我們可以通過簡單的創建一個繼承接口IudWebUserList達到目的:
IudWebUserList = interface(IWebUserList)
[{0C7E6E80-3F82-47C6-B37E-04BEA4FAEE4A}]
function UserDisplayName(AUserID: variant): string;
end;
呵呵,現在我們有了一個實現的接口,正如WebSnap所要求的,它仍然可以保證良好的兼容性。我們每當需要放置一個接口,只需要對它進行實現,這在WebSnap中通過接口很容易做到。
增加緩沖區
我曾經在社區中征求大家對TDBWebUserList的意見,Graham Colwell建議我為登錄的用戶增加緩沖區以便改善系統的運行速度,這樣,每當有人請求關於用戶的信息(比如權限信息),系統就沒有必要頻繁的訪問和更新數據庫了。這個解決方案看起來很簡單:只需要創建一個登錄用戶的列表,然後改為從列表中讀取數據就可以了。但是,這又帶來的另一個問題:我的組件怎樣才能獲知用戶從網絡中退出以便從緩沖區中刪除他的記錄呢?
我仔細研究了博覽的文檔(也包括博覽的源碼),卻沒有找到相關的解決方案,於是我開始浏覽WebSnap的源碼,在那裡我找到了答案:InotifyWebActivate!InotifyWebActivate接口在激活和停用WebSnap應用的時候通知它的調用者。InotifyWebActivate接口是這樣定義的:
INotifyWebActivate = interface
[{CE18BE42-1358-11D4-ABF4-F18FFAD12B3C}]
procedure NotifyActivate;
procedure NotifyDeactivate;
end;
在實現了這個未公開的接口之後,我的組件就可以獲知WebSnap應用的激活和停用了。好,在找到這個接口之後,剩下的工作只是一個檢查用戶是否退出系統的例行程序,如果用戶已經退出系統,就從緩沖區中刪除他的記錄。
TudEndUserSessionAdapter
將TudEndUserSessionAdapter組件加入WebSnap是為了獲得TDBWebUserList的IudWebUserList接口並修正EndUser.DisplayName錯誤(TendUserSessionAdapter顯示的是用戶名而不是顯示名稱)。
為此,我研究了TendUserSessionAdapter,發現用戶名是存儲在一個Session對象中,而顯示名稱卻沒有。為獲得顯示名稱,只需做如下修改:為顯示名稱設置一個新的Session變量,當腳本請求顯示名稱的時候,讀取這個session對象並返回這個值。
function TudCustomEndUserSessionAdapter.GetUserName: string;
begin
if WebContext <> nil then
Result := WebContext.Session.Values[sUserName];
if Result = then
Result := UserID;
end;
很簡單,不是嗎?所以WebSnap並不難學。
TudLoginFormAdapter
TudLoginFormAdapter是對WebSnap部分最簡單的擴充,它只是統計存在的TloginFormAdapter並輸出NextPage屬性,這就是它的全部源代碼:
type
TudLoginFormAdapter = class(TLoginFormAdapter)
published
property NextPage;
end;
NextPage 屬性允許你設置一旦用戶登錄將轉向哪裡,例如,你也許想把管理員轉向管理頁面,而將客戶轉向技術支持頁面,轉向哪裡取決於用戶的權限。下面是一個例子:
procedure TLoginPage.udLoginFormAdapterLogin(Sender: TObject; UserID: Variant);
begin
if HomePage.DBWebUserList.CheckAccessRights(UserID, admin) then
udLoginFormAdapter.NextPage := AdminPage.Name
else if HomePage.DBWebUserList.CheckAccessRights(UserID, user) then
udLoginFormAdapter.NextPage := UserPage.Name
else
udLoginFormAdapter.NextPage := VisitorPage.Name;
end;
當然,因為NextPage屬性是輸出的,你可以在設計程序的時候為它賦值。
TudStringsValuesList
介紹TudStringsValuesList有兩個理由:修正博覽的TstringsValuesList錯誤(OnPrepareStrings事件從未調用)和在Delphi中提供對在迭代腳本中當前字符串的訪問。即使這意味著完全的重寫,也並不是十分困難——只需要簡單的復制/粘貼,對代碼做幾處很小的改動。
修正錯誤 修正OnPrepareStrings錯誤很簡單,先讓我們來看看博覽的TstringsValuesList源碼摘錄:
procedure TCustomStringsValuesList.SetStrings(const Value: TStrings);
begin
FStrings.Assign(Value);
end;
function TCustomStringsValuesList.GetStrings: TStrings;
begin
if not (csLoading in ComponentState) and not StringsPrepared then
PrepareStrings;
Result := FStrings;
end;
procedure TCustomStringsValuesList.PrepareStrings;
begin
StringsPrepared := True;
if Assigned(OnPrepareStrings) then
OnPrepareStrings(Self);
end;
SetStrings和GetStrings方法是為了控制TstringsValuesList的Strings屬性。如果你研究一下GetStrings方法,你將發現它只有當strings沒有准備好並且組件沒有被加載的時候才調用PrepareStrings方法
理想的情況是這:當首次需要strings的時候,PrepareStrings應該被調用,當請求被停用的時候,ImplNotifyDeactivate應該將StringsPrepared設回為否,所以當再次請求時PrepareStrings將再次被調用,然而,事實卻不是這樣,理由如下:
function TCustomStringsValuesList.ImplGetListName: string;
begin
if FIndex < FStrings.Count then
Result := FStrings.Names[FIndex]
else
Result := ;
end;
function TCustomStringsValuesList.ImplGetListValue: Variant;
var
S: string;
begin
if FIndex < FStrings.Count then
begin
S := FStrings.Names[FIndex];
if S <> then
Result := FStrings.Values[S]
else
Result := FStrings[FIndex]
end
else
Result := Unassigned;
end;
function TCustomStringsValuesList.ImplNextIteration(
var OwnerData: Pointer): Boolean;
begin
Inc(FIndex);
Result := FIndex < FStrings.Count;
end;
當腳本需要使用 strings的時候,代碼訪問的是內部Fstrings變量,從而“騙過”了GetStrings方法!唉!為了解決這個問題,只需將所有涉及Strings的方法中對Fstrings的首次調用由Fstrings改為Strings就可以了。
增加CurrentIteration屬性CurrentIteration屬性的作用也許你也能猜出來,在Delphi的代碼中,你的頁面腳本所在位置就是它的current iteration,這個功能很容易實現,在迭代的實現方法中,分配返回的字符串:
function TudStringsValuesList.ImplStartIterator(var OwnerData: Pointer):Boolean;
begin
FIndex := 0;
Result := FIndex < Strings.Count;
if Result then
FCurrentIteration := Strings[FIndex];
end;
function TudStringsValuesList.ImplNextIteration(var OwnerData:Pointer): Boolean;
begin
Inc(FIndex);
Result := FIndex < Strings.Count;
if Result then
FCurrentIteration := Strings[FIndex];
end;
如果迭代成功,結果為真,如果結果為真,我們將CurrentIteration屬性的值設為迭代的值。
結束語
WebSnap就象一個萬維網上的機器,如果你有機會,請打開它的引擎蓋,看看博覽的做工是多麼出眾,如果你看到需要改進的地方,請動手完善我們的WebSnap,我想這也正是設立這個社區的目的吧!