ASP.NET WebAPI框架解析第二篇(HttpModule的創建和使用)


我們先看一下執行流程圖

image

圖中畫紅圈的部分便是HttpModule,在說創建HttpModule之前,先說一下HttpApplication對象,HttpApplication對象由Asp.net框架創建,每個請求對應一個HttpApplcation實例對象,Asp.Net框架內部維護了一個HttpApplication對象池,可以復用該對象,以便節省服務器資源。HttpApplication對象內部有許多事件,其中的一些事件如下:

BeginRequest Asp.net處理的第一個事件,表示處理的開始

AuthenticateRequest 驗證請求,一般用來取得請求用戶的信息

PostResolveRequestCache 已經完成緩存的獲取操作

……

EndRequest 本次請求處理完成

其中PostResolveRequestCache 這個事件就被路由模塊監聽了。我們看看一個標准HttpModule模塊的接口定義是怎么樣的。

public interface IHttpModule
{
     void Init(HttpApplication context);

    void Dispose();

}

注意到Init(HttpApplication context)這個方法,每注冊一個HttpModule模塊,Asp.Net框架內部通過反射獲取對應的程序集,並通過反射調用Init方法,Context參數便是處理一個Http請求的HttpApplication實例對象。那怎么讓我們注冊的模塊參與到處理Http請求中呢?前面提到HttpApplication內部有許多事件,而我們的HttpModule中的Init方法會被Asp.Net框架調用,所以我們可以利用這個去注冊相應的事件,執行我們的代碼,我們在這里可以做很多事,比如自定義驗證、自定義授權,圖片壓縮,圖片加水印,服務器日志記錄、惡意請求攔截等等。

為了演示自定義HttpModule的使用,我們創建一個寄宿模式為WebHost的WebAPI,為了便於理解ASP.NET WebAPI的組成,我們不直接創建一個WebAPI模板的項目。

1.使用VS2017創建一個空的解決方案

image


2.新建一個空的.NetFramwork類庫項目和一個空的ASP.NET Web應用

image

image

image

我們對WebAPI類庫項目引用WebAPI所需的System.Web.Http.dll。我在I:\Program Files (x86)\Microsoft Visual Studio\Shared\Packages\Microsoft.AspNet.WebApi.Core.5.2.7\lib\net45路徑下找到了該dll。

image

然后在WebAPI項目下建立一個類,使用命名空間System.Web.Htpp,繼承該命名空間下的ApiController即可,然后定義一個方法,我定義的如下。注意類名一定要以Controller結尾,否則在路由匹配成功后,WebAPI框架內部不能通過路由解析到的值去反射構建對應的控制器實例對象。

image

然后對WebHost項目引用如下dll(我都是在I:\Program Files (x86)\Microsoft Visual Studio\Shared\Packages\目錄下找到的,實在找不到就創建一個WEBAPI模板然后到項目目錄下的Package文件中去拷貝)

System.Web.Http.dll

System.Web.Http.WebHost.dll

System.Net.Http.Formatting.dll

WebHost項目還要保持對WebAPI項目的引用

為WebHost項目添加Global.asax文件,VS為我們自動創建了一個Global類,並繼承於System.Web命名空間下的HttpApplication類。這個類就是處理Http請求的類。我們也可以在這里監聽相應的事件,監聽函數格式以XXX_XXX的形式定義,至於為什么要這么定義的原因是為了便於通過反射進行注冊。有些事件只能在這里監聽,比如Application_Start,Application_Start在整個應用生命周期中只會執行一次,相當於靜態構造函數.我們在Application_Start中去注冊全局的路由表,在WebHost模式下,WebAPI的路由系統實際上由ASP.NET的路由系統完成的,當然WebAPI本身的路由系統也可以完成。我們注冊以下路由表。

protected void Application_Start(object sender, EventArgs e)
{
     GlobalConfiguration.Configuration.Routes.MapHttpRoute(name: "first",routeTemplate: "webapi/{controller}/{action}");

}

然后在VS2017環境下使用IIS Express啟動,然后在瀏覽器輸入http://localhost:63210/webapi/home/index,注意在我這里端口號是63210。便可得到如下結果。

image

我們回顧一下這個流程。

->客戶端(在這里是瀏覽器)發起HTTP請求

->IIS Express接收到該請求

->IIS Express發現該請求路徑不是已知的靜態資源類型,進而把請求交給Asp.Net托管代碼處理

->Asp.Net從HttpApplication池中取一個實例對象去處理該Http請求

->注冊相應HttpModule模塊,並調用Init()函數,這個時候HttpContext還沒有形成,我們只能在這里注冊相應的監聽事件函數。

->HttpContext形成,HttpApplication實例對象內部的事件輪流觸發,其中有一個PostMapRequestHandler事件,在這個事件觸發后,會調用相應HttpHandler執行相應的處理,后面再說HttpHandler

->必要的事件觸發完成之后生成Http報文並交由IIS Express返回給客戶端

->客戶端接受Http報文並解析顯示在客戶端中。

這只是一個大概的過程,具體比這復雜,但我們關注點在HttpModule上,所以足夠了。

我們現在嘗試自己創建HttpModule

我在WebHost項目下建立了一個文件夾,然后新建了一個AuthorizeHttpModule,模擬授權,功能很簡單,看Http請求頭中是否包含name字段,並且值為HK,否則,向Http響應報文中的Body部分寫入內容,並攔截請求。

image

namespace WebHost.HttpModules
{
     public class AuthorizeHttpModule : IHttpModule
     {
         public void Dispose()
         {
             return;
         }

        public void Init(HttpApplication context)
         {
             //此時HttpContext還未構建完成,不能在這里操作HttpContext
             //注冊事件監聽函數
             context.BeginRequest += Authorize;
         }
         private void Authorize(object sender,EventArgs e)
         {
             HttpApplication app = sender as HttpApplication;

            if (app.Request.Headers.Get("name") != "HK")
             {
                 //不加這一行客戶端可能不能自動正確的解析字符編碼
                 app.Response.Headers.Add("content-Type", "text/html;charset=utf-8");
                 //通過Write(string s)寫入的字符串在內部默認被轉換為utf-8編碼。C#string默認編碼為UTF-16
                 //要先寫入原始的字符串編碼,調用BinaryWrite(byte[] bytes)
                 app.Response.Write("驗證不通過!");
                 app.CompleteRequest();
             }
         }
     }
}

在WebHost項目目錄下的Web.Config中配置HttpModule信息,以便Asp.Net框架讀取。注意,有個全局的Web.Config文件,其中默認注冊了許多HttpModule,其中有一個HttpModule(WebDAVModule)會截斷Put和Delete請求。

image

如上,我添加了system.webServer節點(IIS集成模式下是這樣配置,經典模式自行搜索),並在子節點modules下添加了自定義的HttpModule,其中name是HttpModule名稱,ASP.NET內部會以這個名稱作為Key,type表示HttpModule的類型名,以便反射讀取相應的類型對象。該類型所在的程序集必須在Web應用程序的Bin目錄,否則不能正常加載,由於我創建的HttpMudle是在WebHost項目中,所以不會出現問題。我們現在啟動IIS Express看看效果。

image

可以看到我們的HttpModule起作用了。我們用PostMan為請求頭添加一個字段name,值為HK,然后請求/webapi/home/index試試看

image

可以看到沒有被攔截。前面我們提到了一個HttpHandler,這個HttpHandler實際上是最終處理頁面的處理者,后面再說。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM