HttpHandler,HttpApplication, HttpModule

來源:互聯網
上載者:User

標籤:

    選擇HttpHandler還是HttpModule?  HttpHandler和HttpModule之間有什麼差別   之所以有這個疑問,是因為在這二類對象中都可以訪問Request, Response對象,都能處理請求。  我原以為在部落格 用Asp.net寫自己的服務架構 中做了那麼多的示範應該把它們的使用方法說清楚了, 然而有些人看了我的那些樣本,仍然不知道該如何選擇它們,為了實現同一個目標,我既用了HttpHandler,也有用HttpModule。 現在看來,我當時設計的那些樣本並不是講清楚HttpHandler和HttpModule之間有什麼差別, 而是在示範如何利用HttpHandler和HttpModule設計一個服務架構。  很慶幸那篇部落格內容沒有走題,今天只好再來寫一篇了。  本文約定:   1. HttpHandler泛指所有實現IHttpHandler介面的類型。   2. HttpModule泛指所有實現IHttpModule介面的類型。   因此,本文將不會特別區分這些類型與介面。  理解ASP.NET管線  HttpHandler和HttpModule,它們都與ASP.NET管線有關,所以我想理解這二類對象必須要理解ASP.NET管線的工作方式。  反映了ASP.NET管線的處理流程:ASP.NET管線處理流程  這是一張時序圖,我們應該從二個角度來理解它:   1. 有哪些調用動作。   2. 有哪些參與者。  每個調用動作,都反映了ASP.NET管線的處理階段,它們會引發相應的事件(除GetHandler,ProcessRequest之外), HttpModule則會訂閱這些事件,參與到管線處理過程。 這些階段中,有些階段還引發了二個事件,完整的管線事件可參考MSDN文檔:  圖片中還反映了ASP.NET的三種主要的參與者:   1. HttpModule   2. HttpHandlerFactory   3. HttpHandler  有沒有有想過:這三種參與者中,每種有多少個參與對象呢?  為了清楚地回答這個問題,我準備了下面的表格:管線參與者 每次請求中參與者數量 HttpModule >= 0 HttpHandlerFactory 1 HttpHandler 1   為什麼要引入HttpHandlerFactory呢? 請看我的部落格 細說 HttpHandler 的映射過程,今天就不重複這塊內容了。  除開HttpHandlerFactory,我們可以發現:在ASP.NET管線中,HttpHandler應該只有一個,而HttpModule是可選的。   進而,我們是不是可以這樣理解:HttpHandler才是處理請求的主角(不可缺少),HttpModule是配角(可以沒有)?  理解HttpApplication  前面我們一直在說ASP.NET管線,那麼,誰在控制管線過程?   答案是:HttpApplication對象。   1. HttpApplication細分它的處理過程,在不同階段引發不同的事件,使得HttpModule通過訂閱事件的方式加入到請求的處理過程中。   2. 在請求的處理過程中,HttpApplication對象主要扮演著控制處理流程的推進作用。   3. HttpApplication會在固定的階段擷取一個IHttpHandler執行個體,然後將請求的響應過程交給具體的IHttpHandler來實現。  HttpApplication如何產生,如何工作?    1. HttpApplication對象會被重用,當HttpRuntime不能從HttpApplicationFactory擷取閒置執行個體時,才會建立。   2. HttpRuntime會將每個請求交給一個HttpApplication對象來處理。   3. HttpApplication對象在初始化時負責載入全部的HttpModule。   4. 每個HttpApplication對象會控制屬於它的管線過程(前面已解釋)。  HttpApplication是個非常重要的類型,它的許多功能都屬於架構的基礎部分,不需要我們調用, 因此,我們平時不會用到它。  我不想讓部落格走題,下面來看看今天的主角吧。  理解HttpHandler  前面說到HttpRuntime會將請求交給HttpApplication來處理, 此時你有沒有想過這樣一個問題:為什麼HttpApplication不直接處理請求,而是要再交給一個HttpHandler對象來處理呢?  答案是:每個請求的內容可能並不相同,它們存在多樣性,因此ASP.NET採用了抽象原廠模式來處理這些請求。 ASP.NET在web.config的架構中,允許我們指定某些請求映射到一個HttpHandlerFactory,例如:<!--適用於IIS6的配置--><system.web>    <httpHandlers>        <add path="*.cspx" verb="*" type="MyMVC.AjaxHandlerFactory, MyMVC" validate="true" />        <add path="*.aspx" verb="*" type="MyMVC.MvcPageHandlerFactory, MyMVC" validate="true" />        <add path="/mvc/*" verb="*" type="MyMVC.MvcPageHandlerFactory, MyMVC" validate="true" />    </httpHandlers></system.web><!--適用於IIS7的配置(整合模式)--><system.webServer>    <handlers>        <add name="AjaxHandlerFactory" verb="*" path="*.cspx" type="MyMVC.AjaxHandlerFactory, MyMVC" preCondition="integratedMode" />        <add name="MvcPageHandlerFactory" verb="*" path="*.aspx" type="MyMVC.MvcPageHandlerFactory, MyMVC" preCondition="integratedMode" />        <add name="MvcPageHandlerFactory2" verb="*" path="/mvc/*" type="MyMVC.MvcPageHandlerFactory, MyMVC" preCondition="integratedMode" />    </handlers></system.webServer>  當某個請求與一個規則匹配後,ASP.NET會調用匹配的HttpHandlerFactory的GetHandler方法來擷取一個HttpHandler執行個體, 最後由一個HttpHandler執行個體來處理當前請求。  HttpApplication是如何將請求交給HttpHandler執行個體來處理的呢?   為了理解這個過程,我們要來看一下IHttpHandler介面的定義:// 這個介面用於同步調用// 非同步版本的介面用法請參考:http://www.cnblogs.com/fish-li/archive/2011/11/20/2256385.htmlpublic interface IHttpHandler{    // 擷取一個值,該值指示其他請求是否可以使用 IHttpHandler 執行個體。    bool IsReusable { get; }    // 通過實現 IHttpHandler 介面的自訂 HttpHandler 啟用 HTTP Web 請求的處理。    void ProcessRequest(HttpContext context);}  HttpApplication在將某個請求交給HttpHandler執行個體來處理時,是通過介面來調用的(ProcessRequest方法)。  與HttpHandler的相關話題:   1. 非同步 HttpHandler:細說ASP.NET的各種非同步作業。   2. 如何重用HttpHandler:細說 HttpHandler 的映射過程。  HttpHanlder的典型應用<%@ WebHandler Language="C#" Class="Login" %>using System;using System.Web;public class Login : IHttpHandler {    public void ProcessRequest (HttpContext context) {        context.Response.ContentType = "text/plain";        string username = context.Request.Form["name"];        string password = context.Request.Form["password"];        if( password == "aaaa" ) {            System.Web.Security.FormsAuthentication.SetAuthCookie(username, false);            context.Response.Write("OK");        }        else {            context.Response.Write("使用者名稱或密碼不正確。");        }    }  通常我去這樣建立一個ashx檔案(HttpHanlder)響應某種特殊的請求。   所以,我們應該這樣理解HttpHanlder:一個HttpHanlder用於響應一類特定的請求。  我們經常用到的HttpHanlder有哪些?  1. aspx頁面。  2. asmx服務檔案。  3. ashx檔案(一般處理常式)。  4. 實現IHttpHandler介面的自訂類型。  我們通常使用HttpHanlder做什嗎?HttpHanlder類型 實現目標 aspx頁面 響應請求,輸出HTML結果 asmx服務檔案 響應服務調用 ashx檔案(一般處理常式) 實現簡單的AJAX響應 實現IHttpHandler介面的自訂類 響應什麼副檔名的請求??   理解HttpModule  設計HttpHanlder的目的很明確:產生響應結果。   那麼,設計HttpModule又是為什麼呢?  前面說過,一個HttpHanlder用於處理一類特定的請求,每個aspx, ashx都可以認為是一類請求。 有時候我們發現所有頁面可能都需要某些相同的檢查功能(如身份檢查), 假如只能使用HttpHanlder,那我們就要讓所有頁面都去調用那些相同的檢查功能。 誰願意做這些重複的事情? 或許有些人會回答,可以自己實現一個基類,把檢查功能放在基類中去調用。 然而,這種做法只能解決重複調用問題,它會讓代碼失去靈活性(擴充性), 試想一下:如果需要再加入新的檢查功能,或者用新的檢查方法替換原有的檢查邏輯時怎麼辦? 只能是修改基類了吧?  設計HttpModule的目的正是為了提供一個靈活的方法解決這種功能重用問題。 它採用事件(觀察者)的設計模式,將某些HttpHanlder都需要的功能抽取出來, 形成不同的觀察者類型,這些觀察者類型可以編譯成類庫形式,供多個網站項目共用。 為了讓ASP.NET管線更靈活,ASP.NET允許我們在web.config中自由配置需要的HttpModule,例如:<!--適用於IIS6的配置--><system.web>    <httpModules>        <add name="SetOutputCacheModule" type="MyMVC.SetOutputCacheModule, MyMVC"/>    </httpModules></system.web><!--適用於IIS7的配置(整合模式)--><system.webServer>    <modules>        <add name="SetOutputCacheModule" type="MyMVC.SetOutputCacheModule, MyMVC" preCondition="integratedMode" />    </modules></system.webServer>  配置只是告訴ASP.NET:這些HttpModule需要運行起來。 有沒有想過這些HttpModule到底是如何進入管線運行起來的呢? 前面我只是說了HttpModule會訂閱這些事件,那麼事件又是在哪裡訂閱的呢? 還是來看一下IHttpModule介面的定義吧:// 這個介面用於同步調用// 非同步用法請參考:http://www.cnblogs.com/fish-li/archive/2011/11/20/2256385.htmlpublic interface IHttpModule{    //  初始化模組,並使其為處理請求做好準備。    void Init(HttpApplication app);    void Dispose();}  注意這個關鍵的Init方法,它傳入一個HttpApplication類型的參數,有了HttpApplication對象,HttpModule就可以訂閱HttpApplication的所有事件了。 請看下面的範例程式碼:public class TestModule : IHttpModule{    public void Dispose() {}    public void Init(HttpApplication app)    {        app.PostAcquireRequestState += new EventHandler(app_PostAcquireRequestState);        app.PreRequestHandlerExecute += new EventHandler(app_PreRequestHandlerExecute);    }    void app_PreRequestHandlerExecute(object sender, EventArgs e)    {        throw new NotImplementedException();    }    void app_PostAcquireRequestState(object sender, EventArgs e)    {        throw new NotImplementedException();    }}  HttpModule的典型應用public class SetOutputCacheModule : IHttpModule{    public void Init(HttpApplication app)    {        app.PreRequestHandlerExecute += new EventHandler(app_PreRequestHandlerExecute);    }    void app_PreRequestHandlerExecute(object sender, EventArgs e)    {        HttpApplication app = (HttpApplication)sender;        Dictionary<string, OutputCacheSetting> settings = ConfigManager.Settings;        if( settings == null )            throw new ConfigurationErrorsException("SetOutputCacheModule載入設定檔失敗。");        // 實現方法:        // 尋找配置參數,如果找到匹配的請求,就設定OutputCache        OutputCacheSetting setting = null;        if( settings.TryGetValue(app.Request.FilePath, out setting) ) {            setting.SetResponseCache(app.Context);        }    }  這個Module用於給一些在設定檔中指出要緩衝的請求設定輸出緩衝,範例程式碼已在上篇部落格 不修改代碼就能最佳化ASP.NET網站效能的一些方法 介紹過了。 其實設定輸出緩衝的最根本手段還是調用Response.Cache的一些公開方法,修改輸出回應標頭。  我們用HttpModule做什麼事情?  1. 修改某些請求(例如前面的樣本修改了回應標頭)。  2. 檢查檢查請求(例如身份認證檢查)。  HttpModule能處理哪些請求呢?  1. 預設是全部進入ASP.NET的請求。  2. 如果只需要處理部分請求,那麼請自行判斷(參考前面的樣本)。  三大對象的總結  前面我分別介紹了HttpApplication,HttpHanlder和HttpModule,這裡再把三者的關係重新梳理一遍。  在請求的處理過程中,HttpApplication對象主要扮演著控制管線處理流程的作用,它負責推進整個處理流程, 除了在不同階段引發不同的事件外(供HttpModule使用),HttpApplication對象還會根據當前請求尋找一個合適的IHttpApplicationFactory執行個體, 並最終得到一個IHttpHandler的執行個體用於處理請求。  設計這三種類型的目的在於:   1. HttpApplication控制處理流程,在不同階段引發不同的事件。   2. 由於請求的多樣性,每個請求會由一個HttpHandler對象來處理。   3. 對於一些通用性的功能,尤其是與響應內容無關的,設計成HttpModule是最合適的。  案例示範  Q:我有一些html檔案,需要做身份認證檢查(判斷Session),我該如何??   想好了就來看看我的解決方案:/// <summary>/// 用於響應HTML檔案的處理器/// </summary>public class HtmlHttpHandler : IHttpHandler, IRequiresSessionState, IReadOnlySessionState {    public void ProcessRequest(HttpContext context)    {        // 擷取要請求的檔案名稱        string filePath = context.Server.MapPath(context.Request.FilePath);        // 用Fiddler查看回應標頭,如果看到有這個頭,就表示是由這段代碼處理的。        context.Response.AddHeader("SeesionExist", (context.Session != null ? "yes": "no"));        // 在這裡,你可以訪問context.Session        // 設定響應內容標題        context.Response.ContentType = "text/html";        // 輸出檔案內容        context.Response.TransmitFile(filePath);    }   Q:我需要壓縮所有的ASP.NET請求的響應結果,該怎麼實現?   想好了就來看看我的解決方案:/// <summary>/// 為請求支援gzip壓縮輸出的DEMO/// </summary>public class GzipModule : IHttpModule{    public void Init(HttpApplication app)    {        app.BeginRequest += new EventHandler(app_BeginRequest);    }    void app_BeginRequest(object sender, EventArgs e)    {        HttpApplication app = (HttpApplication)sender;        // 判斷瀏覽器是否支援GZIP壓縮        string flag = app.Request.Headers["Accept-Encoding"];        if( string.IsNullOrEmpty(flag) == false && flag.ToLower().IndexOf("gzip") >= 0 ) {            // 設定GZIP壓縮            app.Response.Filter = new GZipStream(app.Response.Filter, CompressionMode.Compress);            app.Response.AppendHeader("Content-Encoding", "gzip");        }    }   如何選擇?  在結束這篇部落格之前,再問問各位讀者:現在知道何時選擇HttpHandler還是HttpModule了嗎?  如果還沒有看明白,那我就最後告訴你一個識別方法:   1. 如果要響應一類請求,那麼就選擇HttpHandler。   2. 如果要修改或者檢查所有請求(總之就是不產生響應結果),那就選擇HttpModule。  最後給各位留下一個題目,下面這些ASP.NET提供的功能,它們是採用了哪個方式實現的?   1. Session   2. 身份認證   3. URL授權檢查   3. 通過trace.axd查看跟蹤資訊   4. OutputCache   5. 禁止下載config檔案   6. 禁止查看下載原始碼檔案   注意:本文的主題是:選擇HttpHandler還是HttpModule,所以請不要扯遠了。

  

HttpHandler,HttpApplication, HttpModule

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.