導讀:
在Internet時代的開端,用戶端的需求非常有限;.htm檔案就可以滿足他們的需求。但是,随着時間的流逝,用戶端需求的擴充超越了.htm檔案或靜态檔案所包含的功能。
開發者需要擴充或擴充Web伺服器的功能。Web伺服器廠商設計了不同的解決方案,但是都遵循同一個主題“向Web伺服器插入某些元件”。所有的Web伺服器補充技術都允許開發者建立并插入元件以增強Web伺服器的功能。微軟公司提出了ISAPI(Internet伺服器API),網景公司提出了NSAPI(網景伺服器API)等等。
ISAPI是一種重要的技術,它允許我們增強與ISAPI相容的Web伺服器(IIS就是一種與ISAPI相容的Web伺服器)的能力。我們使用下面的元件達到這個目的:
ISAPI擴充
ISAPI過濾器
ISAPI擴充是使用Win32動态連結庫來實作的。你可以把ISAPI擴充看作是一個普通的應用程式。ISAPI擴充的處理目标是http請求。這意味着你必須調用它們才能激活它們。 你可以認為ISAPI過濾器僅僅就是一個過濾器而已。用戶端每次向伺服器送出請求的時候,請求要經過過濾器。用戶端不需要在請求中指定過濾器,隻需要簡單地把請求發送給Web伺服器,接着Web伺服器把請求傳遞給相關的過濾器。接下來過濾器可能修改請求,執行某些登入操作等等。
由于這些元件的複雜性,實作它們非常困難。開發者不得不使用C/C++來開發這些元件,但是對于很多人來說,使用C/C++進行開發簡直就是痛苦的代名詞。
那麼ASP.NET提供什麼東西來實作這些功能呢?ASP.NET提供的是HttpHandler(HTTP處理程式)和HttpModule(HTTP子產品)。
在深入了解這些元件的詳細資訊之前,了解一下http請求經過HTTP子產品和HTTP處理程式的時候的處理流程是有價值的。
建立示例應用程式
我建立了下面一些的C#項目以示範應用程式的不同元件:
NewHandler (HTTP處理程式)
Webapp (示範HTTP處理程式)
SecurityModules (HTTP子產品)
Webapp2 (示範HTTP子產品)
這些應用程式的安裝步驟:
解開attached zip檔案中的是以代碼。
建立兩個虛拟目錄webapp和webapp2;把這兩個目錄指向Webapp和Webapp2應用程式的實際實體目錄。
把NewHandler項目中的Newhandler.dll檔案複制到webapp應用程式的bin目錄。
把SecurityModules項目中的SecurityModules.dll檔案複制到webapp2應用程式的bin目錄中。
ASP.NET請求的處理過程
ASP.NET請求處理過程是基于管道模型的,在模型中ASP.NET把http請求傳遞給管道中的所有子產品。每個子產品都接收http請求并有完全控制權限。子產品可以用任何自認為适合的方式來處理請求。一旦請求經過了所有HTTP子產品,就最終被HTTP處理程式處理。HTTP處理程式對請求進行一些處理,并且結果将再次經過管道中的HTTP子產品:
請注意在http請求的處理過程中,隻能調用一個HTTP處理程式,然而可以調用多個HTTP子產品。
Http處理程式
HTTP處理程式是實作了System.Web.IHttpHandler接口的.NET元件。任何實作了IHttpHandler接口的類都可以用于處理輸入的HTTP請求。HTTP處理程式與ISAPI擴充有些類似。HTTP處理程式和ISAPI擴充的差别在于在URL中可以使用HTTP處理程式的檔案名稱直接調用它們,與ISAPI擴充類似。
HTTP處理程式實作了下列方法:
方法名稱 描述
ProcessRequest 這個方法實際上是http處理程式的核心。我們調用這個方法來處理http請求。
IsReusable 我們調用這個屬性來決定http處理程式的執行個體是否可以用于處理相同其它類型的請求。HTTP處理程式可以傳回true或false來表明它們是否可以重複使用。
你可以使用web.config或者machine.config檔案把這些類映射到http請求上。映射完成以後,當接收到相應請求的時候ASP.NET會執行個體化http處理程式。我們将解釋如何在web.config和/或machine.config檔案中定義所有這些細節資訊。
ASP.NET還通過IHttpHandlerFactory接口支援http處理程式的擴充。ASP.NET提供了把http請求路由到實作IHttpHandlerFactory接口的類的對象上的能力。此外,ASP.NET還利用了Factory設計模式。這種模式為建立一組相關對象而不提供具體類的功能提供了接口。簡單的說,你可以把用于建立依賴傳遞進來的參數建立的http處理程式對象的類看作是factory(工廠)。我們不用指定需要執行個體化的特定的http處理程式;http處理程式工廠處理這種事務。這樣做的優點在于如果未來實作IHttpHandler接口的對象的實作方法發生了改變,隻要接口仍然相同,用戶端就不會受到影響。
下面是IHttpHandlerFactory接口中的方法清單:
方法名稱 描述
GetHandler 這個方法負責建立适當的處理程式并把它的指針傳回到調用代碼(ASP.NET運作時)。這個方法傳回的處理程式對象應該實作了IHttpHandler接口。
ReleaseHandler 這個方法負責在請求處理完成後釋放http處理程式。Factory 實作決定了它的操作。Factory 實作可以是實際摧毀執行個體,也可以把它放入緩沖池供以後使用。
在配置檔案中注冊HTTP處理程式和HTTP處理程式工廠
ASP.NET在下面的配置檔案中維護自己的配置資訊:
machine.config
web.config
machine.config檔案包含應用于計算機上安裝的所有Web應用程式的配置設定資訊。
web.config檔案對于每個Web應用程式來說是特定的。每個Web應用程式都有自己的web.config檔案。Web應用程式的任何子目錄也可能包含自己的web.config檔案;這使得它們能夠覆寫父目錄的設定資訊。
為了給我們的Web應用程式添加HTTP處理程式,你可以使用<httpHandlers>和<add>節點。實際上,處理程式都帶有<add>節點,列舉在<httpHandlers>和</httpHandlers>節點之間。下面是添加HTTP處理程式的一個普通的例子:
<httpHandlers>
<add verb="supported http verbs" path="path" type="namespace.classname, assemblyname" />
<httpHandlers>
在上面的XML中,
Verb屬性指定了處理程式支援的HTTP動作。如果某個處理程式支援所有的HTTP動作,請使用“*”,否則使用逗号分隔的清單列出支援的動作。是以如果你的處理程式隻支援HTTP GET和POST,那麼verb屬性就應該是“GET, POST”。
Path屬性指定了需要調用處理程式的路徑和檔案名(可以包含通配符)。例如,如果你希望自己的處理程式隻有在test.xyz檔案被請求的時候才被調用,那麼path屬性就包含“test.xyz”,如果你希望含有.xyz字尾的所有檔案都調用處理程式,path屬性應該包含“*.xyz”。
Type屬性用名字空間、類名稱和部件名稱的組合形式指定處理程式或處理程式工廠的實際類型。ASP.NET運作時首先搜尋應用程式的bin目錄中的部件DLL,接着在全局部件緩沖(GAC)中搜尋。
ASP.NET運作時對HTTP處理程式的使用方式
無論你是否相信,ASP.NET都使用HTTP請求實作了大量的自己的功能。ASP.NET使用處理程式來處理.aspx、 .asmx、 .soap和其它ASP.NET檔案。
下面是 machine.config檔案中的一個片段:
<httpHandlers>
<add verb="*" path="trace.axd" type="System.Web.Handlers.TraceHandler"/>
<add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory"/>
<add verb="*" path="*.ashx" type="System.Web.UI.SimpleHandlerFactory"/>
<add verb="*" path="*.config" type="System.Web.HttpForbiddenHandler"/>
<add verb="GET,HEAD" path="*" type="System.Web.StaticFileHandler"/>
. . . . . .
. . . . . .
</httpHandlers>
在上面的配置資訊中你可以看到對.aspx檔案的所有請求都由System.Web.UI.PageHandlerFactory類來處理。與此類似,對.config檔案和其它檔案(它們不能被用戶端直接通路)的所有請求都由System.Web.HttpForbiddenHandler類處理。你可能已經猜到,當通路這些檔案的時候,該類簡單地給用戶端傳回一個錯誤資訊。
執行HTTP處理程式
現在你将看到如何實作一個HTTP處理程式。那麼我們的新處理程式要做什麼任務呢?前面我提到,處理程式大多數用于給Web伺服器添加新功能;是以,我将建立一個處理程式來處理新的檔案類型——擴充名為.15seconds的檔案。我們建立了這個處理程式并在我們的Web應用程式的web.config檔案中注冊之後,所有對.15seconds檔案的請求都将由這個新處理程式來處理。
你可能正在考慮這個處理程式的使用方法。如果你希望引入一種新的伺服器腳本語言或動态伺服器檔案(例如asp、aspx)該怎麼辦呢?你可以為它編寫一個自己的處理程式。類似地,如果你希望在IIS上運作Java小程式、JSP和其它一些伺服器端Java元件應該怎麼辦呢?一種方法是安裝某些ISAPI擴充(例如Allaire或Macromedia Jrun)。你也可以編寫自己的HTTP處理程式。盡管這對于第三方廠商(例如Allaire和Macromedia)來說是很複雜的事務,但是它卻是個很有吸引力的選擇,因為它們的HTTP處理能夠能夠通路ASP.NET運作時暴露的所有新功能。
實作我們的HTTP處理程式包含以下步驟:
1.編寫一個實作IHttpHandler接口的類。
2. 在web.config或machine.config檔案中注冊這個處理程式。
3.在Internet服務管理器中把檔案擴充(.15seconds)映射到ASP.NET ISAPI擴充DLL(aspnet_isapi.dll)上。
第一步
在Visual Studio.NET中建立一個新的C#類庫項目,并把它命名為“MyHandler”。Visual Studio.NET将自動地給項目添加一個叫做“Class1.cs”的類。把它改名為“NewHandler”;在代碼視窗中打開這個類,并把類的名稱和構造函數的名稱改成“NewHandler”。
下面是NewHandler類的代碼:
using System;
using System.Web;
namespace MyHandler
{
public class NewHandler : IHttpHandler
{
public NewHandler()
{
// TODO: 此處添加構造邏輯
}
#region Implementation of IHttpHandler
public void ProcessRequest(System.Web.HttpContext context)
{
HttpResponse objResponse = context.Response ;
objResponse.Write("<html><body><h1>Hello 15Seconds Reader ") ;
objResponse.Write("</body></html>") ;
}
public bool IsReusable
{
get
{
return true;
}
}
#endregion
}
}
你在ProcessRequest方法中可以看到,該HTTP處理程式通過System.Web.HttpContext對象通路了所有作為參數傳遞給它的ASP.NET内部對象。實作ProcessRequest方法隻需要簡單地從context對象中提取HttpResponse對象并把發送一些HTML給用戶端。類似地,IsReusable傳回true,表明這個處理程式可以被重複用作處理其它的HTTP請求。
我們編譯上面的代碼并把它放到webapp虛拟目錄的bin目錄之中。
第二步
Taito注:些處是修改C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/CONFIG下的web.config放在<httphandlers>裡的第一行!
在web.config檔案中通過添加下面的文本來注冊這個處理程式:
<httpHandlers>
<add verb="*" path="*.15seconds" type="MyHandler.NewHandler,MyHandler"/>
</httpHandlers>
第三步
由于我們已經建立了用于處理新擴充檔案的處理程式了,我們還需要把這個擴充名告訴IIS并把它映射到ASP.NET。如果你不執行這個步驟而試圖通路Hello.15seconds檔案,IIS将簡單地傳回該檔案而不是把它傳遞給ASP.NET運作時。其結果是該HTTP處理程式不會被調用。
運作Internet服務管理器,右鍵點選預設Web站點,選擇屬性,移動到Home目錄選項頁,并點選配置按鈕。應用程式配置對話框彈出來了。點選添加按鈕并在可執行字段輸入aspnet_isapi.dll檔案路徑,在擴充字段輸入.15seconds。其它字段不用處理;該對話框如下所示:
點選确認按鈕關閉應用程式配置和預設Web站點屬性對話框。
現在我們運作Internet Explorer并輸入url:http://localhost/webapp/hello.15seconds,看到的頁面如下:
HTTP處理程式中的對話狀态
維護對話狀态是Web應用程式執行的最通常的事務。HTTP處理程式也需要通路這些對話狀态。但是HTTP處理程式的預設設定是沒有激活對話狀态的。為了讀取和/或寫入狀态資料,需要HTTP處理程式實作下面的接口之一:
IRequiresSessionState
IReadOnlySessionState.
當HTTP處理程式需要讀寫對話資料的時候,它必須實作IRequiresSessionState接口。如果它隻讀取對話資料,實作IReadOnlySessionState接口就可以了。
這兩個接口都是标記接口,并沒有包含任何方法。是以,如果你希望激活NewHandler處理程式的對話狀态,要像下面的代碼一樣聲明NewHandler類:
public class NewHandler : IHttpHandler, IRequiresSessionState
HTTP子產品
HTTP子產品是實作了System.Web.IhttpModule接口的.NET元件。這些元件通過在某些事件中注冊自身,把自己插入ASP.NET請求處理管道。當這些事件發生的時候,ASP.NET調用對請求有興趣的HTTP子產品,這樣該子產品就能處理請求了。
HTTP子產品實作了IhttpModule接口的下面一些方法:
方法名稱 描述
Init 這個方法允許HTTP子產品向HttpApplication 對象中的事件注冊自己的事件處理程式。
Dispose 這個方法給予HTTP子產品在對象被垃圾收集之前執行清理的機會。
HTTP子產品可以向System.Web.HttpApplication對象暴露的下面一些方法注冊:
事件名稱 描述
AcquireRequestState 當ASP.NET運作時準備好接收目前HTTP請求的對話狀态的時候引發這個事件。
AuthenticateRequest 當ASP.NET 運作時準備驗證使用者身份的時候引發這個事件。
AuthorizeRequest 當ASP.NET運作時準備授權使用者通路資源的時候引發這個事件。
BeginRequest 當ASP.NET運作時接收到新的HTTP請求的時候引發這個事件。
Disposed 當ASP.NET完成HTTP請求的處理過程時引發這個事件。
EndRequest 把響應内容發送到用戶端之前引發這個事件。
Error 在處理HTTP請求的過程中出現未處理異常的時候引發這個事件。
PostRequestHandlerExecute 在HTTP處理程式結束執行的時候引發這個事件。
PreRequestHandlerExecute 在ASP.NET開始執行HTTP請求的處理程式之前引發這個事件。在這個事件之後,ASP.NET 把該請求轉發給适當的HTTP處理程式。
PreSendRequestContent 在ASP.NET把響應内容發送到用戶端之前引發這個事件。這個事件允許我們在内容到達用戶端之前改變響應内容。我們可以使用這個事件給頁面輸出添加用于所有頁面的内容。例如通用菜單、頭資訊或腳資訊。
PreSendRequestHeaders 在ASP.NET把HTTP響應頭資訊發送給用戶端之前引發這個事件。在頭資訊到達用戶端之前,這個事件允許我們改變它的内容。我們可以使用這個事件在頭資訊中添加cookie和自定義資料。
ReleaseRequestState 當ASP.NET結束所搜有的請求處理程式執行的時候引發這個事件。
ResolveRequestCache 我們引發這個事件來決定是否可以使用從輸出緩沖傳回的内容來結束請求。這依賴于Web應用程式的輸出緩沖時怎樣設定的。
UpdateRequestCache 當ASP.NET完成了目前的HTTP請求的處理,并且輸出内容已經準備好添加給輸出緩沖的時候,引發這個事件。這依賴于Web應用程式的輸出緩沖是如何設定的。
除了這些事件之外,我們還可以使用四個事件。我們可以通過實作Web應用程式的global.asax檔案中一些方法來使用這些事件。
這些事件是:
Application_OnStart
當第一個請求到達Web應用程式的時候引發這個事件。
Application_OnEnd
準備終止應用程式之前引發這個事件。
Session_OnStart
使用者對話的第一個請求引發這個事件。
Session_OnEnd
放棄對話或者對話超期的時候引發這個事件。
在配置檔案中注冊HTTP子產品
當我們建立了HTTP子產品并把它複制到Web應用程式的bin目錄或者全局部件緩沖(Global Assembly Cache)之後,接下來就應該在web.config或machine.config中注冊它了。
我們可以
使用<httpModules>和<add>節點把HTTP子產品添加到Web應用程式中。實際上子產品都使用<add>節點列舉在<httpModules>和</httpModules>節點之内了。
因為配置設定資訊是可以繼承的,是以子目錄從父目錄那兒繼承配置設定資訊。其結果是,子目錄可能繼承了一些不需要的HTTP子產品(它們是父配置資訊的一部分);是以,我們需要一種删除這些不需要的子產品的方法。我們可以使用<remove>節點;如果我們希望删除從應用程式繼承得到的所有HTTP子產品,可以使用<clear>節點。
下面的代碼是添加HTTP子產品的一個通用示例:
<httpModules>
<add type="classname, assemblyname" name="modulename" />
<httpModules>
下面的代碼是從應用程式中删除HTTP子產品的一個通用示例:
<httpModules>
<remove name="modulename" />
<httpModules>
在上面的XML中:
Type屬性用類和部件名稱的形式指定了HTTP子產品的實際類型。
Name屬性指定了子產品的友好名稱。其它應用程式可以使用這個名稱來識别HTTP子產品。
ASP.NET運作時如何使用HTTP子產品
ASP.NET運作時使用HTTP子產品實作某些特殊的功能。下面的片段來自于machine.config檔案,它顯示了ASP.NET運作時安裝的HTTP子產品:
<httpModules>
<add name="OutputCache" type="System.Web.Caching.OutputCacheModule"/>
<add name="Session" type="System.Web.SessionState.SessionStateModule"/>
<add name="WindowsAuthentication"
type="System.Web.Security.WindowsAuthenticationModule"/>
<add name="FormsAuthentication"
type="System.Web.Security.FormsAuthenticationModule"/>
<add name="PassportAuthentication"
type="System.Web.Security.PassportAuthenticationModule"/>
<add name="UrlAuthorization"
type="System.Web.Security.UrlAuthorizationModule"/>
<add name="FileAuthorization"
type="System.Web.Security.FileAuthorizationModule"/>
</httpModules>
ASP.NET使用上面一些HTTP子產品來提供一些服務,例如身份驗證和授權、對話管理和輸出緩沖。由于這些子產品都注冊在machine.config檔案中。
實作一個提供安全服務的HTTP子產品
現在我們實作一個HTTP子產品,它為我們的Web應用程式提供安全服務。該HTTP子產品基本上是提供一種定制的身份認證服務。它将接收HTTP請求中的身份憑證,并确定該憑證是否有效。如果有效,與使用者相關的角色是什麼?通過
User.Identity對象,它把這些角色與通路我們的Web應用程式頁面的使用者的辨別關聯起來。
下面是該HTTP子產品的代碼:
using System;
using System.Web;
using System.Security.Principal;
namespace SecurityModules
{
/// Class1的總體描述。
public class CustomAuthenticationModule : IHttpModule
{
public CustomAuthenticationModule()
{
}
public void Init(HttpApplication r_objApplication)
{
// 向Application 對象注冊事件處理程式。
r_objApplication.AuthenticateRequest +=
new EventHandler(this.AuthenticateRequest) ;
}
public void Dispose()
{
// 此處空出,因為我們不需要做什麼操作。
}
private void AuthenticateRequest(object r_objSender,EventArgs r_objEventArgs)
{
// 鑒别使用者的憑證,并找出使用者角色。。
1. HttpApplication objApp = (HttpApplication) r_objSender ;
2. HttpContext objContext = (HttpContext) objApp.Context ;
3. if ( (objApp.Request["userid"] == null) ||
4. (objApp.Request["password"] == null) )
5. {
6. objContext.Response.Write("<H1>Credentials not provided</H1>") ;
7. objContext.Response.End() ;
8. }
9. string userid = "";
10. userid = objApp.Request["userid"].ToString() ;
11. string password = "";
12. password = objApp.Request["password"].ToString() ;
13. string[] strRoles ;
14. strRoles = AuthenticateAndGetRoles(userid, password) ;
15. if ((strRoles == null) || (strRoles.GetLength(0) == 0))
16. {
17. objContext.Response.Write("<H1>We are sorry but we could not
find this user id and password in our database</H1>") ;
18. objApp.CompleteRequest() ;
19. }
20. GenericIdentity objIdentity = new GenericIdentity(userid,
"CustomAuthentication") ;
21. objContext.User = new GenericPrincipal(objIdentity, strRoles) ;
}
private string[] AuthenticateAndGetRoles(string r_strUserID,string r_strPassword)
{
string[] strRoles = null ;
if ((r_strUserID.Equals("Steve")) &&(r_strPassword.Equals("15seconds")))
{
strRoles = new String[1] ;
strRoles[0] = "Administrator" ;
}
else if ((r_strUserID.Equals("Mansoor")) &&(r_strPassword.Equals("mas")))
{
strRoles = new string[1] ;
strRoles[0] = "User" ;
}
return strRoles ;
}
}
}
我們研究一下上面的代碼。
我們是從Init函數開始的。這個函數把處理程式的AuthenticateRequest事件插入Application(應用程式)對象的事件處理程式清單中。這将導緻引發AuthenticationRequest事件的時候Application調用該方法。
我們的HTTP子產品初始化之後,我們就可以調用它的AuthenticateRequest方法來鑒别用戶端請求。AuthenticateRequest方法是該安全/身份認證機制的核心。在這個函數中:
1和2行提取HttpApplication和HttpContext對象。3到7行檢測是否沒有給我們提供了使用者id或密碼。如果沒有提供,就顯示錯誤資訊,請求處理過程終止。
9到12行從HttpRequest對象中提取使用者id和密碼。
14行調用一個叫做AuthenticateAndGetRoles的輔助(helper)函數。這個函數主要執行身份驗證并決定使用者角色。上面的代碼采用了寫死(hard-coded),隻允許兩個使用者使用,但是我們可以擴充這個方法,并添加代碼與使用者資料庫互動操作并檢索使用者的角色。
16到19行檢測是否有角色與使用者關聯。如果沒有就意味着傳遞給我們的憑證沒有通過驗證;是以該憑證是無效的。是以,給用戶端發送一個錯誤資訊,并且請求結束了。
20和21行非常重要,因為這兩行實際上告訴ASP.NET HTTP運作時已登入使用者的身份。這兩行成功執行以後,我們的aspx頁面就能夠使用User對象通路這些資訊了。
現在我們看一看這種身份驗證機制的運作情況。目前我們隻允許下面兩個使用者登入到系統:
User id = Steve, Password = 15seconds, Role = Administrator
User id = Mansoor, Password = mas, Role = User
注意使用者id和密碼是大小寫敏感的(區分大小寫)。
首先試圖不提供憑證登入系統,在IE中輸入http://localhost/webapp2/index.aspx将看到下面的消息:
現在試圖使用使用者id“Steve”和密碼“15seconds”登入系統。輸入 http://localhost/webapp2/index.aspx?userid=Steve&password=15seconds你将看到下面的歡迎消息:
現在試圖使用使用者id“Mansoor”和秘碼“mas”登入系統。輸入http://localhost/webapp2/index.aspx?userid=Mansoor&password=mas你将看到下面的歡迎消息頁面:
現在試圖使用錯誤的使用者id和密碼組合來登入系統。輸入http://localhost/webapp2/index.aspx?userid=Mansoor&password=xyz你将看到下面的錯誤消息:
這表明我們的安全子產品在起作用了。你可以通過在AuthenticateAndGetRoles方法中使用資料庫通路代碼來擴充該安全子產品。
要使所有的部分都起作用,我們必須對web.config檔案進行一些修改。首先,由于我們要使用自己的身份驗證,是以不需要其它的身份驗證機制。為了達到這個目的,改變webapp2的web.config檔案中的<authentication>節點,如下所示:
<authentication mode="None"/>
類似地,不允許匿名使用者通路我們的Web站點。給web.config檔案添加下面的語句:
<authorization>
<deny users="?"/>
</authorization>
用于至少能夠匿名通路用于提供憑證的檔案。在web.config檔案中使用下面的配置設定資訊把index.aspx作為唯一能夠匿名通路的檔案:
<location path="index.aspx">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
結論
你可能已經意識到有了HTTP處理程式和子產品後,ASP.NET已經給開發者提供了強大的能量。把你自己的元件插入ASP.NET請求處理管道,享受它的優點吧。
作為練習,你應該進一步改程序式,使示例身份驗證子產品更加靈活,并能根據使用者的需要進行調整。
請注意在http請求的處理過程中,隻能調用一個HTTP處理程式,然而可以調用多個HTTP子產品。
Http處理程式
HTTP處理程式是實作了System.Web.IHttpHandler接口的.NET元件。任何實作了IHttpHandler接口的類都可以用于處理輸入的HTTP請求。HTTP處理程式與ISAPI擴充有些類似。HTTP處理程式和ISAPI擴充的差别在于在URL中可以使用HTTP處理程式的檔案名稱直接調用它們,與ISAPI擴充類似。
HTTP處理程式實作了下列方法:
方法名稱 描述
ProcessRequest 這個方法實際上是http處理程式的核心。我們調用這個方法來處理http請求。
IsReusable 我們調用這個屬性來決定http處理程式的執行個體是否可以用于處理相同其它類型的請求。HTTP處理程式可以傳回true或false來表明它們是否可以重複使用。
你可以使用web.config或者machine.config檔案把這些類映射到http請求上。映射完成以後,當接收到相應請求的時候ASP.NET會執行個體化http處理程式。我們将解釋如何在web.config和/或machine.config檔案中定義所有這些細節資訊。
ASP.NET還通過IHttpHandlerFactory接口支援http處理程式的擴充。ASP.NET提供了把http請求路由到實作IHttpHandlerFactory接口的類的對象上的能力。此外,ASP.NET還利用了Factory設計模式。這種模式為建立一組相關對象而不提供具體類的功能提供了接口。簡單的說,你可以把用于建立依賴傳遞進來的參數建立的http處理程式對象的類看作是factory(工廠)。我們不用指定需要執行個體化的特定的http處理程式;http處理程式工廠處理這種事務。這樣做的優點在于如果未來實作IHttpHandler接口的對象的實作方法發生了改變,隻要接口仍然相同,用戶端就不會受到影響。
下面是IHttpHandlerFactory接口中的方法清單:
方法名稱 描述
GetHandler 這個方法負責建立适當的處理程式并把它的指針傳回到調用代碼(ASP.NET運作時)。這個方法傳回的處理程式對象應該實作了IHttpHandler接口。
ReleaseHandler 這個方法負責在請求處理完成後釋放http處理程式。Factory 實作決定了它的操作。Factory 實作可以是實際摧毀執行個體,也可以把它放入緩沖池供以後使用。
在配置檔案中注冊HTTP處理程式和HTTP處理程式工廠
ASP.NET在下面的配置檔案中維護自己的配置資訊:
machine.config
web.config
machine.config檔案包含應用于計算機上安裝的所有Web應用程式的配置設定資訊。
web.config檔案對于每個Web應用程式來說是特定的。每個Web應用程式都有自己的web.config檔案。Web應用程式的任何子目錄也可能包含自己的web.config檔案;這使得它們能夠覆寫父目錄的設定資訊。
為了給我們的Web應用程式添加HTTP處理程式,你可以使用<httpHandlers>和<add>節點。實際上,處理程式都帶有<add>節點,列舉在<httpHandlers>和</httpHandlers>節點之間。下面是添加HTTP處理程式的一個普通的例子:
<httpHandlers>
<add verb="supported http verbs" path="path" type="namespace.classname, assemblyname" />
<httpHandlers>
在上面的XML中,
Verb屬性指定了處理程式支援的HTTP動作。如果某個處理程式支援所有的HTTP動作,請使用“*”,否則使用逗号分隔的清單列出支援的動作。是以如果你的處理程式隻支援HTTP GET和POST,那麼verb屬性就應該是“GET, POST”。
Path屬性指定了需要調用處理程式的路徑和檔案名(可以包含通配符)。例如,如果你希望自己的處理程式隻有在test.xyz檔案被請求的時候才被調用,那麼path屬性就包含“test.xyz”,如果你希望含有.xyz字尾的所有檔案都調用處理程式,path屬性應該包含“*.xyz”。
Type屬性用名字空間、類名稱和部件名稱的組合形式指定處理程式或處理程式工廠的實際類型。ASP.NET運作時首先搜尋應用程式的bin目錄中的部件DLL,接着在全局部件緩沖(GAC)中搜尋。
ASP.NET運作時對HTTP處理程式的使用方式
無論你是否相信,ASP.NET都使用HTTP請求實作了大量的自己的功能。ASP.NET使用處理程式來處理.aspx、 .asmx、 .soap和其它ASP.NET檔案。
下面是 machine.config檔案中的一個片段:
<httpHandlers>
<add verb="*" path="trace.axd" type="System.Web.Handlers.TraceHandler"/>
<add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory"/>
<add verb="*" path="*.ashx" type="System.Web.UI.SimpleHandlerFactory"/>
<add verb="*" path="*.config" type="System.Web.HttpForbiddenHandler"/>
<add verb="GET,HEAD" path="*" type="System.Web.StaticFileHandler"/>
. . . . . .
. . . . . .
</httpHandlers>
在上面的配置資訊中你可以看到對.aspx檔案的所有請求都由System.Web.UI.PageHandlerFactory類來處理。與此類似,對.config檔案和其它檔案(它們不能被用戶端直接通路)的所有請求都由System.Web.HttpForbiddenHandler類處理。你可能已經猜到,當通路這些檔案的時候,該類簡單地給用戶端傳回一個錯誤資訊。
執行HTTP處理程式
現在你将看到如何實作一個HTTP處理程式。那麼我們的新處理程式要做什麼任務呢?前面我提到,處理程式大多數用于給Web伺服器添加新功能;是以,我将建立一個處理程式來處理新的檔案類型——擴充名為.15seconds的檔案。我們建立了這個處理程式并在我們的Web應用程式的web.config檔案中注冊之後,所有對.15seconds檔案的請求都将由這個新處理程式來處理。
你可能正在考慮這個處理程式的使用方法。如果你希望引入一種新的伺服器腳本語言或動态伺服器檔案(例如asp、aspx)該怎麼辦呢?你可以為它編寫一個自己的處理程式。類似地,如果你希望在IIS上運作Java小程式、JSP和其它一些伺服器端Java元件應該怎麼辦呢?一種方法是安裝某些ISAPI擴充(例如Allaire或Macromedia Jrun)。你也可以編寫自己的HTTP處理程式。盡管這對于第三方廠商(例如Allaire和Macromedia)來說是很複雜的事務,但是它卻是個很有吸引力的選擇,因為它們的HTTP處理能夠能夠通路ASP.NET運作時暴露的所有新功能。
實作我們的HTTP處理程式包含以下步驟:
1.編寫一個實作IHttpHandler接口的類。
2. 在web.config或machine.config檔案中注冊這個處理程式。
3.在Internet服務管理器中把檔案擴充(.15seconds)映射到ASP.NET ISAPI擴充DLL(aspnet_isapi.dll)上。
第一步
在Visual Studio.NET中建立一個新的C#類庫項目,并把它命名為“MyHandler”。Visual Studio.NET将自動地給項目添加一個叫做“Class1.cs”的類。把它改名為“NewHandler”;在代碼視窗中打開這個類,并把類的名稱和構造函數的名稱改成“NewHandler”。
下面是NewHandler類的代碼:
using System;
using System.Web;
namespace MyHandler
{
public class NewHandler : IHttpHandler
{
public NewHandler()
{
// TODO: 此處添加構造邏輯
}
#region Implementation of IHttpHandler
public void ProcessRequest(System.Web.HttpContext context)
{
HttpResponse objResponse = context.Response ;
objResponse.Write("<html><body><h1>Hello 15Seconds Reader ") ;
objResponse.Write("</body></html>") ;
}
public bool IsReusable
{
get
{
return true;
}
}
#endregion
}
}
你在ProcessRequest方法中可以看到,該HTTP處理程式通過System.Web.HttpContext對象通路了所有作為參數傳遞給它的ASP.NET内部對象。實作ProcessRequest方法隻需要簡單地從context對象中提取HttpResponse對象并把發送一些HTML給用戶端。類似地,IsReusable傳回true,表明這個處理程式可以被重複用作處理其它的HTTP請求。
我們編譯上面的代碼并把它放到webapp虛拟目錄的bin目錄之中。
第二步
Taito注:些處是修改C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/CONFIG下的web.config放在<httphandlers>裡的第一行!
在web.config檔案中通過添加下面的文本來注冊這個處理程式:
<httpHandlers>
<add verb="*" path="*.15seconds" type="MyHandler.NewHandler,MyHandler"/>
</httpHandlers>
第三步
由于我們已經建立了用于處理新擴充檔案的處理程式了,我們還需要把這個擴充名告訴IIS并把它映射到ASP.NET。如果你不執行這個步驟而試圖通路Hello.15seconds檔案,IIS将簡單地傳回該檔案而不是把它傳遞給ASP.NET運作時。其結果是該HTTP處理程式不會被調用。
運作Internet服務管理器,右鍵點選預設Web站點,選擇屬性,移動到Home目錄選項頁,并點選配置按鈕。應用程式配置對話框彈出來了。點選添加按鈕并在可執行字段輸入aspnet_isapi.dll檔案路徑,在擴充字段輸入.15seconds。其它字段不用處理;該對話框如下所示:
點選确認按鈕關閉應用程式配置和預設Web站點屬性對話框。
現在我們運作Internet Explorer并輸入url:http://localhost/webapp/hello.15seconds,看到的頁面如下:
HTTP處理程式中的對話狀态
維護對話狀态是Web應用程式執行的最通常的事務。HTTP處理程式也需要通路這些對話狀态。但是HTTP處理程式的預設設定是沒有激活對話狀态的。為了讀取和/或寫入狀态資料,需要HTTP處理程式實作下面的接口之一:
IRequiresSessionState
IReadOnlySessionState.
當HTTP處理程式需要讀寫對話資料的時候,它必須實作IRequiresSessionState接口。如果它隻讀取對話資料,實作IReadOnlySessionState接口就可以了。
這兩個接口都是标記接口,并沒有包含任何方法。是以,如果你希望激活NewHandler處理程式的對話狀态,要像下面的代碼一樣聲明NewHandler類:
public class NewHandler : IHttpHandler, IRequiresSessionState
HTTP子產品
HTTP子產品是實作了System.Web.IhttpModule接口的.NET元件。這些元件通過在某些事件中注冊自身,把自己插入ASP.NET請求處理管道。當這些事件發生的時候,ASP.NET調用對請求有興趣的HTTP子產品,這樣該子產品就能處理請求了。
HTTP子產品實作了IhttpModule接口的下面一些方法:
方法名稱 描述
Init 這個方法允許HTTP子產品向HttpApplication 對象中的事件注冊自己的事件處理程式。
Dispose 這個方法給予HTTP子產品在對象被垃圾收集之前執行清理的機會。
HTTP子產品可以向System.Web.HttpApplication對象暴露的下面一些方法注冊:
事件名稱 描述
AcquireRequestState 當ASP.NET運作時準備好接收目前HTTP請求的對話狀态的時候引發這個事件。
AuthenticateRequest 當ASP.NET 運作時準備驗證使用者身份的時候引發這個事件。
AuthorizeRequest 當ASP.NET運作時準備授權使用者通路資源的時候引發這個事件。
BeginRequest 當ASP.NET運作時接收到新的HTTP請求的時候引發這個事件。
Disposed 當ASP.NET完成HTTP請求的處理過程時引發這個事件。
EndRequest 把響應内容發送到用戶端之前引發這個事件。
Error 在處理HTTP請求的過程中出現未處理異常的時候引發這個事件。
PostRequestHandlerExecute 在HTTP處理程式結束執行的時候引發這個事件。
PreRequestHandlerExecute 在ASP.NET開始執行HTTP請求的處理程式之前引發這個事件。在這個事件之後,ASP.NET 把該請求轉發給适當的HTTP處理程式。
PreSendRequestContent 在ASP.NET把響應内容發送到用戶端之前引發這個事件。這個事件允許我們在内容到達用戶端之前改變響應内容。我們可以使用這個事件給頁面輸出添加用于所有頁面的内容。例如通用菜單、頭資訊或腳資訊。
PreSendRequestHeaders 在ASP.NET把HTTP響應頭資訊發送給用戶端之前引發這個事件。在頭資訊到達用戶端之前,這個事件允許我們改變它的内容。我們可以使用這個事件在頭資訊中添加cookie和自定義資料。
ReleaseRequestState 當ASP.NET結束所搜有的請求處理程式執行的時候引發這個事件。
ResolveRequestCache 我們引發這個事件來決定是否可以使用從輸出緩沖傳回的内容來結束請求。這依賴于Web應用程式的輸出緩沖時怎樣設定的。
UpdateRequestCache 當ASP.NET完成了目前的HTTP請求的處理,并且輸出内容已經準備好添加給輸出緩沖的時候,引發這個事件。這依賴于Web應用程式的輸出緩沖是如何設定的。
除了這些事件之外,我們還可以使用四個事件。我們可以通過實作Web應用程式的global.asax檔案中一些方法來使用這些事件。
這些事件是:
Application_OnStart
當第一個請求到達Web應用程式的時候引發這個事件。
Application_OnEnd
準備終止應用程式之前引發這個事件。
Session_OnStart
使用者對話的第一個請求引發這個事件。
Session_OnEnd
放棄對話或者對話超期的時候引發這個事件。
在配置檔案中注冊HTTP子產品
當我們建立了HTTP子產品并把它複制到Web應用程式的bin目錄或者全局部件緩沖(Global Assembly Cache)之後,接下來就應該在web.config或machine.config中注冊它了。
我們可以
使用<httpModules>和<add>節點把HTTP子產品添加到Web應用程式中。實際上子產品都使用<add>節點列舉在<httpModules>和</httpModules>節點之内了。
因為配置設定資訊是可以繼承的,是以子目錄從父目錄那兒繼承配置設定資訊。其結果是,子目錄可能繼承了一些不需要的HTTP子產品(它們是父配置資訊的一部分);是以,我們需要一種删除這些不需要的子產品的方法。我們可以使用<remove>節點;如果我們希望删除從應用程式繼承得到的所有HTTP子產品,可以使用<clear>節點。
下面的代碼是添加HTTP子產品的一個通用示例:
<httpModules>
<add type="classname, assemblyname" name="modulename" />
<httpModules>
下面的代碼是從應用程式中删除HTTP子產品的一個通用示例:
<httpModules>
<remove name="modulename" />
<httpModules>
在上面的XML中:
Type屬性用類和部件名稱的形式指定了HTTP子產品的實際類型。
Name屬性指定了子產品的友好名稱。其它應用程式可以使用這個名稱來識别HTTP子產品。
ASP.NET運作時如何使用HTTP子產品
ASP.NET運作時使用HTTP子產品實作某些特殊的功能。下面的片段來自于machine.config檔案,它顯示了ASP.NET運作時安裝的HTTP子產品:
<httpModules>
<add name="OutputCache" type="System.Web.Caching.OutputCacheModule"/>
<add name="Session" type="System.Web.SessionState.SessionStateModule"/>
<add name="WindowsAuthentication"
type="System.Web.Security.WindowsAuthenticationModule"/>
<add name="FormsAuthentication"
type="System.Web.Security.FormsAuthenticationModule"/>
<add name="PassportAuthentication"
type="System.Web.Security.PassportAuthenticationModule"/>
<add name="UrlAuthorization"
type="System.Web.Security.UrlAuthorizationModule"/>
<add name="FileAuthorization"
type="System.Web.Security.FileAuthorizationModule"/>
</httpModules>
ASP.NET使用上面一些HTTP子產品來提供一些服務,例如身份驗證和授權、對話管理和輸出緩沖。由于這些子產品都注冊在machine.config檔案中。
實作一個提供安全服務的HTTP子產品
現在我們實作一個HTTP子產品,它為我們的Web應用程式提供安全服務。該HTTP子產品基本上是提供一種定制的身份認證服務。它将接收HTTP請求中的身份憑證,并确定該憑證是否有效。如果有效,與使用者相關的角色是什麼?通過
User.Identity對象,它把這些角色與通路我們的Web應用程式頁面的使用者的辨別關聯起來。
下面是該HTTP子產品的代碼:
using System;
using System.Web;
using System.Security.Principal;
namespace SecurityModules
{
/// Class1的總體描述。
public class CustomAuthenticationModule : IHttpModule
{
public CustomAuthenticationModule()
{
}
public void Init(HttpApplication r_objApplication)
{
// 向Application 對象注冊事件處理程式。
r_objApplication.AuthenticateRequest +=
new EventHandler(this.AuthenticateRequest) ;
}
public void Dispose()
{
// 此處空出,因為我們不需要做什麼操作。
}
private void AuthenticateRequest(object r_objSender,EventArgs r_objEventArgs)
{
// 鑒别使用者的憑證,并找出使用者角色。。
1. HttpApplication objApp = (HttpApplication) r_objSender ;
2. HttpContext objContext = (HttpContext) objApp.Context ;
3. if ( (objApp.Request["userid"] == null) ||
4. (objApp.Request["password"] == null) )
5. {
6. objContext.Response.Write("<H1>Credentials not provided</H1>") ;
7. objContext.Response.End() ;
8. }
9. string userid = "";
10. userid = objApp.Request["userid"].ToString() ;
11. string password = "";
12. password = objApp.Request["password"].ToString() ;
13. string[] strRoles ;
14. strRoles = AuthenticateAndGetRoles(userid, password) ;
15. if ((strRoles == null) || (strRoles.GetLength(0) == 0))
16. {
17. objContext.Response.Write("<H1>We are sorry but we could not
find this user id and password in our database</H1>") ;
18. objApp.CompleteRequest() ;
19. }
20. GenericIdentity objIdentity = new GenericIdentity(userid,
"CustomAuthentication") ;
21. objContext.User = new GenericPrincipal(objIdentity, strRoles) ;
}
private string[] AuthenticateAndGetRoles(string r_strUserID,string r_strPassword)
{
string[] strRoles = null ;
if ((r_strUserID.Equals("Steve")) &&(r_strPassword.Equals("15seconds")))
{
strRoles = new String[1] ;
strRoles[0] = "Administrator" ;
}
else if ((r_strUserID.Equals("Mansoor")) &&(r_strPassword.Equals("mas")))
{
strRoles = new string[1] ;
strRoles[0] = "User" ;
}
return strRoles ;
}
}
}
我們研究一下上面的代碼。
我們是從Init函數開始的。這個函數把處理程式的AuthenticateRequest事件插入Application(應用程式)對象的事件處理程式清單中。這将導緻引發AuthenticationRequest事件的時候Application調用該方法。
我們的HTTP子產品初始化之後,我們就可以調用它的AuthenticateRequest方法來鑒别用戶端請求。AuthenticateRequest方法是該安全/身份認證機制的核心。在這個函數中:
1和2行提取HttpApplication和HttpContext對象。3到7行檢測是否沒有給我們提供了使用者id或密碼。如果沒有提供,就顯示錯誤資訊,請求處理過程終止。
9到12行從HttpRequest對象中提取使用者id和密碼。
14行調用一個叫做AuthenticateAndGetRoles的輔助(helper)函數。這個函數主要執行身份驗證并決定使用者角色。上面的代碼采用了寫死(hard-coded),隻允許兩個使用者使用,但是我們可以擴充這個方法,并添加代碼與使用者資料庫互動操作并檢索使用者的角色。
16到19行檢測是否有角色與使用者關聯。如果沒有就意味着傳遞給我們的憑證沒有通過驗證;是以該憑證是無效的。是以,給用戶端發送一個錯誤資訊,并且請求結束了。
20和21行非常重要,因為這兩行實際上告訴ASP.NET HTTP運作時已登入使用者的身份。這兩行成功執行以後,我們的aspx頁面就能夠使用User對象通路這些資訊了。
現在我們看一看這種身份驗證機制的運作情況。目前我們隻允許下面兩個使用者登入到系統:
User id = Steve, Password = 15seconds, Role = Administrator
User id = Mansoor, Password = mas, Role = User
注意使用者id和密碼是大小寫敏感的(區分大小寫)。
首先試圖不提供憑證登入系統,在IE中輸入http://localhost/webapp2/index.aspx将看到下面的消息:
現在試圖使用使用者id“Steve”和密碼“15seconds”登入系統。輸入 http://localhost/webapp2/index.aspx?userid=Steve&password=15seconds你将看到下面的歡迎消息:
現在試圖使用使用者id“Mansoor”和秘碼“mas”登入系統。輸入http://localhost/webapp2/index.aspx?userid=Mansoor&password=mas你将看到下面的歡迎消息頁面:
現在試圖使用錯誤的使用者id和密碼組合來登入系統。輸入http://localhost/webapp2/index.aspx?userid=Mansoor&password=xyz你将看到下面的錯誤消息:
這表明我們的安全子產品在起作用了。你可以通過在AuthenticateAndGetRoles方法中使用資料庫通路代碼來擴充該安全子產品。
要使所有的部分都起作用,我們必須對web.config檔案進行一些修改。首先,由于我們要使用自己的身份驗證,是以不需要其它的身份驗證機制。為了達到這個目的,改變webapp2的web.config檔案中的<authentication>節點,如下所示:
<authentication mode="None"/>
類似地,不允許匿名使用者通路我們的Web站點。給web.config檔案添加下面的語句:
<authorization>
<deny users="?"/>
</authorization>
用于至少能夠匿名通路用于提供憑證的檔案。在web.config檔案中使用下面的配置設定資訊把index.aspx作為唯一能夠匿名通路的檔案:
<location path="index.aspx">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
結論
你可能已經意識到有了HTTP處理程式和子產品後,ASP.NET已經給開發者提供了強大的能量。把你自己的元件插入ASP.NET請求處理管道,享受它的優點吧。
作為練習,你應該進一步改程序式,使示例身份驗證子產品更加靈活,并能根據使用者的需要進行調整。
請注意在http請求的處理過程中,隻能調用一個HTTP處理程式,然而可以調用多個HTTP子產品。
Http處理程式
HTTP處理程式是實作了System.Web.IHttpHandler接口的.NET元件。任何實作了IHttpHandler接口的類都可以用于處理輸入的HTTP請求。HTTP處理程式與ISAPI擴充有些類似。HTTP處理程式和ISAPI擴充的差别在于在URL中可以使用HTTP處理程式的檔案名稱直接調用它們,與ISAPI擴充類似。
HTTP處理程式實作了下列方法:
方法名稱 描述
ProcessRequest 這個方法實際上是http處理程式的核心。我們調用這個方法來處理http請求。
IsReusable 我們調用這個屬性來決定http處理程式的執行個體是否可以用于處理相同其它類型的請求。HTTP處理程式可以傳回true或false來表明它們是否可以重複使用。
你可以使用web.config或者machine.config檔案把這些類映射到http請求上。映射完成以後,當接收到相應請求的時候ASP.NET會執行個體化http處理程式。我們将解釋如何在web.config和/或machine.config檔案中定義所有這些細節資訊。
ASP.NET還通過IHttpHandlerFactory接口支援http處理程式的擴充。ASP.NET提供了把http請求路由到實作IHttpHandlerFactory接口的類的對象上的能力。此外,ASP.NET還利用了Factory設計模式。這種模式為建立一組相關對象而不提供具體類的功能提供了接口。簡單的說,你可以把用于建立依賴傳遞進來的參數建立的http處理程式對象的類看作是factory(工廠)。我們不用指定需要執行個體化的特定的http處理程式;http處理程式工廠處理這種事務。這樣做的優點在于如果未來實作IHttpHandler接口的對象的實作方法發生了改變,隻要接口仍然相同,用戶端就不會受到影響。
下面是IHttpHandlerFactory接口中的方法清單:
方法名稱 描述
GetHandler 這個方法負責建立适當的處理程式并把它的指針傳回到調用代碼(ASP.NET運作時)。這個方法傳回的處理程式對象應該實作了IHttpHandler接口。
ReleaseHandler 這個方法負責在請求處理完成後釋放http處理程式。Factory 實作決定了它的操作。Factory 實作可以是實際摧毀執行個體,也可以把它放入緩沖池供以後使用。
在配置檔案中注冊HTTP處理程式和HTTP處理程式工廠
ASP.NET在下面的配置檔案中維護自己的配置資訊:
machine.config
web.config
machine.config檔案包含應用于計算機上安裝的所有Web應用程式的配置設定資訊。
web.config檔案對于每個Web應用程式來說是特定的。每個Web應用程式都有自己的web.config檔案。Web應用程式的任何子目錄也可能包含自己的web.config檔案;這使得它們能夠覆寫父目錄的設定資訊。
為了給我們的Web應用程式添加HTTP處理程式,你可以使用<httpHandlers>和<add>節點。實際上,處理程式都帶有<add>節點,列舉在<httpHandlers>和</httpHandlers>節點之間。下面是添加HTTP處理程式的一個普通的例子:
<httpHandlers>
<add verb="supported http verbs" path="path" type="namespace.classname, assemblyname" />
<httpHandlers>
在上面的XML中,
Verb屬性指定了處理程式支援的HTTP動作。如果某個處理程式支援所有的HTTP動作,請使用“*”,否則使用逗号分隔的清單列出支援的動作。是以如果你的處理程式隻支援HTTP GET和POST,那麼verb屬性就應該是“GET, POST”。
Path屬性指定了需要調用處理程式的路徑和檔案名(可以包含通配符)。例如,如果你希望自己的處理程式隻有在test.xyz檔案被請求的時候才被調用,那麼path屬性就包含“test.xyz”,如果你希望含有.xyz字尾的所有檔案都調用處理程式,path屬性應該包含“*.xyz”。
Type屬性用名字空間、類名稱和部件名稱的組合形式指定處理程式或處理程式工廠的實際類型。ASP.NET運作時首先搜尋應用程式的bin目錄中的部件DLL,接着在全局部件緩沖(GAC)中搜尋。
ASP.NET運作時對HTTP處理程式的使用方式
無論你是否相信,ASP.NET都使用HTTP請求實作了大量的自己的功能。ASP.NET使用處理程式來處理.aspx、 .asmx、 .soap和其它ASP.NET檔案。
下面是 machine.config檔案中的一個片段:
<httpHandlers>
<add verb="*" path="trace.axd" type="System.Web.Handlers.TraceHandler"/>
<add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory"/>
<add verb="*" path="*.ashx" type="System.Web.UI.SimpleHandlerFactory"/>
<add verb="*" path="*.config" type="System.Web.HttpForbiddenHandler"/>
<add verb="GET,HEAD" path="*" type="System.Web.StaticFileHandler"/>
. . . . . .
. . . . . .
</httpHandlers>
在上面的配置資訊中你可以看到對.aspx檔案的所有請求都由System.Web.UI.PageHandlerFactory類來處理。與此類似,對.config檔案和其它檔案(它們不能被用戶端直接通路)的所有請求都由System.Web.HttpForbiddenHandler類處理。你可能已經猜到,當通路這些檔案的時候,該類簡單地給用戶端傳回一個錯誤資訊。
執行HTTP處理程式
現在你将看到如何實作一個HTTP處理程式。那麼我們的新處理程式要做什麼任務呢?前面我提到,處理程式大多數用于給Web伺服器添加新功能;是以,我将建立一個處理程式來處理新的檔案類型——擴充名為.15seconds的檔案。我們建立了這個處理程式并在我們的Web應用程式的web.config檔案中注冊之後,所有對.15seconds檔案的請求都将由這個新處理程式來處理。
你可能正在考慮這個處理程式的使用方法。如果你希望引入一種新的伺服器腳本語言或動态伺服器檔案(例如asp、aspx)該怎麼辦呢?你可以為它編寫一個自己的處理程式。類似地,如果你希望在IIS上運作Java小程式、JSP和其它一些伺服器端Java元件應該怎麼辦呢?一種方法是安裝某些ISAPI擴充(例如Allaire或Macromedia Jrun)。你也可以編寫自己的HTTP處理程式。盡管這對于第三方廠商(例如Allaire和Macromedia)來說是很複雜的事務,但是它卻是個很有吸引力的選擇,因為它們的HTTP處理能夠能夠通路ASP.NET運作時暴露的所有新功能。
實作我們的HTTP處理程式包含以下步驟:
1.編寫一個實作IHttpHandler接口的類。
2. 在web.config或machine.config檔案中注冊這個處理程式。
3.在Internet服務管理器中把檔案擴充(.15seconds)映射到ASP.NET ISAPI擴充DLL(aspnet_isapi.dll)上。
第一步
在Visual Studio.NET中建立一個新的C#類庫項目,并把它命名為“MyHandler”。Visual Studio.NET将自動地給項目添加一個叫做“Class1.cs”的類。把它改名為“NewHandler”;在代碼視窗中打開這個類,并把類的名稱和構造函數的名稱改成“NewHandler”。
下面是NewHandler類的代碼:
using System;
using System.Web;
namespace MyHandler
{
public class NewHandler : IHttpHandler
{
public NewHandler()
{
// TODO: 此處添加構造邏輯
}
#region Implementation of IHttpHandler
public void ProcessRequest(System.Web.HttpContext context)
{
HttpResponse objResponse = context.Response ;
objResponse.Write("<html><body><h1>Hello 15Seconds Reader ") ;
objResponse.Write("</body></html>") ;
}
public bool IsReusable
{
get
{
return true;
}
}
#endregion
}
}
你在ProcessRequest方法中可以看到,該HTTP處理程式通過System.Web.HttpContext對象通路了所有作為參數傳遞給它的ASP.NET内部對象。實作ProcessRequest方法隻需要簡單地從context對象中提取HttpResponse對象并把發送一些HTML給用戶端。類似地,IsReusable傳回true,表明這個處理程式可以被重複用作處理其它的HTTP請求。
我們編譯上面的代碼并把它放到webapp虛拟目錄的bin目錄之中。
第二步
在web.config檔案中通過添加下面的文本來注冊這個處理程式:
<httpHandlers>
<add verb="*" path="*.15seconds" type="MyHandler.NewHandler,MyHandler"/>
</httpHandlers>
第三步
由于我們已經建立了用于處理新擴充檔案的處理程式了,我們還需要把這個擴充名告訴IIS并把它映射到ASP.NET。如果你不執行這個步驟而試圖通路Hello.15seconds檔案,IIS将簡單地傳回該檔案而不是把它傳遞給ASP.NET運作時。其結果是該HTTP處理程式不會被調用。
運作Internet服務管理器,右鍵點選預設Web站點,選擇屬性,移動到Home目錄選項頁,并點選配置按鈕。應用程式配置對話框彈出來了。點選添加按鈕并在可執行字段輸入aspnet_isapi.dll檔案路徑,在擴充字段輸入.15seconds。其它字段不用處理;該對話框如下所示:
點選确認按鈕關閉應用程式配置和預設Web站點屬性對話框。
現在我們運作Internet Explorer并輸入url:http://localhost/webapp/hello.15seconds,看到的頁面如下:
HTTP處理程式中的對話狀态
維護對話狀态是Web應用程式執行的最通常的事務。HTTP處理程式也需要通路這些對話狀态。但是HTTP處理程式的預設設定是沒有激活對話狀态的。為了讀取和/或寫入狀态資料,需要HTTP處理程式實作下面的接口之一:
IRequiresSessionState
IReadOnlySessionState.
當HTTP處理程式需要讀寫對話資料的時候,它必須實作IRequiresSessionState接口。如果它隻讀取對話資料,實作IReadOnlySessionState接口就可以了。
這兩個接口都是标記接口,并沒有包含任何方法。是以,如果你希望激活NewHandler處理程式的對話狀态,要像下面的代碼一樣聲明NewHandler類:
public class NewHandler : IHttpHandler, IRequiresSessionState
HTTP子產品
HTTP子產品是實作了System.Web.IhttpModule接口的.NET元件。這些元件通過在某些事件中注冊自身,把自己插入ASP.NET請求處理管道。當這些事件發生的時候,ASP.NET調用對請求有興趣的HTTP子產品,這樣該子產品就能處理請求了。
HTTP子產品實作了IhttpModule接口的下面一些方法:
方法名稱 描述
Init 這個方法允許HTTP子產品向HttpApplication 對象中的事件注冊自己的事件處理程式。
Dispose 這個方法給予HTTP子產品在對象被垃圾收集之前執行清理的機會。
HTTP子產品可以向System.Web.HttpApplication對象暴露的下面一些方法注冊:
事件名稱 描述
AcquireRequestState 當ASP.NET運作時準備好接收目前HTTP請求的對話狀态的時候引發這個事件。
AuthenticateRequest 當ASP.NET 運作時準備驗證使用者身份的時候引發這個事件。
AuthorizeRequest 當ASP.NET運作時準備授權使用者通路資源的時候引發這個事件。
BeginRequest 當ASP.NET運作時接收到新的HTTP請求的時候引發這個事件。
Disposed 當ASP.NET完成HTTP請求的處理過程時引發這個事件。
EndRequest 把響應内容發送到用戶端之前引發這個事件。
Error 在處理HTTP請求的過程中出現未處理異常的時候引發這個事件。
PostRequestHandlerExecute 在HTTP處理程式結束執行的時候引發這個事件。
PreRequestHandlerExecute 在ASP.NET開始執行HTTP請求的處理程式之前引發這個事件。在這個事件之後,ASP.NET 把該請求轉發給适當的HTTP處理程式。
PreSendRequestContent 在ASP.NET把響應内容發送到用戶端之前引發這個事件。這個事件允許我們在内容到達用戶端之前改變響應内容。我們可以使用這個事件給頁面輸出添加用于所有頁面的内容。例如通用菜單、頭資訊或腳資訊。
PreSendRequestHeaders 在ASP.NET把HTTP響應頭資訊發送給用戶端之前引發這個事件。在頭資訊到達用戶端之前,這個事件允許我們改變它的内容。我們可以使用這個事件在頭資訊中添加cookie和自定義資料。
ReleaseRequestState 當ASP.NET結束所搜有的請求處理程式執行的時候引發這個事件。
ResolveRequestCache 我們引發這個事件來決定是否可以使用從輸出緩沖傳回的内容來結束請求。這依賴于Web應用程式的輸出緩沖時怎樣設定的。
UpdateRequestCache 當ASP.NET完成了目前的HTTP請求的處理,并且輸出内容已經準備好添加給輸出緩沖的時候,引發這個事件。這依賴于Web應用程式的輸出緩沖是如何設定的。
除了這些事件之外,我們還可以使用四個事件。我們可以通過實作Web應用程式的global.asax檔案中一些方法來使用這些事件。
這些事件是:
Application_OnStart
當第一個請求到達Web應用程式的時候引發這個事件。
Application_OnEnd
準備終止應用程式之前引發這個事件。
Session_OnStart
使用者對話的第一個請求引發這個事件。
Session_OnEnd
放棄對話或者對話超期的時候引發這個事件。
在配置檔案中注冊HTTP子產品
當我們建立了HTTP子產品并把它複制到Web應用程式的bin目錄或者全局部件緩沖(Global Assembly Cache)之後,接下來就應該在web.config或machine.config中注冊它了。
我們可以
使用<httpModules>和<add>節點把HTTP子產品添加到Web應用程式中。實際上子產品都使用<add>節點列舉在<httpModules>和</httpModules>節點之内了。
因為配置設定資訊是可以繼承的,是以子目錄從父目錄那兒繼承配置設定資訊。其結果是,子目錄可能繼承了一些不需要的HTTP子產品(它們是父配置資訊的一部分);是以,我們需要一種删除這些不需要的子產品的方法。我們可以使用<remove>節點;如果我們希望删除從應用程式繼承得到的所有HTTP子產品,可以使用<clear>節點。
下面的代碼是添加HTTP子產品的一個通用示例:
<httpModules>
<add type="classname, assemblyname" name="modulename" />
<httpModules>
下面的代碼是從應用程式中删除HTTP子產品的一個通用示例:
<httpModules>
<remove name="modulename" />
<httpModules>
在上面的XML中:
Type屬性用類和部件名稱的形式指定了HTTP子產品的實際類型。
Name屬性指定了子產品的友好名稱。其它應用程式可以使用這個名稱來識别HTTP子產品。
ASP.NET運作時如何使用HTTP子產品
ASP.NET運作時使用HTTP子產品實作某些特殊的功能。下面的片段來自于machine.config檔案,它顯示了ASP.NET運作時安裝的HTTP子產品:
<httpModules>
<add name="OutputCache" type="System.Web.Caching.OutputCacheModule"/>
<add name="Session" type="System.Web.SessionState.SessionStateModule"/>
<add name="WindowsAuthentication"
type="System.Web.Security.WindowsAuthenticationModule"/>
<add name="FormsAuthentication"
type="System.Web.Security.FormsAuthenticationModule"/>
<add name="PassportAuthentication"
type="System.Web.Security.PassportAuthenticationModule"/>
<add name="UrlAuthorization"
type="System.Web.Security.UrlAuthorizationModule"/>
<add name="FileAuthorization"
type="System.Web.Security.FileAuthorizationModule"/>
</httpModules>
ASP.NET使用上面一些HTTP子產品來提供一些服務,例如身份驗證和授權、對話管理和輸出緩沖。由于這些子產品都注冊在machine.config檔案中。
實作一個提供安全服務的HTTP子產品
現在我們實作一個HTTP子產品,它為我們的Web應用程式提供安全服務。該HTTP子產品基本上是提供一種定制的身份認證服務。它将接收HTTP請求中的身份憑證,并确定該憑證是否有效。如果有效,與使用者相關的角色是什麼?通過
User.Identity對象,它把這些角色與通路我們的Web應用程式頁面的使用者的辨別關聯起來。
下面是該HTTP子產品的代碼:
using System;
using System.Web;
using System.Security.Principal;
namespace SecurityModules
{
/// Class1的總體描述。
public class CustomAuthenticationModule : IHttpModule
{
public CustomAuthenticationModule()
{
}
public void Init(HttpApplication r_objApplication)
{
// 向Application 對象注冊事件處理程式。
r_objApplication.AuthenticateRequest +=
new EventHandler(this.AuthenticateRequest) ;
}
public void Dispose()
{
// 此處空出,因為我們不需要做什麼操作。
}
private void AuthenticateRequest(object r_objSender,EventArgs r_objEventArgs)
{
// 鑒别使用者的憑證,并找出使用者角色。。
1. HttpApplication objApp = (HttpApplication) r_objSender ;
2. HttpContext objContext = (HttpContext) objApp.Context ;
3. if ( (objApp.Request["userid"] == null) ||
4. (objApp.Request["password"] == null) )
5. {
6. objContext.Response.Write("<H1>Credentials not provided</H1>") ;
7. objContext.Response.End() ;
8. }
9. string userid = "";
10. userid = objApp.Request["userid"].ToString() ;
11. string password = "";
12. password = objApp.Request["password"].ToString() ;
13. string[] strRoles ;
14. strRoles = AuthenticateAndGetRoles(userid, password) ;
15. if ((strRoles == null) || (strRoles.GetLength(0) == 0))
16. {
17. objContext.Response.Write("<H1>We are sorry but we could not
find this user id and password in our database</H1>") ;
18. objApp.CompleteRequest() ;
19. }
20. GenericIdentity objIdentity = new GenericIdentity(userid,
"CustomAuthentication") ;
21. objContext.User = new GenericPrincipal(objIdentity, strRoles) ;
}
private string[] AuthenticateAndGetRoles(string r_strUserID,string r_strPassword)
{
string[] strRoles = null ;
if ((r_strUserID.Equals("Steve")) &&(r_strPassword.Equals("15seconds")))
{
strRoles = new String[1] ;
strRoles[0] = "Administrator" ;
}
else if ((r_strUserID.Equals("Mansoor")) &&(r_strPassword.Equals("mas")))
{
strRoles = new string[1] ;
strRoles[0] = "User" ;
}
return strRoles ;
}
}
}
我們研究一下上面的代碼。
我們是從Init函數開始的。這個函數把處理程式的AuthenticateRequest事件插入Application(應用程式)對象的事件處理程式清單中。這将導緻引發AuthenticationRequest事件的時候Application調用該方法。
我們的HTTP子產品初始化之後,我們就可以調用它的AuthenticateRequest方法來鑒别用戶端請求。AuthenticateRequest方法是該安全/身份認證機制的核心。在這個函數中:
1和2行提取HttpApplication和HttpContext對象。3到7行檢測是否沒有給我們提供了使用者id或密碼。如果沒有提供,就顯示錯誤資訊,請求處理過程終止。
9到12行從HttpRequest對象中提取使用者id和密碼。
14行調用一個叫做AuthenticateAndGetRoles的輔助(helper)函數。這個函數主要執行身份驗證并決定使用者角色。上面的代碼采用了寫死(hard-coded),隻允許兩個使用者使用,但是我們可以擴充這個方法,并添加代碼與使用者資料庫互動操作并檢索使用者的角色。
16到19行檢測是否有角色與使用者關聯。如果沒有就意味着傳遞給我們的憑證沒有通過驗證;是以該憑證是無效的。是以,給用戶端發送一個錯誤資訊,并且請求結束了。
20和21行非常重要,因為這兩行實際上告訴ASP.NET HTTP運作時已登入使用者的身份。這兩行成功執行以後,我們的aspx頁面就能夠使用User對象通路這些資訊了。
現在我們看一看這種身份驗證機制的運作情況。目前我們隻允許下面兩個使用者登入到系統:
User id = Steve, Password = 15seconds, Role = Administrator
User id = Mansoor, Password = mas, Role = User
注意使用者id和密碼是大小寫敏感的(區分大小寫)。
首先試圖不提供憑證登入系統,在IE中輸入http://localhost/webapp2/index.aspx将看到下面的消息:
現在試圖使用使用者id“Steve”和密碼“15seconds”登入系統。輸入 http://localhost/webapp2/index.aspx?userid=Steve&password=15seconds你将看到下面的歡迎消息:
現在試圖使用使用者id“Mansoor”和秘碼“mas”登入系統。輸入http://localhost/webapp2/index.aspx?userid=Mansoor&password=mas你将看到下面的歡迎消息頁面:
現在試圖使用錯誤的使用者id和密碼組合來登入系統。輸入http://localhost/webapp2/index.aspx?userid=Mansoor&password=xyz你将看到下面的錯誤消息:
這表明我們的安全子產品在起作用了。你可以通過在AuthenticateAndGetRoles方法中使用資料庫通路代碼來擴充該安全子產品。
要使所有的部分都起作用,我們必須對web.config檔案進行一些修改。首先,由于我們要使用自己的身份驗證,是以不需要其它的身份驗證機制。為了達到這個目的,改變webapp2的web.config檔案中的<authentication>節點,如下所示:
<authentication mode="None"/>
類似地,不允許匿名使用者通路我們的Web站點。給web.config檔案添加下面的語句:
<authorization>
<deny users="?"/>
</authorization>
用于至少能夠匿名通路用于提供憑證的檔案。在web.config檔案中使用下面的配置設定資訊把index.aspx作為唯一能夠匿名通路的檔案:
<location path="index.aspx">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
結論
你可能已經意識到有了HTTP處理程式和子產品後,ASP.NET已經給開發者提供了強大的能量。把你自己的元件插入ASP.NET請求處理管道,享受它的優點吧。
作為練習,你應該進一步改程序式,使示例身份驗證子產品更加靈活,并能根據使用者的需要進行調整。
請注意在http請求的處理過程中,隻能調用一個HTTP處理程式,然而可以調用多個HTTP子產品。
Http處理程式
HTTP處理程式是實作了System.Web.IHttpHandler接口的.NET元件。任何實作了IHttpHandler接口的類都可以用于處理輸入的HTTP請求。HTTP處理程式與ISAPI擴充有些類似。HTTP處理程式和ISAPI擴充的差别在于在URL中可以使用HTTP處理程式的檔案名稱直接調用它們,與ISAPI擴充類似。
HTTP處理程式實作了下列方法:
方法名稱 描述
ProcessRequest 這個方法實際上是http處理程式的核心。我們調用這個方法來處理http請求。
IsReusable 我們調用這個屬性來決定http處理程式的執行個體是否可以用于處理相同其它類型的請求。HTTP處理程式可以傳回true或false來表明它們是否可以重複使用。
你可以使用web.config或者machine.config檔案把這些類映射到http請求上。映射完成以後,當接收到相應請求的時候ASP.NET會執行個體化http處理程式。我們将解釋如何在web.config和/或machine.config檔案中定義所有這些細節資訊。
ASP.NET還通過IHttpHandlerFactory接口支援http處理程式的擴充。ASP.NET提供了把http請求路由到實作IHttpHandlerFactory接口的類的對象上的能力。此外,ASP.NET還利用了Factory設計模式。這種模式為建立一組相關對象而不提供具體類的功能提供了接口。簡單的說,你可以把用于建立依賴傳遞進來的參數建立的http處理程式對象的類看作是factory(工廠)。我們不用指定需要執行個體化的特定的http處理程式;http處理程式工廠處理這種事務。這樣做的優點在于如果未來實作IHttpHandler接口的對象的實作方法發生了改變,隻要接口仍然相同,用戶端就不會受到影響。
下面是IHttpHandlerFactory接口中的方法清單:
方法名稱 描述
GetHandler 這個方法負責建立适當的處理程式并把它的指針傳回到調用代碼(ASP.NET運作時)。這個方法傳回的處理程式對象應該實作了IHttpHandler接口。
ReleaseHandler 這個方法負責在請求處理完成後釋放http處理程式。Factory 實作決定了它的操作。Factory 實作可以是實際摧毀執行個體,也可以把它放入緩沖池供以後使用。
在配置檔案中注冊HTTP處理程式和HTTP處理程式工廠
ASP.NET在下面的配置檔案中維護自己的配置資訊:
machine.config
web.config
machine.config檔案包含應用于計算機上安裝的所有Web應用程式的配置設定資訊。
web.config檔案對于每個Web應用程式來說是特定的。每個Web應用程式都有自己的web.config檔案。Web應用程式的任何子目錄也可能包含自己的web.config檔案;這使得它們能夠覆寫父目錄的設定資訊。
為了給我們的Web應用程式添加HTTP處理程式,你可以使用<httpHandlers>和<add>節點。實際上,處理程式都帶有<add>節點,列舉在<httpHandlers>和</httpHandlers>節點之間。下面是添加HTTP處理程式的一個普通的例子:
<httpHandlers>
<add verb="supported http verbs" path="path" type="namespace.classname, assemblyname" />
<httpHandlers>
在上面的XML中,
Verb屬性指定了處理程式支援的HTTP動作。如果某個處理程式支援所有的HTTP動作,請使用“*”,否則使用逗号分隔的清單列出支援的動作。是以如果你的處理程式隻支援HTTP GET和POST,那麼verb屬性就應該是“GET, POST”。
Path屬性指定了需要調用處理程式的路徑和檔案名(可以包含通配符)。例如,如果你希望自己的處理程式隻有在test.xyz檔案被請求的時候才被調用,那麼path屬性就包含“test.xyz”,如果你希望含有.xyz字尾的所有檔案都調用處理程式,path屬性應該包含“*.xyz”。
Type屬性用名字空間、類名稱和部件名稱的組合形式指定處理程式或處理程式工廠的實際類型。ASP.NET運作時首先搜尋應用程式的bin目錄中的部件DLL,接着在全局部件緩沖(GAC)中搜尋。
ASP.NET運作時對HTTP處理程式的使用方式
無論你是否相信,ASP.NET都使用HTTP請求實作了大量的自己的功能。ASP.NET使用處理程式來處理.aspx、 .asmx、 .soap和其它ASP.NET檔案。
下面是 machine.config檔案中的一個片段:
<httpHandlers>
<add verb="*" path="trace.axd" type="System.Web.Handlers.TraceHandler"/>
<add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory"/>
<add verb="*" path="*.ashx" type="System.Web.UI.SimpleHandlerFactory"/>
<add verb="*" path="*.config" type="System.Web.HttpForbiddenHandler"/>
<add verb="GET,HEAD" path="*" type="System.Web.StaticFileHandler"/>
. . . . . .
. . . . . .
</httpHandlers>
在上面的配置資訊中你可以看到對.aspx檔案的所有請求都由System.Web.UI.PageHandlerFactory類來處理。與此類似,對.config檔案和其它檔案(它們不能被用戶端直接通路)的所有請求都由System.Web.HttpForbiddenHandler類處理。你可能已經猜到,當通路這些檔案的時候,該類簡單地給用戶端傳回一個錯誤資訊。
執行HTTP處理程式
現在你将看到如何實作一個HTTP處理程式。那麼我們的新處理程式要做什麼任務呢?前面我提到,處理程式大多數用于給Web伺服器添加新功能;是以,我将建立一個處理程式來處理新的檔案類型——擴充名為.15seconds的檔案。我們建立了這個處理程式并在我們的Web應用程式的web.config檔案中注冊之後,所有對.15seconds檔案的請求都将由這個新處理程式來處理。
你可能正在考慮這個處理程式的使用方法。如果你希望引入一種新的伺服器腳本語言或動态伺服器檔案(例如asp、aspx)該怎麼辦呢?你可以為它編寫一個自己的處理程式。類似地,如果你希望在IIS上運作Java小程式、JSP和其它一些伺服器端Java元件應該怎麼辦呢?一種方法是安裝某些ISAPI擴充(例如Allaire或Macromedia Jrun)。你也可以編寫自己的HTTP處理程式。盡管這對于第三方廠商(例如Allaire和Macromedia)來說是很複雜的事務,但是它卻是個很有吸引力的選擇,因為它們的HTTP處理能夠能夠通路ASP.NET運作時暴露的所有新功能。
實作我們的HTTP處理程式包含以下步驟:
1.編寫一個實作IHttpHandler接口的類。
2. 在web.config或machine.config檔案中注冊這個處理程式。
3.在Internet服務管理器中把檔案擴充(.15seconds)映射到ASP.NET ISAPI擴充DLL(aspnet_isapi.dll)上。
第一步
在Visual Studio.NET中建立一個新的C#類庫項目,并把它命名為“MyHandler”。Visual Studio.NET将自動地給項目添加一個叫做“Class1.cs”的類。把它改名為“NewHandler”;在代碼視窗中打開這個類,并把類的名稱和構造函數的名稱改成“NewHandler”。
下面是NewHandler類的代碼:
using System;
using System.Web;
namespace MyHandler
{
public class NewHandler : IHttpHandler
{
public NewHandler()
{
// TODO: 此處添加構造邏輯
}
#region Implementation of IHttpHandler
public void ProcessRequest(System.Web.HttpContext context)
{
HttpResponse objResponse = context.Response ;
objResponse.Write("<html><body><h1>Hello 15Seconds Reader ") ;
objResponse.Write("</body></html>") ;
}
public bool IsReusable
{
get
{
return true;
}
}
#endregion
}
}
你在ProcessRequest方法中可以看到,該HTTP處理程式通過System.Web.HttpContext對象通路了所有作為參數傳遞給它的ASP.NET内部對象。實作ProcessRequest方法隻需要簡單地從context對象中提取HttpResponse對象并把發送一些HTML給用戶端。類似地,IsReusable傳回true,表明這個處理程式可以被重複用作處理其它的HTTP請求。
我們編譯上面的代碼并把它放到webapp虛拟目錄的bin目錄之中。
第二步
在web.config檔案中通過添加下面的文本來注冊這個處理程式:
<httpHandlers>
<add verb="*" path="*.15seconds" type="MyHandler.NewHandler,MyHandler"/>
</httpHandlers>
第三步
由于我們已經建立了用于處理新擴充檔案的處理程式了,我們還需要把這個擴充名告訴IIS并把它映射到ASP.NET。如果你不執行這個步驟而試圖通路Hello.15seconds檔案,IIS将簡單地傳回該檔案而不是把它傳遞給ASP.NET運作時。其結果是該HTTP處理程式不會被調用。
運作Internet服務管理器,右鍵點選預設Web站點,選擇屬性,移動到Home目錄選項頁,并點選配置按鈕。應用程式配置對話框彈出來了。點選添加按鈕并在可執行字段輸入aspnet_isapi.dll檔案路徑,在擴充字段輸入.15seconds。其它字段不用處理;該對話框如下所示:
點選确認按鈕關閉應用程式配置和預設Web站點屬性對話框。
現在我們運作Internet Explorer并輸入url:http://localhost/webapp/hello.15seconds,看到的頁面如下:
HTTP處理程式中的對話狀态
維護對話狀态是Web應用程式執行的最通常的事務。HTTP處理程式也需要通路這些對話狀态。但是HTTP處理程式的預設設定是沒有激活對話狀态的。為了讀取和/或寫入狀态資料,需要HTTP處理程式實作下面的接口之一:
IRequiresSessionState
IReadOnlySessionState.
當HTTP處理程式需要讀寫對話資料的時候,它必須實作IRequiresSessionState接口。如果它隻讀取對話資料,實作IReadOnlySessionState接口就可以了。
這兩個接口都是标記接口,并沒有包含任何方法。是以,如果你希望激活NewHandler處理程式的對話狀态,要像下面的代碼一樣聲明NewHandler類:
public class NewHandler : IHttpHandler, IRequiresSessionState
HTTP子產品
HTTP子產品是實作了System.Web.IhttpModule接口的.NET元件。這些元件通過在某些事件中注冊自身,把自己插入ASP.NET請求處理管道。當這些事件發生的時候,ASP.NET調用對請求有興趣的HTTP子產品,這樣該子產品就能處理請求了。
HTTP子產品實作了IhttpModule接口的下面一些方法:
方法名稱 描述
Init 這個方法允許HTTP子產品向HttpApplication 對象中的事件注冊自己的事件處理程式。
Dispose 這個方法給予HTTP子產品在對象被垃圾收集之前執行清理的機會。
HTTP子產品可以向System.Web.HttpApplication對象暴露的下面一些方法注冊:
事件名稱 描述
AcquireRequestState 當ASP.NET運作時準備好接收目前HTTP請求的對話狀态的時候引發這個事件。
AuthenticateRequest 當ASP.NET 運作時準備驗證使用者身份的時候引發這個事件。
AuthorizeRequest 當ASP.NET運作時準備授權使用者通路資源的時候引發這個事件。
BeginRequest 當ASP.NET運作時接收到新的HTTP請求的時候引發這個事件。
Disposed 當ASP.NET完成HTTP請求的處理過程時引發這個事件。
EndRequest 把響應内容發送到用戶端之前引發這個事件。
Error 在處理HTTP請求的過程中出現未處理異常的時候引發這個事件。
PostRequestHandlerExecute 在HTTP處理程式結束執行的時候引發這個事件。
PreRequestHandlerExecute 在ASP.NET開始執行HTTP請求的處理程式之前引發這個事件。在這個事件之後,ASP.NET 把該請求轉發給适當的HTTP處理程式。
PreSendRequestContent 在ASP.NET把響應内容發送到用戶端之前引發這個事件。這個事件允許我們在内容到達用戶端之前改變響應内容。我們可以使用這個事件給頁面輸出添加用于所有頁面的内容。例如通用菜單、頭資訊或腳資訊。
PreSendRequestHeaders 在ASP.NET把HTTP響應頭資訊發送給用戶端之前引發這個事件。在頭資訊到達用戶端之前,這個事件允許我們改變它的内容。我們可以使用這個事件在頭資訊中添加cookie和自定義資料。
ReleaseRequestState 當ASP.NET結束所搜有的請求處理程式執行的時候引發這個事件。
ResolveRequestCache 我們引發這個事件來決定是否可以使用從輸出緩沖傳回的内容來結束請求。這依賴于Web應用程式的輸出緩沖時怎樣設定的。
UpdateRequestCache 當ASP.NET完成了目前的HTTP請求的處理,并且輸出内容已經準備好添加給輸出緩沖的時候,引發這個事件。這依賴于Web應用程式的輸出緩沖是如何設定的。
除了這些事件之外,我們還可以使用四個事件。我們可以通過實作Web應用程式的global.asax檔案中一些方法來使用這些事件。
這些事件是:
Application_OnStart
當第一個請求到達Web應用程式的時候引發這個事件。
Application_OnEnd
準備終止應用程式之前引發這個事件。
Session_OnStart
使用者對話的第一個請求引發這個事件。
Session_OnEnd
放棄對話或者對話超期的時候引發這個事件。
在配置檔案中注冊HTTP子產品
當我們建立了HTTP子產品并把它複制到Web應用程式的bin目錄或者全局部件緩沖(Global Assembly Cache)之後,接下來就應該在web.config或machine.config中注冊它了。
我們可以
使用<httpModules>和<add>節點把HTTP子產品添加到Web應用程式中。實際上子產品都使用<add>節點列舉在<httpModules>和</httpModules>節點之内了。
因為配置設定資訊是可以繼承的,是以子目錄從父目錄那兒繼承配置設定資訊。其結果是,子目錄可能繼承了一些不需要的HTTP子產品(它們是父配置資訊的一部分);是以,我們需要一種删除這些不需要的子產品的方法。我們可以使用<remove>節點;如果我們希望删除從應用程式繼承得到的所有HTTP子產品,可以使用<clear>節點。
下面的代碼是添加HTTP子產品的一個通用示例:
<httpModules>
<add type="classname, assemblyname" name="modulename" />
<httpModules>
下面的代碼是從應用程式中删除HTTP子產品的一個通用示例:
<httpModules>
<remove name="modulename" />
<httpModules>
在上面的XML中:
Type屬性用類和部件名稱的形式指定了HTTP子產品的實際類型。
Name屬性指定了子產品的友好名稱。其它應用程式可以使用這個名稱來識别HTTP子產品。
ASP.NET運作時如何使用HTTP子產品
ASP.NET運作時使用HTTP子產品實作某些特殊的功能。下面的片段來自于machine.config檔案,它顯示了ASP.NET運作時安裝的HTTP子產品:
<httpModules>
<add name="OutputCache" type="System.Web.Caching.OutputCacheModule"/>
<add name="Session" type="System.Web.SessionState.SessionStateModule"/>
<add name="WindowsAuthentication"
type="System.Web.Security.WindowsAuthenticationModule"/>
<add name="FormsAuthentication"
type="System.Web.Security.FormsAuthenticationModule"/>
<add name="PassportAuthentication"
type="System.Web.Security.PassportAuthenticationModule"/>
<add name="UrlAuthorization"
type="System.Web.Security.UrlAuthorizationModule"/>
<add name="FileAuthorization"
type="System.Web.Security.FileAuthorizationModule"/>
</httpModules>
ASP.NET使用上面一些HTTP子產品來提供一些服務,例如身份驗證和授權、對話管理和輸出緩沖。由于這些子產品都注冊在machine.config檔案中。
實作一個提供安全服務的HTTP子產品
現在我們實作一個HTTP子產品,它為我們的Web應用程式提供安全服務。該HTTP子產品基本上是提供一種定制的身份認證服務。它将接收HTTP請求中的身份憑證,并确定該憑證是否有效。如果有效,與使用者相關的角色是什麼?通過
User.Identity對象,它把這些角色與通路我們的Web應用程式頁面的使用者的辨別關聯起來。
下面是該HTTP子產品的代碼:
using System;
using System.Web;
using System.Security.Principal;
namespace SecurityModules
{
/// Class1的總體描述。
public class CustomAuthenticationModule : IHttpModule
{
public CustomAuthenticationModule()
{
}
public void Init(HttpApplication r_objApplication)
{
// 向Application 對象注冊事件處理程式。
r_objApplication.AuthenticateRequest +=
new EventHandler(this.AuthenticateRequest) ;
}
public void Dispose()
{
// 此處空出,因為我們不需要做什麼操作。
}
private void AuthenticateRequest(object r_objSender,EventArgs r_objEventArgs)
{
// 鑒别使用者的憑證,并找出使用者角色。。
1. HttpApplication objApp = (HttpApplication) r_objSender ;
2. HttpContext objContext = (HttpContext) objApp.Context ;
3. if ( (objApp.Request["userid"] == null) ||
4. (objApp.Request["password"] == null) )
5. {
6. objContext.Response.Write("<H1>Credentials not provided</H1>") ;
7. objContext.Response.End() ;
8. }
9. string userid = "";
10. userid = objApp.Request["userid"].ToString() ;
11. string password = "";
12. password = objApp.Request["password"].ToString() ;
13. string[] strRoles ;
14. strRoles = AuthenticateAndGetRoles(userid, password) ;
15. if ((strRoles == null) || (strRoles.GetLength(0) == 0))
16. {
17. objContext.Response.Write("<H1>We are sorry but we could not
find this user id and password in our database</H1>") ;
18. objApp.CompleteRequest() ;
19. }
20. GenericIdentity objIdentity = new GenericIdentity(userid,
"CustomAuthentication") ;
21. objContext.User = new GenericPrincipal(objIdentity, strRoles) ;
}
private string[] AuthenticateAndGetRoles(string r_strUserID,string r_strPassword)
{
string[] strRoles = null ;
if ((r_strUserID.Equals("Steve")) &&(r_strPassword.Equals("15seconds")))
{
strRoles = new String[1] ;
strRoles[0] = "Administrator" ;
}
else if ((r_strUserID.Equals("Mansoor")) &&(r_strPassword.Equals("mas")))
{
strRoles = new string[1] ;
strRoles[0] = "User" ;
}
return strRoles ;
}
}
}
我們研究一下上面的代碼。
我們是從Init函數開始的。這個函數把處理程式的AuthenticateRequest事件插入Application(應用程式)對象的事件處理程式清單中。這将導緻引發AuthenticationRequest事件的時候Application調用該方法。
我們的HTTP子產品初始化之後,我們就可以調用它的AuthenticateRequest方法來鑒别用戶端請求。AuthenticateRequest方法是該安全/身份認證機制的核心。在這個函數中:
1和2行提取HttpApplication和HttpContext對象。3到7行檢測是否沒有給我們提供了使用者id或密碼。如果沒有提供,就顯示錯誤資訊,請求處理過程終止。
9到12行從HttpRequest對象中提取使用者id和密碼。
14行調用一個叫做AuthenticateAndGetRoles的輔助(helper)函數。這個函數主要執行身份驗證并決定使用者角色。上面的代碼采用了寫死(hard-coded),隻允許兩個使用者使用,但是我們可以擴充這個方法,并添加代碼與使用者資料庫互動操作并檢索使用者的角色。
16到19行檢測是否有角色與使用者關聯。如果沒有就意味着傳遞給我們的憑證沒有通過驗證;是以該憑證是無效的。是以,給用戶端發送一個錯誤資訊,并且請求結束了。
20和21行非常重要,因為這兩行實際上告訴ASP.NET HTTP運作時已登入使用者的身份。這兩行成功執行以後,我們的aspx頁面就能夠使用User對象通路這些資訊了。
現在我們看一看這種身份驗證機制的運作情況。目前我們隻允許下面兩個使用者登入到系統:
User id = Steve, Password = 15seconds, Role = Administrator
User id = Mansoor, Password = mas, Role = User
注意使用者id和密碼是大小寫敏感的(區分大小寫)。
首先試圖不提供憑證登入系統,在IE中輸入http://localhost/webapp2/index.aspx将看到下面的消息:
現在試圖使用使用者id“Steve”和密碼“15seconds”登入系統。輸入 http://localhost/webapp2/index.aspx?userid=Steve&password=15seconds你将看到下面的歡迎消息:
現在試圖使用使用者id“Mansoor”和秘碼“mas”登入系統。輸入http://localhost/webapp2/index.aspx?userid=Mansoor&password=mas你将看到下面的歡迎消息頁面:
現在試圖使用錯誤的使用者id和密碼組合來登入系統。輸入http://localhost/webapp2/index.aspx?userid=Mansoor&password=xyz你将看到下面的錯誤消息:
這表明我們的安全子產品在起作用了。你可以通過在AuthenticateAndGetRoles方法中使用資料庫通路代碼來擴充該安全子產品。
要使所有的部分都起作用,我們必須對web.config檔案進行一些修改。首先,由于我們要使用自己的身份驗證,是以不需要其它的身份驗證機制。為了達到這個目的,改變webapp2的web.config檔案中的<authentication>節點,如下所示:
<authentication mode="None"/>
類似地,不允許匿名使用者通路我們的Web站點。給web.config檔案添加下面的語句:
<authorization>
<deny users="?"/>
</authorization>
用于至少能夠匿名通路用于提供憑證的檔案。在web.config檔案中使用下面的配置設定資訊把index.aspx作為唯一能夠匿名通路的檔案:
<location path="index.aspx">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>
結論
你可能已經意識到有了HTTP處理程式和子產品後,ASP.NET已經給開發者提供了強大的能量。把你自己的元件插入ASP.NET請求處理管道,享受它的優點吧。
作為練習,你應該進一步改程序式,使示例身份驗證子產品更加靈活,并能根據使用者的需要進行調整。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=540688
本文轉自
http://blog.csdn.net/taito/archive/2005/12/01/540688.aspx