iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 2
2

前言:

上一篇文章我們有說 Asp.net 有兩個核心組件 IHttpMoudle & IHttphandler

Asp.net 是一個pipeline的模型,理解這些管道除了可以讓我們節省許多不必要的資源浪費也可讓我們程式架構更加有條理.

如果我們要寫權限驗證的程式碼在Asp.net MVC中,雖然可以寫在ControllerAction

但更好做法是我可以寫一個類別繼承AuthorizeAttributeoverride OnAuthorization方法並掛上此標籤.

更了解這些原理可以讓我們寫程式事半功倍.

IHttpHandler和HttpModule關係

HTTP請求像是一個旅客身上帶著行李拿著票來搭火車.

  • HttpHandler 是火車的終點站.
  • HttpModule 是火車中途停靠的各站.

此篇同步發布在筆者BlogAsp.Net支柱 IHttpMoudle & IHttphandler (第2天)

Asp.net HttpApplication物件事件 生命週期

我們已經知道Asp.net是一個請求處理響應的管道而這個管道中微軟有提供許多點可以讓我們進行客製化的擴充程式撰寫

事件可藉由IHttpModule來擴充註冊

Event事件名稱:

官網列出可用事件很多,下面列出我有用過的幾個事件和其功用.

  • BeginRequest
  • AuthorizeRequest
  • PostResolveRequestCache
  • MapRequestHandler
  • AcquireRequestState
  • PreRequestHandlerExecute
  • PostRequestHandlerExecute
  • EndRequest

事件方法說明:

  • BeginRequest: 已經啟動要求。如果要在要求前執行某個動作 (例如, 在每頁頁首顯示廣告橫幅), 請同步處理這個事件。
  • AuthorizeRequest: 您可以在內部使用這個事件, 以實作授權機制 (例如, 將存取控制清單 (ACL) 儲存在資料庫, 而非存入檔案系統)。您也可以覆寫這個事件, 但無此必要。
  • PostResolveRequestCache:當 ASP.NET 略過目前事件處理常式的執行並允許快取模組從快取中服務要求時發生。
  • MapRequestHandler:ASP.NET 基礎結構會使用事件來判斷目前要求的要求處理常式。 如需詳細資訊
  • AcquireRequestState: 工作階段狀態是擷取自狀態儲存區。如果要建置自已的狀態管理模組, 則可以同步處理這個事件, 以便從狀態儲存區擷取「工作階段」狀態。
  • PreRequestHandlerExecute: 這個事件會在執行 HTTP 處理常式之前產生。
  • 在介於PreRequestHandlerExecutePostRequestHandlerExecute事件之間會執行HttpHandler程式碼.
  • PostRequestHandlerExecute: 這個事件會在執行 HTTP 處理常式之後產生。
  • EndRequest: 要求已完成。您可能想要建置偵錯模組, 以便收集要求的全部資訊, 然後再將資訊寫入網頁中。

IHttpHandlerIHttpModule關係如 Implementing HTTPHandler and HTTPModule in ASP.NET 文章提到

img

每個請求一定會通過所有被註冊的IHttpModule,而最終會執行一個IHttpHandler後進行返回.

我們常聽到的Asp.net WebformMVC都是經過管道Module並執行相對應的Handler.

所以HttpHanlder 和 HttpMoudule 搭配使用達到更強大的功能.

IHttpHandler

MSDN說明

您可以撰寫自訂的 HTTP 處理常式來處理特定的預先定義的任何 Common Language Specification (CLS) 標準的語言中的 HTTP 要求的類型。 可執行程式碼中定義HttpHandler類別,而不是傳統的 ASP 或 ASP.NET Web 網頁,這些特定的要求回應。 HTTP 處理常式提供您一種低層級的要求和回應服務的 IIS Web 伺服器互動,以及大部分 ISAPI 擴充程式類似,但使用簡單的程式設計模型提供的功能。

IHttpHandler是一個可以讓我們實現的介面
裡面包含:

屬性:

public bool IsReusable { get; }

取得值,指出另一個要求是否可以使用 IHttpHandler 執行個體。

方法:

public void ProcessRequest(HttpContext context)

以實作 IHttpHandler 介面的自訂 HttpHandler 來啟用 HTTP Web 要求的處理。

IHttpModule

MSDN說明

Modules are called before and after the handler executes. Modules enable developers to intercept, participate in, or modify each individual request. Modules implement the IHttpModule interface, which is located in the System.Web namespace.

處理常式 (Handler) 在執行前後,會呼叫模組 (Module)。 模組可以讓開發人員攔截、參與或修改每個要求。

更印證了

 如果把Http請求當作火車那

  • IHttpHandler是火車的終點
  • IHttpModule是沿路經過的站點

要查看有哪寫IHttpModuleIHttpHandler被註冊可以看applicationhost.config檔案

路徑:C:\Users[user]\Documents\IISExpress\config\applicationhost.config

自己建立一個 IHttpHandler

在前面有說到每個Http請求的最終是為了給一個HttpHander來執行處理.

像我們常看到的

  • ASP.NET page (*.aspx)
  • Web service (*.asmx)
  • Generic Web (*.ashx)

甚至是MVC (MvcHandler)都是實現於IHttpHander介面

這邊介紹如果要如何建立自己HttpHander.

Web.Config註冊上面撰寫的IHttpHandler

我們要在Web.Config中設定我們撰寫的HttpHandler

