HttpModule的認識與深入理解

weixin_34344677發表於2009-10-19

HttpModule是向實現類提供模組初始化和處置事件。當一個HTTP請求到達HttpModule時,整個ASP.NET Framework系統還並沒有對這個HTTP請求做任何處理,也就是說此時對於HTTP請求來講,HttpModule是一個HTTP請求的“必經之路”,所以可以在這個HTTP請求傳遞到真正的請求處理中心(HttpHandler)之前附加一些需要的資訊在這個HTTP請求資訊之上,或者針對截獲的這個HTTP請求資訊作一些額外的工作,或者在某些情況下乾脆終止滿足一些條件的HTTP請求,從而可以起到一個Filter過濾器的作用。


首先你要實現IHttpModule介面這個介面只有兩個方法,一個是Init方法一個Dispose方法.

ContractedBlock.gifExpandedBlockStart.gifCode
using System;

namespace System.Web
{
    
// Summary:
    
//     Provides module initialization and disposal events to the implementing class.
    public interface IHttpModule
    {
        
// Summary:
        
//     Disposes of the resources (other than memory) used by the module that implements
        
//     System.Web.IHttpModule.
        void Dispose();
        
//
        
// Summary:
        
//     Initializes a module and prepares it to handle requests.
        
//
        
// Parameters:
        
//   context:
        
//     An System.Web.HttpApplication that provides access to the methods, properties,
        
//     and events common to all application objects within an ASP.NET application
        void Init(HttpApplication context);
    }
}

 

一個HTTP請求在HttpModule容器的傳遞過程中,會在某一時刻(ResolveRequestCache事件)將這個HTTP請求傳遞給HttpHandler容器。在這個事件之後,HttpModule容器會建立一個HttpHandler的入口例項,但是此時並沒有將HTTP請求控制權交出,而是繼續觸發AcquireRequestState事件以及PreRequestHandlerExcute事件。在PreRequestHandlerExcute事件之後,HttpModule視窗就會將控制權暫時交給HttpHandler容器,以便進行真正的HTTP請求處理工作。


而在HttpHandler容器內部會執行ProcessRequest方法來處理HTTP請求。在容器HttpHandler處理完畢整個HTTP請求之後,會將控制權交還給HttpModule,HttpModule則會繼續對處理完畢的HTTP請求資訊流進行層層的轉交動作,直到返回到客戶端為止。


HttpModule過程在下面的事件:
BeginRequest    指示請求處理開始。
AuthenticateRequest PostAuthenticateRequest    封裝請求身份驗證過程。
AuthorizeRequest   PostAuthorizeRequest    封裝請求授權過程。 
ResolveRequestCache PostResolveRequestCache    封裝檢查是否能利用以前快取的輸出頁面處理請求的過程。
PostMapRequestHandler    指示已發現用於處理請求的 HTTP 處理程式。
AcquireRequestState    PostAcquireRequestState    封裝對請求會話狀態的檢索。
PostRequestHandlerExecute    指示用於處理請求的 HTTP 處理程式已執行。
ReleaseRequestState   PostReleaseRequestState    封裝對請求會話狀態的釋出。
UpdateRequestCache    PostUpdateRequestCache    封裝檢查是否應對請求的資源的輸出進行快取以備今後重複使用的過程。
EndRequest    指示請求處理結束。

 

可以利用HttpModule通過呼叫HttpApplication.CompleteRequest()方法實現當滿足某一個條件時終止此次的HTTP請求。需要注意的是,即使呼叫了HttpApplication.CompleteRequest()方法終止了一個HTTP請求,ASP.NET Framework仍然會觸發HttpApplication後面的這3個事件:EndRequest事件、PreSendRequestHeaders事件、PreSendRequestContent事件。
如果存在多個自定義的HttpModule的話,當Module1終止了一個HTTP請求,這個HTTP請求將不會再觸發Module2中相應的事件了,但Module2的最後三個事件仍會被觸發。

ContractedBlock.gifExpandedBlockStart.gifCode
public class CompleteRequestHttpModule : IHttpModule
    {
        
#region IHttpModule 成員
         
public void Dispose()
        {} 

        
public void Init(HttpApplication application)
        {
            application.BeginRequest 
+= new EventHandler(Application_BeginRequest);
        } 

        
void Application_BeginRequest(object sender, EventArgs e)
        {
            HttpApplication application 
= (HttpApplication)sender;
            application.CompleteRequest();
            application.Context.Response.Write(
"請求被終止。");
        } 
        
#endregion
    }


HttpModule示例:

 過濾http請求

ContractedBlock.gifExpandedBlockStart.gifCode
    /// <summary>
    
/// 頁面訪問驗證模組
    
