本文內容 引入 IHttpModule 概述 創建自定義 HTTP 模塊的步驟 演示創建自定義 HTTP 模塊 HTTP 模塊的工作方式 HTTP 模塊與 Global.asax 文件 參考資料 引入 本文在 VS 2008 和 IIS 6 環境下概述如何利用 IHttpModule 自定義 HTT ...
本文內容
- 引入
- IHttpModule 概述
- 創建自定義 HTTP 模塊的步驟
- 演示創建自定義 HTTP 模塊
- HTTP 模塊的工作方式
- HTTP 模塊與 Global.asax 文件
- 參考資料
引入
本文在 VS 2008 和 IIS 6 環境下概述如何利用 IHttpModule 自定義 HTTP 模塊。
當我們在 VS 2008 里新建一個 Web 應用程式項目後,會在 Web.config 文件看到如下一個配置:
<httpModules>
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpModules>
我們知道 VS 2008 已經可以開發 Ajax 程式,非同步刷新界面,如 ScriptManager 控制項、UpdatePanel 控制項,那麼 System.Web.Handlers.ScriptModule 類就是管理用於 ASP.NET 中 AJAX 功能的 HTTP 模塊。
更進一步,看它 C# 聲明:
[AspNetHostingPermissionAttribute(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermissionAttribute(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
public class ScriptModule : IHttpModule
該類繼承了 IHttpModule 介面。代碼如下所示。看看它的事件,你多少體會出如若讓 Web 應用程式支持 Ajax 功能,發出非同步請求,需要做什麼,至少能夠檢查傳入和傳出的請求。
//
// ScriptModule.cs
//
// Author:
// Igor Zelmanovich <[email protected]>
//
// (C) 2007 Mainsoft, Inc. http://www.mainsoft.com
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Web.Script.Services;
namespace System.Web.Handlers
{
public class ScriptModule : IHttpModule
{
protected virtual void Init (HttpApplication context) {
context.PreSendRequestHeaders += new EventHandler (PreSendRequestHeaders);
context.PostAcquireRequestState += new EventHandler (PostAcquireRequestState);
context.AuthenticateRequest += new EventHandler (AuthenticateRequest);
}
void AuthenticateRequest (object sender, EventArgs e) {
// The AuthenticateRequest event is raised after the identity of the current user has been
// established. The handler for this event sets the SkipAuthorization property of the HttpContext
// for the current request. This property is checked in the authorization module to see
// if it has to omit authorization checking for the requested url. Usually an HttpModule
// use this property to allow anonymous access to some resources (for example,
// the Login Page if we’re using forms authentication). In our scenario,
// the ScriptModule sets the SkipAuthorization to true if the requested url is
// scriptresource.axd or if the authorization module is enabled and the request is a rest
// request to the authorization web service.
}
void PostAcquireRequestState (object sender, EventArgs e) {
// The PostAcquireRequestState event is raised after the session data has been obtained.
// If the request is for a class that implements System.Web.UI.Page and it is a rest
// method call, the WebServiceData class (that was explained in a previous post) is used
// to call the requested method from the Page. After the method has been called,
// the CompleteRequest method is called, bypassing all pipeline events and executing
// the EndRequest method. This allows MS AJAX to be able to call a method on a page
// instead of having to create a web service to call a method.
HttpApplication app = (HttpApplication) sender;
HttpContext context = app.Context;
if (context == null)
return;
HttpRequest request = context.Request;
string contentType = request.ContentType;
IHttpHandler currentHandler = context.CurrentHandler;
if (currentHandler == null)
return;
#if TARGET_J2EE
if (!(currentHandler is Page) && currentHandler is IServiceProvider) {
pageType = (Type) ((IServiceProvider) currentHandler).GetService (typeof (Type));
if (pageType == null)
return;
}
#endif
Type pageType = currentHandler.GetType ();
if (typeof (Page).IsAssignableFrom (pageType) && !String.IsNullOrEmpty (contentType) && contentType.StartsWith ("application/json", StringComparison.OrdinalIgnoreCase)) {
IHttpHandler h = RestHandler.GetHandler (context, pageType, request.FilePath);
h.ProcessRequest (context);
app.CompleteRequest ();
}
}
void PreSendRequestHeaders (object sender, EventArgs e) {
HttpApplication app = (HttpApplication) sender;
HttpContext context = app.Context;
if (context.Request.Headers ["X-MicrosoftAjax"] == "Delta=true") {
Page p = context.CurrentHandler as Page;
#if TARGET_J2EE
if (p == null && context.CurrentHandler is IServiceProvider)
p = (Page) ((IServiceProvider) context.CurrentHandler).GetService (typeof (Page));
#endif
if (p == null)
return;
ScriptManager sm = ScriptManager.GetCurrent (p);
if (sm == null)
return;
if (context.Response.StatusCode == 302) {
context.Response.StatusCode = 200;
context.Response.ClearContent ();
if (context.Error == null || sm.AllowCustomErrorsRedirect)
ScriptManager.WriteCallbackRedirect (context.Response.Output, context.Response.RedirectLocation);
else
sm.WriteCallbackException (context.Response.Output, context.Error, false);
}
else if (context.Error != null) {
context.Response.StatusCode = 200;
context.Response.ClearContent ();
sm.WriteCallbackException (context.Response.Output, context.Error, true);
}
}
}
protected virtual void Dispose () {
}
#region IHttpModule Members
void IHttpModule.Dispose () {
Dispose ();
}
void IHttpModule.Init (HttpApplication context) {
Init (context);
}
#endregion
}
}
IHttpModule 概述
HTTP 模塊是一個在每次針對應用程式發出請求時調用的程式集。HTTP 模塊作為 ASP.NET 請求管道的一部分調用,它們能夠在整個請求過程中訪問生命周期事件。HTTP 模塊使您可以檢查傳入和傳出的請求並根據該請求進行操作。
HTTP 模塊通常具有以下用途:
- 安全 因為您可以檢查傳入的請求,所以 HTTP 模塊可以在調用請求頁、XML Web services 或處理程式之前執行自定義的身份驗證或其他安全檢查。
- 統計信息和日誌記錄 因為 HTTP 模塊是在每次請求時調用的,所以,您可以將請求統計信息和日誌信息收集到一個集中的模塊中,而不是收集到各頁中。
- 自定義的頁眉或頁腳 因為您可以修改傳出響應,所以可以在每一個頁面或 XML Web services 響應中插入內容,如自定義的標頭信息。
創建自定義 HTTP 模塊的步驟
編寫 HTTP 模塊的一般過程如下:
- 創建一個實現 IHttpModule 介面的類。
- 實現 Init 方法。該方法初始化模塊,並創建所需的任何應用程式事件。例如,如果希望為響應附加一些內容,可以創建 EndRequest 事件。如果希望執行自定義身份驗證邏輯,可以創建 AuthenticateRequest 事件。
- 為已創建的事件編寫代碼。
- 若模塊需要釋放,還可以實現 Dispose 方法。
- 在 Web.config 文件中註冊模塊。
演示創建自定義 HTTP 模塊
將一個字元串追加到響應的開頭和末尾。在對其擴展名已分配給 ASP.NET 的文件進行任何請求的過程中,該模塊都將自動運行。
- 新建 Web 項目。
- 新建名為 HelloWorldModule 的類,該類繼承 IHttpModule,如下所示:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace HttpModuleDemo
{
public class HelloWorldModule : IHttpModule
{
public HelloWorldModule()
{
}
public String ModuleName
{
get { return "HelloWorldModule"; }
}
// In the Init function, register for HttpApplication
// events by adding your handlers.
public void Init(HttpApplication application)
{
application.BeginRequest +=
(new EventHandler(this.Application_BeginRequest));
application.EndRequest +=
(new EventHandler(this.Application_EndRequest));
}
private void Application_BeginRequest(Object source,
EventArgs e)
{
// Create HttpApplication and HttpContext objects to access
// request and response properties.
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
string filePath = context.Request.FilePath;
string fileExtension =
VirtualPathUtility.GetExtension(filePath);
if (fileExtension.Equals(".aspx"))
{
context.Response.Write("<h1><font color=red>" +
"HelloWorldModule: Beginning of Request" +
"</font></h1><hr>");
}
}
private void Application_EndRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
string filePath = context.Request.FilePath;
string fileExtension =
VirtualPathUtility.GetExtension(filePath);
if (fileExtension.Equals(".aspx"))
{
context.Response.Write("<hr><h1><font color=red>" +
"HelloWorldModule: End of Request</font></h1>");
}
}
public void Dispose() { }
}
}
- 在 Web.config 文件註冊該模塊,如下所示:
<httpModules>
<add name="HelloWorldModule" type="HttpModuleDemo.HelloWorldModule"/>
</httpModules>
- 新建一個頁面,並用瀏覽器查看該頁面,頁面會顯示如下信息。
HTTP 模塊的工作方式
模塊必須註冊才能從請求管道接收通知。註冊 HTTP 模塊的最常用方法是在應用程式的 Web.config 文件中進行註冊。
當 ASP.NET 創建表示您的應用程式的 HttpApplication 類的實例時,將創建已註冊的任何模塊的實例。在創建模塊時,將調用它的 Init 方法,並且模塊會自行初始化。
當這些應用程式事件被引發時,將調用模塊中的相應方法。該方法可以執行所需的任何邏輯。如檢查身份驗證或記錄請求信息。在事件處理過程中,模塊能訪問當前請求的 Context 屬性。這使得可以將請求重定向到其他頁、修改請求或者執行任何其他請求操作。例如,若模塊檢查身份驗證,則在憑據不正確的情況下,模塊可能重定向到登錄頁或錯誤頁。否則,當模塊的事件處理程式完成運行時,ASP.NET 會調用管道中的下一個進程。這可能是另一個模塊,也可能是用於該請求的 HTTP 處理程式(如 .aspx 文件)。
HTTP 模塊與 Global.asax 文件
之所以把它們放在一起,是因為在某種程度上,它們功能有相似之處。
可以在應用程式的 Global.asax 文件中實現模塊的許多功能,這使你可以響應應用程式事件。但是,
- 模塊相對於 Global.asax 文件具有如下優點:模塊可以進行封裝。創建一次後在其他應用程式中使用。通過將它們添加到全局程式集緩存,並將其註冊到 Machine.config 文件中,可以跨應用程式重新使用它們。
- 使用 Global.asax 文件的好處在於可以處理其他已註冊事件,如 Session_Start 和 Session_End。此外,Global.asax 文件還允許您實例化可在整個應用程式中使用的全局對象。
當您必須創建依賴應用程式事件的代碼,並且符合以下條件時,都應該使用模塊:
- 希望在其他應用程式中重用該模塊。
- 希望避免將複雜代碼放在 Global.asax 文件中。
- 該模塊應用於管道中的所有請求(僅限 IIS 7.0 集成模式)。
當您必須創建依賴應用程式事件的代碼並且不需要跨應用程式重用代碼時,應該將代碼添加到 Global.asax 文件中。當必須訂閱對於模塊不可用的事件(如 Session_Start)時,也可以使用 Global.asax 文件。
參考資料
MSDN HTTP 處理程式和 HTTP 模塊概述 http://msdn.microsoft.com/zh-cn/library/bb398986(v=VS.90).aspx
MSDN IHttpModule http://msdn.microsoft.com/zh-cn/library/system.web.ihttpmodule(v=VS.90).aspx