🚫 Ad Blocker Detected

Please disable your AD blocker to continue using this site. Ads help us keep the content free! please press keyboard F5 to refresh page after disabled AD blocker

請關閉廣告攔截器以繼續使用本網站。廣告有助於我們保證內容免費。謝謝! 關閉後請按 F5 刷新頁面

0%

揭密Mvc使用IHttpHandler by UrlRoutingModule-4.0 (第8天)

Agenda

前言:

前面幾篇文章已經詳細分享解說Asp.net如何透過HttpApplication找到IHttpHandler並執行呼叫介面方法.

瀏覽器請求IIS流程

今天要跟大家分享上圖的最後一塊拼圖揭密並探索Asp.net MVC使用的IHttpHandler.

UrlRoutingModule-4.0

在標題已經透漏我們是透過UrlRoutingModule這個繼承IHttpModule的類別來取得IHttpHandler

有人可能會有疑問是我明明沒有註冊此HttpModule Asp.net怎麼知道的呢?

原因是這個Module是預設就載入

下圖是一般IIS預設載入的HttpModule可以看到UrlRoutingModule已經在裡面了.

7-MVCModule.PNG

另外我們也可以看applicationhost.config檔案,也可以看到UrlRoutingModule-4.0也已經在裡面了.

我們可以發現他是在System.Web.Routing這個命名空間下.

1
2
3
4
5
<modules>
....
<add name="ServiceModel-4.0" type="System.ServiceModel.Activation.ServiceHttpModule,System.ServiceModel.Activation,Version=4.0.0.0,Culture=neutral,PublicKeyToken=31bf3856ad364e35" preCondition="managedHandler,runtimeVersionv4.0" />
<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="managedHandler,runtimeVersionv4.0" />
</modules>

此連結可以看到 UrlRoutingModule 原始碼

1
2
3
4
5
6
7
8
9
10
protected virtual void Init(HttpApplication application) {

// Check if this module has been already addded
if (application.Context.Items[_contextKey] != null) {
return; // already added to the pipeline
}
application.Context.Items[_contextKey] = _contextKey;

application.PostResolveRequestCache += OnApplicationPostResolveRequestCache;
}

前面文章有說道Init方法會在HttpApplication呼叫InitInternal方法時被呼叫.

這這裡可看到application.PostResolveRequestCache多註冊一個OnApplicationPostResolveRequestCache事件.

讓我們來看看此事件做了什麼事情

OnApplicationPostResolveRequestCache事件

OnApplicationPostResolveRequestCache方法中,利用 HttpContextWrapper轉接器模式把app.Context轉接成一個可接受HttpContextBase物件,並呼叫傳入PostResolveRequestCache方法中.

1
2
3
4
5
private void OnApplicationPostResolveRequestCache(object sender, EventArgs e) {
HttpApplication app = (HttpApplication)sender;
HttpContextBase context = new HttpContextWrapper(app.Context);
PostResolveRequestCache(context);
}

PostResolveRequestCache方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public virtual void PostResolveRequestCache(HttpContextBase context) {
// Match the incoming URL against the route table
RouteData routeData = RouteCollection.GetRouteData(context);

// Do nothing if no route found
if (routeData == null) {
return;
}

// If a route was found, get an IHttpHandler from the route's RouteHandler
IRouteHandler routeHandler = routeData.RouteHandler;

//... 判斷 error 程式碼

if (routeHandler is StopRoutingHandler) {
return;
}

RequestContext requestContext = new RequestContext(context, routeData);

// Dev10 766875 Adding RouteData to HttpContext
context.Request.RequestContext = requestContext;

IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);

//... 判斷 error 程式碼

// Remap IIS7 to our handler
context.RemapHandler(httpHandler);
}

RouteCollection是一個全域路由集合,註冊使用路由(Asp.net Global.cs中我們很常看到使用).

對於此集合註冊路由,是MVC,WebApi能運行的關鍵喔

MVC中我們透過MapRoute擴展方法來註冊路由,其實在這個擴展方法中會建立一個Route物件並加入RouteCollection集合中.

Route物件會提供一個HttpHandler來給我們呼叫使用.

1
2
3
4
5
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

RouteCollection.GetRouteData(context)取得路由中匹配此次請求的路由資料,藉由此註冊進集合並繼承RouteBase抽象類別的物件

IRouteHandler取得執行HttpHandler

routeData會有一個重要的屬性RouteHandler是繼承於IRouteHandler

這個介面只有一個方法就是回傳IHttpHandler看到這基本上就可以知道MVCIHttpHandler是呼叫RouteHandler.GetHttpHandler回傳的物件.

1
2
3
public interface IRouteHandler {
IHttpHandler GetHttpHandler(RequestContext requestContext);
}

後面會對於此介面有更詳細介紹

RemapHandler設置HttpContext的HttpHandler

PostResolveRequestCache最後面幾段程式碼,是透過routeHandler.GetHttpHandler(requestContext)取得IHttpHandler,並將其設置給context

1
2
3
4
IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);

// Remap IIS7 to our handler
context.RemapHandler(httpHandler);

這邊說明一下RemapHandler作用,最主要是把傳入參數handler傳給_remapHandler欄位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void RemapHandler(IHttpHandler handler) {
EnsureHasNotTransitionedToWebSocket();

IIS7WorkerRequest wr = _wr as IIS7WorkerRequest;

if (wr != null) {
// Remap handler not allowed after ResolveRequestCache notification
if (_notificationContext.CurrentNotification >= RequestNotification.MapRequestHandler) {
throw new InvalidOperationException(SR.GetString(SR.Invoke_before_pipeline_event, "HttpContext.RemapHandler", "HttpApplication.MapRequestHandler"));
}

string handlerTypeName = null;
string handlerName = null;

if (handler != null) {
Type handlerType = handler.GetType();

handlerTypeName = handlerType.AssemblyQualifiedName;
handlerName = handlerType.FullName;
}

wr.SetRemapHandler(handlerTypeName, handlerName);
}

_remapHandler = handler;
}

_remapHandler就是RemapHandlerInstance屬性回傳的值

1
2
3
4
5
internal IHttpHandler RemapHandlerInstance {
get {
return _remapHandler;
}
}

我們之前有分享MapHandlerExecutionStep,MapHttpHandler會優先讀取存在context.RemapHandlerInstanceHttpHandler如果有物件就給CallHandlerExecutionStep呼叫使用.

這邊算是比較完整圓了上一篇埋的小伏筆.

小結

今天談到我們了解到

  1. MVC是透過UrlRoutingModule-4.0這個HttpModule取得HttpHandler
  2. MVC是在application.PostResolveRequestCache這個事件決定使用的HttpHandler
  3. 路由其實是Asp.net MVC呼叫的關鍵
  4. 因為在MapHandlerExecutionStep執行前已經決定context.RemapHandlerInstance所以就不會呼叫到config設定HttpHander物件

基本上Asp.net部分已經介紹完了,接下來會進入Asp.net MVC的世界.

__此文作者__:Daniel Shih(石頭)
__此文地址__: https://isdaniel.github.io/Ithelp-day8/
__版權聲明__:本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 3.0 TW 許可協議。轉載請註明出處!

如果本文對您幫助很大,可街口支付斗內鼓勵石頭^^