/// </summary>
    public class AuthenticationModule : IHttpModule
    {
        
#region IHttpModule Members

        
public void Dispose()
        {
        }

        
public void Init(HttpApplication context)
        {
            context.AcquireRequestState 
+= new EventHandler(context_AcquireRequestState);
        }

        
#endregion

        
private void context_AcquireRequestState(object sender, EventArgs e)
        {
            HttpContext context 
= HttpContext.Current;
            
string path = context.Request.Path.ToLower();
            
// 只處理aspx檔案,因為其他檔案無法獲得Session物件,無法判斷是否已經登入
            if (path.EndsWith(".aspx"))
            {
                
// 如果使用者沒有登入就會返回false
                if (!UserRules.Instance.IsCurrentUserLogined)
                {
                    
// 對於公共資料夾和根目錄的檔案不做判斷
                    if (path.StartsWith("/" + AppSettings.PUBLICFOLDERNAME + "/")==false && !(path.LastIndexOf("/"== 0))
                    {
                        
// 跳轉到公共頁面首頁                        
                        context.Response.Redirect(AppSettings.PUBLICLOGOUTFILENAME, false);
                        context.ApplicationInstance.CompleteRequest();
                    }
                }
            }
        }
    }

在web.confg中新增httpModules節點註冊事件
<httpModules>
     <add name="AuthenticationModule" type="Business.AuthenticationModule, Business"/>
</httpModules>

 
判斷瀏覽器的版本 

ContractedBlock.gifExpandedBlockStart.gifCode
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

/// <summary>
/// BrowserModule 的摘要說明
/// </summary>
public class BrowserModule : IHttpModule
{
    
public BrowserModule()
    {
        
//
        
// TODO: 在此處新增建構函式邏輯
        
//
    }

    
#region IHttpModule 成員

    
public void Dispose()
    {

    }

    
public void Init(HttpApplication context)
    {
        
//新增判斷事件
        context.BeginRequest += new EventHandler(context_BeginRequest);
        context.EndRequest 
+= new EventHandler(context_EndRequest);
    }

    
void context_EndRequest(object sender, EventArgs e)
    {
        
if (isfalse)
        {
            HttpApplication application 
= (HttpApplication)sender;
            application.Context.Response.Clear();
//清空瀏覽器所有的內容
            application.Context.Response.Output.Write("瀏覽器不符合要求");
        }

    }
    
public bool isfalse = false;//判斷瀏覽器是否符合要求
    void context_BeginRequest(object sender, EventArgs e)
    {
        HttpApplication application 
= (HttpApplication)sender;//獲得實際發生事件的物件
        HttpBrowserCapabilities browser = application.Context.Request.Browser;//獲得瀏覽器的相關資訊
        
//獲得瀏覽器的名字  application.Context.Response.Output.Write(browser.Browser);
        
//獲得瀏覽器的版本號 application.Context.Response.Output.Write(browser.MajorVersion );
        if (browser.Browser != "IE" || browser.MajorVersion < 7)//判斷瀏覽器的版本是否是IE5以上的
        {
            
string strBrowserChooserPage = "";//建立變數裝載瀏覽器不符合條件後要匯入的頁面
            try
            {
                strBrowserChooserPage 
= ConfigurationManager.AppSettings["BrowserChooserPage"];//從配置檔案中匯入要瀏覽的頁面
            }
            
catch (System.Configuration.ConfigurationErrorsException ex)
            {
                
throw new Exception(string.Format("請正確配置web.config AppSetting[BrowserChooserPage]節點,系統錯誤提示:{0}", ex.Message));
            }
            
catch (System.Exception ex)
            {
                
throw ex;
            }
            isfalse 
= true;//為True的時候瀏覽器不符合要求,處理完context_BeginRequest事件後,最好在處理context_EndRequest事件的時候判斷是否合適
        }
        
else
        {
         
            application.Context.Response.Output.Write(
"瀏覽器符合要求");
        }
    }
    
#endregion
}

註冊web.config事件

實現URL重寫

ContractedBlock.gifExpandedBlockStart.gifCode
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

/// <summary>
/// UrlReWriteHttpModule 的摘要說明
/// </summary>
public class UrlReWriteHttpModule : IHttpModule
{
    
public UrlReWriteHttpModule()
    {
        
//
        
// TODO: 在此處新增建構函式邏輯
        
//
    }

    
#region IHttpModule 成員

    
public void Dispose()
    {
        
throw new Exception("The method or operation is not implemented.");
    }

    
public void Init(HttpApplication context)
    {
        context.BeginRequest 
+= new EventHandler(context_BeginRequest);
    }

    
void context_BeginRequest(object sender, EventArgs e)
    {
        HttpContext context 
= (sender as HttpApplication).Context;
        
//string Url1 = context.Request.Path;
        
//string Url2 = context.Request.PathInfo;
        
//string Url3 = context.Request.Url.ToString();
        
//得到整個的網路地址
        string fullOrigionalpath = context.Request .Url.ToString();

        
if (fullOrigionalpath.Contains("/Books.aspx"))
        {
            context.RewritePath(
"Products.aspx?Category=Books");//重寫地址,重寫後位址列中的顯示不變,但是真正返回的頁面是被重寫的頁面
        }
        
else if (fullOrigionalpath.Contains("/DVDs.aspx"))
        {
            context.RewritePath(
"Products.aspx?Category=DVDs");
        }
        
else if (fullOrigionalpath.Contains("/DVDs.aspx"))
        {
            context.RewritePath(
"Products.aspx?Category=CDs.aspx");
        }
    }

    
#endregion
}

 註冊web.config事件

-------------------------------------------------完成------------------------------------------------

從上面的例子看出,其實是一個好簡單的實現方法,就是在init中註冊處理過程事件,把程式碼寫完後,在web.congif中HttpModules註冊就完成了.

開發程式千變萬變, 通過這一方法可以編寫出好多合適自己用的功能,這需要大家去實踐了. 收集與整理,且當成長的回憶.

 

相關文章