ASP.NET架構代表着在IIS和ISAPI程式設計模型之上的一個重要的生産力層. 如果你熟悉ASP.NET開發的話, 你就會知道它為你的應用程式邏輯編寫托管代碼提供了便利, 比如說C#, VB.NET, 并且允許你在由Microsoft Visual Studio提供的面向生産力的可視化編輯器中工作. ASP.NET架構還提供了許多其他有價值的抽象, 來幫助開發人員進行狀态管理, 資料綁定, 導航 以及資料緩存.
ASP.NET架構是由一個叫做aspnet_isapi.dll的ISAPI extension實作的. ASP.NET的基礎配置涉及: 在IIS站點或虛拟目錄的層次, 為ASP.NET的檔案字尾注冊映射的應用程式, ASP.NET的檔案字尾包括.aspx, .ascx, 和.asmx. 當IIS看到一個指向這些檔案類型的請求的時候, 他會把這個請求送給aspnet_isapi.dll, 讓它來處理, 這個dll會高效地将控制轉移給ASP.NET架構. 是以說, ASP.NET架構處理一個請求的方式很大程度上取決于目标檔案的檔案類型(擴充名).
ASP.NET架構像一個 獨立的ASP.NET應用程式一樣地執行指向IIS站點和虛拟目錄的請求. 在每一個ASP.NET應用程式的身後, 都有一個包括一堆檔案的根目錄. 這種架構保證了ASP.NET應用程式的部署是一種非常簡單的x-copy風格. 你可以簡單地在Web伺服器上建立一個新的虛拟目錄, 然後拷貝你的ASP.NET應用程式的檔案到根目錄下. 當然, 有一點小麻煩的是在Web場環境中建立虛拟目錄, 然後将同樣的檔案部署到場中的所有前端伺服器上.
每個ASP.NET應用程式都可以在它的根目錄下被獨立地配置 web.config檔案. web.config檔案是一個基于xml的檔案, 包含許多元素, 用于控制ASP.NET架構中的各種各樣的feature的行為, 比如說編譯啦, 頁面渲染啦, 狀态控制了什麼的. 一個簡單的web.config檔案看起來像下面這樣:
<?
xml
version
=
"1.0
" encoding
=
"utf-8
" ?>
<
configuration
>
<
system.web
>
<
customErrors
mode
=
"On
" />
<
httpRuntime
maxRequestLength
=
"51200
" />
<
authentication
mode
=
"Windows
" />
<
identity
impersonate
=
"true
" />
<
authorization
>
<
allow
users
=
"*
" />
</
authorization
>
</
system.web
>
</
configuration
>
注意到ASP.NET架構在運作每一個ASP.NET應用程式的時候, 都有一定程度的隔離性. 即使是在你已經配置了多個ASP.NET應用程式運作在同一個IIS Application Pool中的時候, 也有隔離性. ASP.NET架構提供了運作在同一個IIS工作者程序中的對多個ASP.NET應用程式之間的隔離, 隔離方式是把它們分别加載到分開的.NET Framework AppDomain中.[學友注: 關于AppDomain, 可以參考文章走近.NET AppDomain ]
ASP.NET頁面
===========
頁面(page)是ASP.NET架構中最有價值的抽象之一. 開發者建構ASP.NET應用程式, 最典型的方式是在Visual Studio的可視化設計界面上拖拽伺服器控件, 和通過标準的屬性單來修改頁面和控件的屬性. ASP.NET架構和Visual Studio還使得向頁面中添加處理邏輯相對更加簡單了, 達到這個目的的方式是在頁面級上的響應事件中和在控件級的事件中通過編寫托管代碼.
在實體層面, ASP.NET應用程式中的一個頁面是一個以.aspx字尾結尾的, 存儲在Web伺服器上的檔案, 在它被用戶端請求時, 它會被編譯為一個dll檔案. 考慮下面的aspx頁面定義, 它包含一個伺服器端控件和一個簡單的事件處理程式.
<%
@
Page
Language
="C#"
%>
<
script
runat
="server">
protected override void
OnLoad
(EventArgs
e
) {
lblDisplay
.Text
= "Hello, ASP.NET"
;
}
</
script
>
<
html
>
<
body
>
<
form
id
="frmMain"
runat
="server">
<
asp
:
Label
runat
="server"
ID
="lblDisplay" />
</
form
>
</
body
>
在背景, ASP.NET架構做了不少工作來把.aspx檔案編譯成為一個dll檔案. 首先, 它必須先将aspx檔案解析并生成一個C#(或VB.NET)的源檔案, 源檔案中包含一個繼承自Page類的公有類, Page類是定義在System.Web.UI命名空間下的, 存在于system.web.dll這個程式集中. 當ASP.NET頁面解析器生成這個繼承自Page的類時, 它建立一個控件的樹形結構, 包括頁面中定義的所有的伺服器端控件. 頁面解析器同時添加需要的代碼來勾住頁面中定義的任何的事件處理程式.
一旦ASP.NET頁面解析器建造好了.aspx的源檔案, 它就開始編譯這個源檔案成為一個DLL. 這個編譯自動發生在這個.aspx頁面第一次被請求的時候. 一旦ASP.NET運作時編譯好了aspx檔案的DLL, DLL的一份拷貝會被用來為所有後來的指向同樣aspx檔案的請求服務. 然而, ASP.NET運作時會監視DLL的時間戳, 如果它發現與DLL相關聯的aspx檔案被更新了, 它就會重新編譯來重建那個DLL.
ASP.NET架構如此流行的原因之一, 就是因為它友善的伺服器端控件. 使用與ASP.NET架構一同release的開箱即用(out-of-box)的控件建立頁面非常容易.控件有很多, 比如validation控件, calendar控件, 還有支援資料綁定的控件, 比如GridView控件還有Repeater控件. 還有呢, 開發者創作自定義的控件并把它用在頁面裡也是相對容易的.
母版頁
================
ASP.NET 2.0引入了母版頁(master page), 它提供了一種非常高效的建立頁模闆的方式. 特别地, 一個母版頁定義了可以在許多不同頁面間使用的通用元素(比如說頂部橫幅top banner), 還有站點導航控件. 在母版頁中定義的布局可以被很多跟這個母版頁連結到一起的頁面使用. 在ASP.NET技術中, 與一個母版頁連結到一起的頁面叫做content page(内容頁). 母版頁與内容頁的基本關系在下面可以看到圖示:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiInBnaugDO1QWZwEGNhVzN20CNmFmYtQ2YzQTLkBzMi1SZkFmNiVDOl9lMw0SMz0yNxAjMlQjMtETMtkDMwIzLclzNGF0XUVkTuA1UBRnbp9GUlJXYoNFdul2bQVmchh2UvwlclRXaydVZ2lGTzd3bk5WaX9CXwRXYwdXYvwVbvN2Xzd2bsJmbj9CXt92YuM3ZvxmYuNmLzV2Zh1Wavw1LcpDc0RHaiojIsJye.jpg)
舉例, 假設你希望建立一個母版頁, 用來定義一個HTML布局, 這個布局包括一個橫幅在頁的頂部. 你應該建立一個字尾名為.master的檔案作為開始, 就叫default.master吧. 下一步你應該在頁面的頂部添加一個@Master指令. 然後, 在下面你定義頁面的HTML布局, 添加并命名placeholders, 就如下面的例子一樣:
<%
@
Master
%>
<
html
>
<
body
>
<
form
id
="frmMain"
runat
="server">
<
table
width
="100%">
<
tr
>
<
td
>
<!-- Display Litware Banner -->
<
h1
>
Litware Inc.</
h1
><
hr
/>
</
td
>
</
tr
>
<
tr
>
<
td
>
<!-- Display Main Body of Page -->
<
asp
:
contentplaceholder
id
="PlaceHolderMain"
runat
="server" />
</
td
>
</
tr
>
</
table
>
</
form
>
</
body
>
</
html
>
當你想建立一個内容頁時, 你應該建立一個.aspx檔案, 然後添加一個@Page指令, 其中包括MasterPageFile屬性. 一旦你決定你想要取代母版頁中的哪一個命名的placeholder, 你就為這個取代操作定義一個Content元素. 接下來是一個簡單的内容頁連結起一個母版頁的例子, 代碼中取代了母版頁中的名為PlaceHolderMain的placeholder.
<%
@
Page
Language
="C#"
MasterPageFile
="~/default.master"
%>
<
script
runat
="server">
protected override void
OnLoad
(EventArgs
e
)
{
lblDisplay
.Text
= "Hello World"
;
}
</
script
>
<
asp
:
Content
ID
="main"
Runat
="Server"
ContentPlaceHolderID
="PlaceHolderMain" >
<
asp
:
Label
ID
="lblDisplay"
runat
="server" />
</
asp
:
Content
>
注意當你建立了一個内容頁并指向了一個母版頁了的時候, 所有你想添加的的HTML必須寫在指向某個名字的placeholder的Content元素中. 如果你在Content元素之外寫了HTML代碼或者是伺服器端控件, 那麼這個頁面就不會被編譯. 但是, 如同你在之前的例子中看到的, 你可以在Content元素之外添加一個腳本塊, 并在腳本塊中添加任何你想添加的代碼.
當一個母版頁定義了一個命名了的placeholder, 你并不需要一定總是在你的内容頁中取代它. 因為, 母版頁建立帶有的預設内容的placeholder. 任何指向那個母版頁的内容頁, 隻要不包括那個命名了的placeholder, 就會得到母版頁中定義的預設内容. 另一個連接配接到同樣母版頁的内容頁, 并且其中包含了那個命名的placeholder, 會用自己的自定義内容取代預設内容.
最後, 注意不管是誰, 隻要他建立了母版頁, 那他就得決定placeholder的名字, 還要決定哪個包含什麼樣的預設内容. 這對于設計WSS站點的頁面時很重要, 因為你建立的内容頁将會指向由WSS團隊建立的母版頁. 在這種情況下, 你必須學會WSS團隊定義了什麼placeholder, 還有哪些種類的内容是可以取代的.
HTTP請求的pipeline
================
在面向高産的頁面和伺服器端控件架構之下, ASP.NET架構為想要在更深一個層次的開發者暴露了HTTP請求Pipeline. 他給開發者提供了可以跟ISAPI程式設計模型在某種程度相比的控制. 然而, 當你為HTTP請求pipeline建立元件的時候, 你卻可以使用如C#或VB.NET一樣的托管代碼. 你還可以使用由ASP.NET提供的API, 這可比直接使用ISAPI程式設計模型容易多了.
下圖展現了HTTP request pipeline和它的三個可以替代的元件類型: HttpHandler, HttpApplication, 和HttpModule. 當請求進來的時候, 他們被排入隊列, 并被賦給一個工作者線程, 然後通過與這三個元件類型的互動來處理這個請求.
任何請求的最後的終點, 你可以在圖中看到, 是HttpHandler類, 它繼承了IHttpHandler接口. 作為一個開發者, 你可以建立一個自定義的HttpHandler元件, 然後通過在web.config中添加設定元素來把你的HttpHandler插入到HTTP request pipeline中.
Http Request Pipeline在HttpHandler之前放置了一個HttpApplication元件. 在一個應用程式範圍的基礎上, 進來的請求在到達HttpHandler之前, 總是通過HttpApplication來路由的. 是以, 給了HttpApplication一個不管最後被路由到哪個HttpHandler, 它總能預處理任何請求的能力. 這個預處理階段是通過一系列的在HttpApplication中定義的事件來控制的, 比如說BeginRequest, AuthenticateRequest, 和AuthorizeRequest.
在你不想使用自定義的HttpApplication元件的情形下, ASP.NET使用一個标準的HttpApplication對象來初始化Http Request Pipeline. 然而, 你可以通過建立一個叫做global.asax的檔案, 把它放到ASP.NET應用程式的根目錄下來取代這個标準的HttpApplication元件. 舉個例子, 你可以建立一個看起來像下這樣的global.asax:
<%
@
Application
Language
="C#"
%>
<
script
runat
="server">
protected void
Application_AuthenticateRequest
(object
sender
, EventArgs
e
) {
// your code goes here for request authentication
}
protected void
Application_AuthorizeRequest
(object
sender
, EventArgs
e
) {
// your code goes here for request authorization
}
</
script
>
HTTP Request Pipeline中可以替換的第三個元件類型是HttpModule. HttpModule跟HttpApplication元件相似, 它被設計用來處理HttpApplication類定義的事件, 并且這些事件的處理發生在請求被傳送給任何一個HttpHandler類型之前. 比方說, 你可以建立一個自定義的HttpModule元件來處理請求層次的時間, 比如說BeginRequest, AuthenticateRequest, 和AuthorizeRequest. 跟HttpHandler一樣, HttpModule類是通過一個接口來定義的. 你可以建立一個實作IHttpModule接口的類, 然後通過在web.config檔案中添加配置元素來把你的HttpModule插入到Http Request Pipeline中.
鑒于HttpApplication元件可以被定義為像帶着.asax字尾的文本檔案一樣的簡單, 自定義的HttpModule元件總是會被程式集DLL中的類一樣被編譯. 要添加一個自定義的HttpModule元件到Http Request Pipeline中, 你就得在web.config檔案中添加入口.
盡管HttpApplication和HttpModule元件在所做的事情上非常相似, HttpModule包含一些值得注意的不同. 首先, 不像HttpApplication元件那樣必須在一個應用程式中隻添加一個HttpApplication元件, 對于HttpModule元件, 你的添加沒有數量上的限制. ASP.NET的web.config檔案可以添加多個不同的HttpModule元件. 第二, HttpModule元件可以被設定在機器的水準上. 事實上ASP.NET架構附帶了好幾個不同的HttpModule元件, 他們被自動的配置在機器機上運作, 來為ASP.NET提供諸如Windows authentication, Forms Authentication和輸出緩存等功能.
對于HTTP Request Pipeline 我們要讨論的最後一個元件是HttpContext. 随着ASP.NEt初始化一個請求并發送給HTTP Request Pipeline, 它還用HttpContext類建立一個對象, 并使用重要的上下文相關的資訊來初始化這個對象.
從時間的角度來說, 在ASP.NET在HTTP Request Pipeline中的任何自定義的代碼有機會執行之前, HttpContext對象就已經被建立出來了, 認識到這一點很重要. 這意味着你永遠可以使用HttpContext對象和它的子對象進行程式設計, 比如說Request, User, 和Response. 無論何時你開發一個用來在Http Request Pipeline中運作的元件, 你都可以寫像下面這樣的代碼:
HttpContext
currentContext
= HttpContext
.Current
;
string
incomingUrl
= currentContext
.Request
.Url
;
string
currentUser
= currentContext
.User
.Identity
.Name
;
currentContext
.Response
.Write
("Hello world"
);
<Inside Microsoft Windows SharePoint Services 3.0>