asp.net 有一群說著同樣語言的人,計劃修一座高聳入雲的高塔直達天庭,以證明族群團結的力量,塔很快就初具規模。這下可驚動天庭的神,他想這人和神都成鄰居了,還怎麼去統治人類?於是便施魔法擾亂人們的語言,使他們無法溝通,於是高塔再也無法繼續修建。
■ 明明白白我的心 - 人機互動(Human-Computer Interaction)
部落格園裡有一篇《給媽媽寫程式》感人肺腑,說的是作者常常為了指導50出頭的媽媽完成COPY之類的操作耗盡心思,最後為其定做一個只需點擊兩下的程式,由此推出UI設計的重要性。誠然,就使用者而言軟體等於N個使用者介面。他們中的大多數壓根不關心裡頭有多少令開發人員陶醉的所謂進階技術,簡單、快捷、美觀是他們要求的全部。人機互動的研究領域就在搗騰這些看似瑣碎的東西。
在系統設計中,需要劃分自動化系統邊界,即把系統劃分成手工操作和系統自動兩個部份,兩者通過使用者介面(User Interface)完成對接,如圖8-1。使用者介面完成系統的輸入輸出工作:收集使用者觸發事件及相關資料,傳遞給系統內部進行處理並接收處理結果,可視化處理結果。在企業級的c/s應用中,aspx及其後台編碼檔案功能單純得只剩下一個--完成人機互動。一個漂亮的網頁不是使用者介面設計的全部內涵,它更應該象一位面容姣好、語音甜美的接待者,輕聲細語地詢問使用者需求,聆聽使用者雜亂無章的訴說,耐心指導使用者完成操作流程,寬容使用者錯誤,準確為使用者送上最終結果--"您的賬戶裡只剩0.4元,無法完成支付操作。"總之一句話,要充分認識和領會白居易同志寫詩的偉大精神,讓50出頭的媽媽愉快地完成人機互動。
■ 人動則影動 - ASP.NET的靜態模型
在ASP.NET架構中,伺服器端的ASP.NET頁面對應用戶端的HTML頁面,包含一個互動Web Form,繼承於FrameWork的Page類。ASPX檔案是人,HTML編碼臨時檔案是影,人動則影動,兩者合成完整意義上的使用者介面。使用者對影進行各種操作,系統通過人進行相應處理,"request-response"回饋機制完成兩者的映像過程,如圖8-2。
HTTP是一個無狀態協議,所以WEB伺服器是位打個磕睡就忘事的先生,當響應發送後將丟棄所有請求資訊。這事必會影響應用程式的可用性,如因為資料驗證錯誤而要求使用者重新輸入幾十個輸入框的所有資訊,連媽媽的指頭都會發出抗議的聲音。為了彌補這個缺陷,ASP.NET使用檢視狀態(ViewState)來完成狀態保持。原理很簡單,伺服器將每次HTTP請求中的頁面資料寫入產生的HTML檔案中,下次頁面提交時一併帶上,如下例。在ASP時代我們曾用<input type="hidden">手工實現,而在.Net中您會發現HTML原始碼會自動產生一個 _VIEWSTATE標籤,其值即為系統儲存的頁面資料。
<%@ Page Language="C#" %>
<script runat="server">
protected override void OnLoad(EventArgs e){
int val = int.Parse(_sum.InnerText);
_sum.InnerText = (val+1).ToString();
base.OnLoad(e);
}
</script>
<html><body><form runat="server">
<span id="_sum" runat="server">0</span>
<input type="submit" />
</form></body></html>
ViewState減少了不少麻煩,但也存在問題。在預設情況下它將被啟用,即使沒有任何用處也去收集所有頁面資訊並穿梭往返於C/S兩端,其用base 64編碼的龐大身軀常吞噬大量頻寬,並存在被脅持的可能而引起系統安全性問題。
使用者會對介面做出各種操作,並希望得到系統的回應,這些使用者介面的操作被.Net統稱為事件(event,系統內部也會產生事件),而對應的系統回應稱為事件處理。在《隨想四》中的Login頁面,當使用者點擊btnLogin按鈕後, btnLogin對象(這時它被稱為事件來源)大叫:"我被點啦,十年了,我終於第一次被點擊啦,555~~~"但Button類本身並未定義相應的事件處理常式,那麼又是誰來完成系統的實際響應操作呢?
在編寫Login類時我們已經定義成員btnLogin_Click方法,並希望由此完成btnLogin.click事件的處理,但MS工程師在定義Button類時並不知道其作用,所以需要一個媒介將兩者串連起來,這時我們不禁回想起《隨想七》中的委託。.net用EventHandler委託作為串連事件來源與事件處理常式的媒介,將兩者綁定:
// 該段程式為《隨想四》中Login.aspx.cs的程式碼片段並有所修改
// EventHandler為System命名空間中定義的公用委託
// sender為事件來源對象,EventArgs類負責收集事件數目據
public delegate void EventHandler(object sender,EventArgs e);
// Login為受託類,完成事件處理定義和連結
public class Login : System.Web.UI.Page{
// Login.OnInit 為Page類受保護方法,執行建立和設定執行個體所需的初始化步驟
// 調用OnInit方法時引發 Init事件
override protected void OnInit(EventArgs e){
// new System.EventHandler產生一委託執行個體
// += 完成事件處理委託的串連過程,同一事件可以觸發多個處理常式,稱為多路廣播
// base.OnInit(e)用於調用父類的同名方法
this.btnLogin.Click += new System.EventHandler(this.btnLogin_Click);
base.OnInit(e);
}
private void btnLogin_Click(object sender, System.EventArgs e){……}
}
■ 苦旅 - ASP.NET的動態模型
每一個HTTP請求出發前可謂悲壯,壯士一去兮不複返!Login.aspx頁面請求將URL和相關資料打包背上,胸前掛著自己的IP地址身份牌,從用戶端瀏覽器出發,翻越電信網通人為製造的各種障礙,爬雪山過草地,步履蹣跚地走到Web伺服器的IIS門前,正想狂喜一陣,當看到大門口排著長長的數以萬計的請求隊列,還常有些餓死骨無人理彩時,它終於倒下了。
矇矓中Login.aspx頁面請求被傳送到HttpRuntime執行個體,HttpRuntime執行個體將:
·擷取一個HTTP Application 對象;
·讓HTTP Application 對象讀取設定檔;
·由HTTP Module 執行個體們分別提供會話維護、驗證或設定檔維護等各項服務;
·HTTP Handler介面執行個體化具體Page類實現處理。
Login是HTML頁面生產車間的工人,每天他負責按照嚴格的標準和固定的流程為客戶製作HTML編碼的Login.aspx頁面。一天早上,login正在硬碟裡愜意地喝著咖啡,身寬體胖的主管HttpHandler大叔通知他立即開工。
今天是第一次進入記憶體,所以他先準備好頁面構架和所有控制項零件並設定其預設狀態;然後嚴格按OnInit方法完成Init事件;之後會在頁面請求中尋找_VIEWSTAT。如果找到就對資料進行讀取和解碼;並讓控制項更新其狀態以準確反映用戶端相應元素狀態; 隨後他拿出OnLoad方法操作手冊來處理Load事件;然後應付一系列被觸發的頁面事件,如果頁面正在被回送,還會包括使用者觸發的事件;用onPreRender方法處理的PreRender事件可以改變提交頁面的方式;然後把當前的頁面狀態儲存到新的檢視狀態中。
完成所有操作後即可產生HTML編碼檔案,期間可通過覆寫(override)Render方法以附加一些HTML代碼,為頁面做最後的修飾。
最後Login釋放所有佔用的檔案、繪圖物件、資料庫連接等關鍵資源,匆忙跑出記憶體,"真是太悶了!"出門時他小聲地嘀咕了一聲。