這是一個範例:

<configuration>
    <system.webServer>
    <handlers>
        <add verb="*" name="MyHttpHandler" path="*.cspx"  type="HttpHandler_HttpModule.MyHttpHandler"/>
    </handlers>
    </system.webServer>
</configuration>

handlers加入system.webServer結點中.

裡面有幾個Attribute

  • verb:請求動作 GET,POST,PUT...如果是*代表全部請請動作都合用.
  • path:請求那些副檔名會執行此HttpHandler
  • type:註冊的HttpHandler類型.

其中最要注意的是type Attribute.

 <add verb="*" name="MyHttpHandler" path="*.cspx"  type="(namespace).(classname)"/>

最後我們就可以請求 http://xxxx/Mypage.cspx 來試試看我們的結果.

我們將請求*.cspx副檔名的所有請求都當作要透過CLR來執行.

自己建立一個 IHttpModule

每個被註冊的HttpModule是Http請求必經之路.

  • Asp.net MVC 是透過System.Web.Routing.UrlRoutingModule 這個HttpModule來完成切入的.

使用IHttpModule須完成幾個步驟:

  1. 建立一個類別實現IHttpModule
  2. Web.Config註冊上面撰寫的IHttpModule

建立一個類別實現IHttpModule

這個範例會在頁面依照順序顯示IIS Pipeline Event

  1. public void Init(HttpApplication context)HttpApplication 中的event做擴充.
public class MyHttpModule:IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.BeginRequest += (sender, args) => ShowStep(sender, "BeginRequest");

        context.AuthorizeRequest += (sender, args) => ShowStep(sender, "AuthorizeRequest");

        context.PostResolveRequestCache += (sender, args) => ShowStep(sender, "PostResolveRequestCache");

        context.MapRequestHandler += (sender, args) => ShowStep(sender, "MapRequestHandler");

        context.AcquireRequestState += (sender, args) => ShowStep(sender, "AcquireRequestState");

        context.PreRequestHandlerExecute += (sender, args) => ShowStep(sender, "PreRequestHandlerExecute");

        //這中間執行IHttpHandler.

        context.PostRequestHandlerExecute += (sender, args) => ShowStep(sender, "PostRequestHandlerExecute");

        context.EndRequest += (sender, args) => ShowStep(sender, "EndRequest");

        context.PreSendRequestHeaders += (sender, args) => ShowStep(sender, "PreSendRequestHeaders");
    }

    private void ShowStep(object app,string eventName)
    {
        var http = (HttpApplication)app;
        http.Response.Write($"Step {eventName}<br/>");
    }

    public void Dispose()
    {
    }
}

Web.Config註冊上面撰寫的IHttpModule

註冊方法IHttpHander很類似,一樣可在system.webServer節點下加入modules

<configuration>
    <system.webServer>
    <modules>
      <add name="MyHttpModule" type="HttpHandler_HttpModule.MyHttpModule"/>
    </modules>  
    </system.webServer>
</configuration>

程式碼

使用IHttpHandler須完成幾個步驟:

  1. 建立一個類別實現IHttpHander
  2. Web.Config註冊上面撰寫的IHttpHandler

建立一個類別實現IHttpHander

繼承完IHttpHandler我們會實現兩個方法.

  1. ProcessRequest(HttpContext context) 執行此次請求動作.
  2. bool IsReusable { get; } 是否要將此次請求加入快取中重用.
public class MyHttpHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "text/html";
        context.Response.Write("==================<br/>");
        context.Response.Write("Hello World<br/>");
        context.Response.Write("==================<br/>");
    }

    public bool IsReusable { get; }
}

ApplicationHost.config 設定擋

前面有說到aspnet_isapi.dll會去判斷此次請求要怎麼處理,她會去讀取handlers註冊節點的對應表來判斷是否要透過CLR處理此次請求.

IIS預設設定Module註冊表?

IIS是透過 ApplicationHost.config 來抓取,HttpHandlerHttpModule設定.

這邊分為兩個部分:

IIS

IIS可借由HandlerMappingsModule來設置

瀏覽器請求IIS流程


  • HandlerMappings

瀏覽器請求IIS流程

可以看到有許多預設要處理的附檔名(.aspx,.svc)在裡面都可以看到.

  • Module

瀏覽器請求IIS流程

IIS預設幫忙載入許多Moudle我們自己客製化的Module也可以在這邊設定


IISExpress

基本上IISExpress 全域 config 會放在這個路徑

C:\Users\%USERPROFILE%\Documents\IISExpress\config

如果VS版本高於2015以上執行Web專案時,會在每個專案上建立一個.vs資料夾並把ApplicationHost.config複製一份放到下面影片的路徑中

瀏覽器請求IIS流程


範例原始碼下載

小結

今天我們學到

  1. 自己建立一個Httpmodule
  2. 自己建立一個Httphandler
  3. ApplicationHost.config設定擋和Httpmodule & Httphandler關係且如何設定.

今天先丟一個問題給大家那,為什麼Asp.net可以依賴IHttphandler介面來完成請求這是怎麼實現的?

下篇我們會來回答上面的答案.

參考資料:


上一篇
[Day01] (開賽)Http 請求 Asp.net IIS伺服器架構
下一篇
[Day03] 啟動吧!Asp.Net IsapiRunTime & HttpRuntime
系列文
從Asp.net框架角度進入Asp.net MVC原始碼30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言