路由機制

VC框架中路由具備重要做用,本文主要介紹路由的一些知識。目錄以下:php

一、引言html

二、什麼是路由web

三、特性路由正則表達式

四、傳統路由windows

五、MVC區域瀏覽器

六、路由調試服務器

七、路由的其它一些信息架構

八、選擇特性路由仍是傳統路由mvc

九、asp.net處理http請求的大體過程app

 

一、引言

MVC的理解:

View是界面,Model是功能模型,Controller是View和Model的橋接,將View的輸入傳遞到Model,並將Model的結果反饋到View。

例如:總統在舞臺上演講,總統口渴了須要水;祕書負責傳喚幕後人員送上水來;後臺人員負責送水。總統以及舞臺是view,祕書是controller,後臺人員是model。總統的要求都是由model實際來完成的,controller只是個傳話的。

 

查看mvc實例:建立一個mvc項目,首先查看它的全局文件:

public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }

對start函數中的其它幾項先不作說明,此處查看RegisterRoutes靜態方法:

public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

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

直接運行網站http://localhost:3306/,獲得頁面,切圖以下:

相似URL也能夠獲得該頁面:

http://localhost:3306/home,http://localhost:3306/home/index/,http://localhost:3306/home/index/1

可是http://localhost:3306/home/index/1/1打開是錯誤頁面。

咱們輸入的URL爲什麼能夠轉到控制器,或者說上文總統想要喝水的指令是如何傳達到祕書那裏的,其中牽涉到MVC路由。

 

二、什麼是路由

上述經過url打開頁面,能夠說是瀏覽器http請求,經過路由機制到具體的頁面。那麼,什麼是路由呢?路由至關因而一箇中轉,是一個配置。也許單純說路由的定義,顯得很抽象,那直接說路由能幹什麼:

ASP.net MVC的路由主要用途有兩種:

一、 匹配傳入的請求,該請求不匹配服務器文件系統中的文件,而是把請求映射到控制器操做;

二、 構造傳出的URL,用來響應控制器操做;

表面來看,路由和URL重寫很類似,此處簡單說說兩者區別:

URL重寫關注的是將一個URL映射到另外一個URL,路由關注的則是如何將URL映射到資源;

URL重寫只能用於傳入的請求URL,而不能幫助生成原始的URL,路由卻可使用它在匹配傳入URL時用到的映射規則來幫助生成URL。

 

三、MVC5特性路由

特性路由時mvc5新增的一個特性,本文先從特性路由講起。啓用特性路由,首先在RegisterRoutes方法中刪除其它路由,只經過調用MapMvcAttributeRoutes啓用特性路由:

public static void RegisterRoutes(RouteCollection routes)
        {
            routes.MapMvcAttributeRoutes();
        }

 新增一個控制器類Book:

public class BookController : Controller
    {
        [Route("Index")]
        public ActionResult Index()
        {
            return View();
        }
    }

訪問URL:http://localhost:56978/index可打開相應的index頁面。

 

Book類中新增一方法:

[Route("")]
        [Route("book")]
        [Route("book/about")]
        public ActionResult About()
        {
            return View();
        }

那麼,URL都能訪問該頁面:/,/book/,/book/about。

上述講到的是靜態路由,可是並不是全部的URL都是靜態的,經過添加路由參數能夠解決此問題:

[Route("book/info/{id}")]
        public ActionResult Info(int id)
        {
            ViewData["id"] = id;
            return View();
        }

加入帶參數路由,URL:http://localhost:56978/book/info/2能夠進行訪問。

問題來了,若是該控制器中有多個頁面,訪問頁面都須要加上/book,難道全部的方法都須要一一添加?確定不是的,控制器類路由能夠解決此問題。

[Route("phone/{action}")]
    public class PhoneController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult About()
        {
            return View();
        }
    }

若是訪問phone下面的index,或者是about頁面,url:http://localhost:56978/phone/about或index均可以進行訪問。

可是,有時控制器上的某些操做具備與其餘操做稍微不一樣的路由,那麼咱們能夠直接把最通用的路由放到控制器上面,而後在具備不一樣路由路由模式的操做上重寫路由。

[Route("phone/{action}")]
    public class PhoneController : Controller
    {
        [Route ("phone2/index")]
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult About()
        {
            return View();
        }
    }

URL:http://localhost:56978/phone2/index能夠進行訪問demo phone page index頁面。可是URL:http://localhost:56978/phone/index頁面卻不能進行訪問,也證明了在操做方法級別指定路由特性時,會覆蓋控制器級別指定的任何路由特性

 前面的類仍然帶有重複性,經過使用RoutePrefix,能夠僅在一個地方指定路由以phone/開頭。

    [RoutePrefix("phone")]
    [Route("{action}")]
    public class PhoneController : Controller
    {
        [Route("")]
        public ActionResult Index()
        {
            return View();
        }

        [Route("")]
        public ActionResult About()
        {
            return View();
        }
    }

URL:http://localhost:56978/phone/,打開報錯:

 若是想讓phoneController支持」/」,那麼使用~/做爲路由模板的開頭,路由前綴就會忽略。在phone的操做方法中再加入[Route("~/")]:

[RoutePrefix("phone")]
    [Route("{action}")]
    public class PhoneController : Controller
    {
        [Route("")]
        public ActionResult Index()
        {
            return View();
        }

        [Route("~/")]
        public ActionResult About()
        {
            return View();
        }
    }

URL:http://localhost:56978/打開demo phone page about頁面。

路由約束

路由約束是一種條件,只有知足該條件,路由才能匹配。

[Route("book/info/{id}")]
        public ActionResult Info(int id)
        {
            ViewData["id"] = id;
            return View();
        }

        [Route("book/list/{id}")]
        public ActionResult List()
        {
            return View();
        }

上述info頁面,URL:http://localhost:56978/book/info/2能夠打開,而url:http://localhost:56978/book/info/name,報錯:

 

上述list頁面,URL:http://localhost:56978/book/list/name,http://localhost:56978/book/list/2均可以打開。

將路由參數定義爲{id:int},如此放到路由模板中的約束叫作內聯約束。如此,將list參數加入路由內聯約束,也能夠達到此效果:

[Route("book/list/{id:int}")]
        public ActionResult List()
        {
            return View();
        }

 經常使用的路由約束:

路由的默認值:

//[Route("money/{action}")]
    [Route("money/{action=index}")]
    public class MoneyController : Controller
    {
        // GET: Money
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult About()
        {
            return View();
        }
    }

那麼url:http://localhost:56978/money/默認和http://localhost:56978/money/index,同時不影響http://localhost:56978/money/about打開about頁面。

將參數看成可選參數:

[Route ("money/list/{id?}")]
        public ActionResult List()
        {
            return View();
        }

Url:http://localhost:56978/money/list和http://localhost:56978/money/list/4一樣能夠打開list頁面。

可是:

[Route ("money/list/{id?}")]
        public ActionResult List(int id)
        {
            return View();
        }

URL:http://localhost:56978/money/list/,不能打開頁面。側面反映了可選參數也是基於操做方法來作具體調整的。

 

四、傳統路由

此處先禁用特性路由,編寫一個最傳統的路由規則,傳統路由都須要在RegisterRoutes下面進行定義:

public static void RegisterRoutes(RouteCollection routes)
        {
            //routes.MapMvcAttributeRoutes();//book,phone,money

            routes.MapRoute("simple", "{controller}/{action}/{id}");
        }

編寫控制器:

public class THomeController : Controller
    {
        // GET: THome
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Detail(int id)
        {
            return View();
        }
    }

url:http://localhost:56978/thome/index/3打開index頁面。

傳統路由URL在段中也容許包含字面值,例如:可能會把mvc集中到某一個現有站點中,而且全部mvc請求都必須以site開頭:

routes.MapRoute("simple", "site/{controller}/{action}/{id}");

仍是THome控制器,打開url:http://localhost:56978/site/thome/index/3,能夠打開index頁面。

 傳統路由具備更加靈活的路由語法規則:在路徑段中容許字面值和路由參數混合在一塊兒。僅有的限制是不容許有兩個連續的路由參數。只須要記住,除非路由提供了controller和action參數,不然MVC不知道爲URL容許那些代碼。

具體實例看下面路由規則:

routes.MapRoute("simple", "{lanuage}-{city}/{controller}/{action}");//true
            routes.MapRoute("simple", "{controller}.{action}.{id}");//true

            routes.MapRoute("simple", "{controller}{action}/{id}");//false

 

傳統路由默認值

定義的路由以下:

routes.MapRoute("simple", "{controller}/{action}/{id}", new { id = UrlParameter.Optional, action = "index" });

上述路由規則,id是可選參數,action默認是index,一樣的,也能夠默認控制器如controller="thome"。

路由約束
路由以下:

routes.MapRoute("simple3", "{year}/{month}/{day}", new { controller = "thome", action = "index" }, new { year = @"\d{4}", month = @"\d{2}", day = @"\d{2}" });

URL:http://localhost:56978/2018/05/30,打開index頁面。

上述約束採用正則進行實現。值得注意的是:特性路由的正則表達式的匹配行爲與傳統路由相反。傳統路由老是進行精確匹配,而特性路由的regex內聯約束支持部分匹配。例如:傳統路由約束year=@」\d{4}」至關於特性路由內聯約束{year:regex(^\d{4}$)}。在特性路由中,若是須要進行精確匹配,必須顯示包含^和$字符。傳統路由老是會替咱們添加這些字符,不編寫自定義約束,是沒法進行部分匹配的。咱們一般進行的是精確字符串匹配,全部傳統路由語法意味着咱們不會忘記這些細節。

路由命名:

Asp.net的路由機制不要求路由具備名稱,並且絕大多數狀況下沒有名稱的路由也能知足咱們的應用,例如:

routes.MapRoute(
                name: "Test",
                url: "code/p/{action}/{id}",
                defaults: new { controller = "Section", action = "Index", id = "" }
                );

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = "" }
                );

添加tstudents的index頁面:

@{
    ViewBag.Title = "Index";
}

<h2>Demo TStudent page Index</h2>

<div>
    <form method="post" id="testCreateUrl" name="testCreateUrl">
        @Html.RouteLink("to default", new { controller = "Home", action = "Index", id = 123 })

        @Html.RouteLink("to Test", new { controller = "section", action = "Index", id = 123 })</form>
</div>

則生成相應的url:http://localhost:56978/Home/Index/123,http://localhost:56978/code/p/Index/123。

上述路由很是簡單,可是有時候咱們會碰到一些特殊狀況:

例如web窗體應用程序路由,但願/aspx/SomePage.aspx,可以處理/static/url:

routes.MapPageRoute("new", "static/url", "~/aspx/SomePage.aspx");

同時在RegisterRoutes方法中,上述路由不能放入路由列表的末尾,不然就不能匹配傳入的請求,所以放在前面。從而致使上述生成的url:

http://localhost:56978/static/url?controller=Home&action=Index&id=123和http://localhost:56978/static/url?controller=section&action=Index&id=123,

很明顯不符合要求,因而使用路由命名:

@Html.RouteLink(
             linkText: "route:test",
             routeName: "test",
             routeValues: new { controller = "section", action = "Index", id = 123 })
        @Html.RouteLink(
             linkText: "route:default",
             routeName: "default",
             routeValues: new { controller = "Home", action = "Index", id = 123 })

結果達到預期。

 

 五、MVC區域

建立一個區域User,新增UsersController中的index頁面,HomeController中的index頁面。

區域路由中默認增長路由規則:

public override void RegisterArea(AreaRegistrationContext context) 
        {

            context.MapRoute(
                "User_default",
                "User/{controller}/{action}/{id}",
                new { action = "Index", id = UrlParameter.Optional }
            );
        }

 

http://localhost:56978/home/index進行訪問:

 

URL:http://localhost:56978/user/home/index/,打開區域內的homecontroller的index頁面。

 上述出現路由衝突,

當使用add area對話框添加區域時,框架會相應的在該區域的名稱空間中爲新區域註冊一個路由,這樣就保證只有新區域中的控制器才能匹配新路由。

名稱空間能夠縮小匹配路由時控制器的候選集。若是路由指定了匹配的名稱空間,那麼只有在這個名稱空間中的控制器纔有可能與該路由匹配。相反,若是路由沒有指定名稱空間,那麼程序中全部的控制器都有可能與該路由匹配,並且很容易致使二義性,即兩個同名控制器同時匹配一個路由。防止出現該二義性的方法是在整個項目中使用惟一的控制器名稱。可是若是有時候想使用相同的控制器名稱時,能夠在特定的路由下指定控制器類的名稱空間。如:

 在區域路由中添加:

public override void RegisterArea(AreaRegistrationContext context) 
        {
            context.MapRoute(
                "User_default",
                "User/{controller}/{action}/{id}",
                new { action = "Index", id = UrlParameter.Optional },
                new [] { "Demo.Areas.User.Controllers" }
            );
        }

在外部路由中添加:

routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = "" },
                namespaces: new[] { "Demo.Controllers" }
                );

打開URL:http://localhost:56978/Home/Index,打開demo home page index頁面。URL:http://localhost:56978/user/home/index/,打開UserArea home page Index頁面。

 

特性路由中使用區域,須要使用RouteArea特性。在特性路由中,不須要指定名稱空間,由於mvc會完成肯定名稱空間的工做(特性放到了控制器上,而控制器指定它本身的名稱空間。)

[RouteArea ("User")]
    [Route("users/{action}")]
    public class UserController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }

    }

 

URL:http://localhost:56978/user/dept/index能夠訪問。按道理來講,應該是能夠的,可是區域內使用特性路由一直行不通。???

若是想更改不一樣的路由前綴:

 

Catchall
定義可變長度的自定義片斷變量,經過在片斷變量前加*號前綴來定義匹配任意數量片斷的路由:

routes.MapRoute("catchallroute", "{controller}/{action}/{id}/{*catchall}",
                new { controller = "Home", action = "Index", id = UrlParameter.Optional },
          new[] { "Demo.Areas.User.Controllers" }
);

控制器類:

public class THomeController : Controller
    {
        // GET: THome
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult Detail(int id)
        {
            return View();
        }
    }

URL:http://localhost:56978/thome/detail/12/23/34,可打開thome page detail頁面。

 

StopRoutingHandler IgnoreRoute

默認狀況下,路由機制會忽略那些映射到磁盤物理文件的請求,可是有一些應用場合,一些不能映射到磁盤文件的請求也不須要路由來處理,例如asp.net的web資源處理程序WebResource.axd的請求,是由一個http處理程序來處理的,而它們並無對應對磁盤上的文件。StopRoutingHandler能夠確保路由忽略這種請求,如:

routes.Add(new Route( "{resource}.axd/{*pathInfo}", new StopRoutingHandler()));

若是傳入了url爲/WebResource.axd的請求,它會與第一個路由相匹配,路由返回一個StopRoutingHandler的對象,因此路由會繼續把該請求傳遞給標準的asp.net處理程序,最終將回到用於處理.axd擴展的標準http處理程序。

還有一種更爲簡單的路由機制忽略指定路由,即IgnoreRoute:

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

看到此處是否是很熟悉,MVC默認配置。

 

6 、路由調試:

路由匹配過程當中若是出現問題是使人沮喪的一件事,由於路由時asp.net內部路由處理邏輯解析,不能設置斷點進行調試。並且路由時按前後順序匹配的,且第一個匹配成功的路由生效,頗有可能發生錯誤不在路由的定義上,而是該路由沒有在路由列表的正確位置上。因而使用RouteDebugger:

使用NuGet進行安裝:

 

在web.Config進行設置:

<add key="RouteDebugger:Enabled" value="false" /></appSettings>

將值改成true,即開啓路由調試。

 

 七、路由如何生成URL

前面已經介紹路由時如何匹配傳入的請求URL,而路由的另外一個職責則是構造與特定路由對應的URL。生成URL的方式有多種,可是最後都是調用RouteCollection. GetVirtualPath的重載方法,有兩個版本:

public VirtualPathData GetVirtualPath(RequestContext requestContext, string name, RouteValueDictionary values);
public VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);

接收當前的RequestContext,路由集合經過RouteBase. GetVirtualPath方法遍歷每一個路由並詢問,能夠根據給定參數生成url嗎?能則返回VirtualPathData的實例,不然返回空值,轉移到下一個路由。第二個重載方法則直接找到指定的名稱的路由,匹配則返回實例,不匹配,則返回空值,而且再也不匹配其餘路由。
VirtualPathData:表示有關路由和虛擬路徑的信息,該路由和虛擬路徑是在使用 ASP.NET 路由框架生成 URL 時產生的。

 

1 頁面調用Html.ActionLink或Url.Action方法,此方法再調用RouteCollection. GetVirtualPath方法,並傳入一個RequestContext對象、一個包含值的字典,以及用來生成url的路由名稱(可選).

2 路由機制查看要求的路由參數(即提供路由參數的默認值),並確保提供的路由值字典爲每個要去的參數提供一個值。不然,url生成過程會當即中止,並返回空值。

3 一些路由可能包含沒有對應路由參數的默認值。

4 路由系統應用路由的約束。

5 路由匹配成功,經過查看每個路由參數,嘗試利用字典中的對應值填充相應參數,進而生成url。

 

路由綁定到操做

當asp.net處理請求時,路由管道主要:

1 UrlRoutingModule嘗試使用在RouteTable中註冊的路由匹配當前的請求;

2 若是RouteTable中有一個路由匹配成功,路由模塊會從成功匹配的路由中獲取IRouteHandler接口對象;

3 路由模塊調用IRouteHandler接口的GetHandler方法,並返回用來處理請求的IHttpHandler對象;

4 調用http處理程序的ProcessRequest方法,而後把要處理的請求傳遞給它。

5 在asp.net mvc中,IRouteHandler是MvcRouteHandler類的一個實例,MvcRouteHandler轉而返回一個實現了IHttpHandler接口的MvcHandler對象。返回的MvcHandler對象主要用來實例化控制器,並調用該實例化的控制器上的操做方法。

 

路由數據:調用GetRouteData方法會返回RouteData的一個實例,RouteData則包含了關於匹配請求的路由信息。

 

路由規則:{controller}/{action}/{id},當一個請求例如/home/index/123傳入時,該路由會嘗試匹配傳入的請求。若是匹配成功,它就建立一個字典,其中包含了從URL中解析出的信息,更確切的說,路由還會向values字典中爲URL中的每一個路由參數添加一個鍵。對於上例來講,至少會有cotroller鍵的值爲home,action鍵的值爲index,id鍵的值爲123.對於特性路由來講,MVC使用DataTokens字典來存儲更精確的信息。在整個MVC中都有用到的RequestContext的RouteData屬性保存着外界路由值。

 八、選擇特性路由仍是傳統路由

特性路由和傳統路由結合使用時,路由系統按順序檢查每一個路由,並選擇第一個匹配的路由。在實際使用過程當中,建議將routes.MapMvcAttributeRoutes();放到首位,特性路由一般更加具體,而傳統路由更加寬泛。

選擇傳統路由:
想要集中配置因此路由;
使用自定義約束對象;
存在現有可工做的應用程序,而又不想修改應用程序。

選擇特性路由:
想把路由與操做代碼保存在一塊兒;
建立新應用程序,或者對現有應用程序進行巨大修改;

傳統路由的集中配置意味着能夠在一個地方理解請求如何映射到操做,傳統路由對自定義約束對象也比特性路由來講更加容易。優先選擇特性路由的緣由是特性路由很好地把關於控制器的全部內容放到了一塊兒,包括控制器使用的URL和運行的操做。

 

九、asp.net處理http請求的大體過程

此處稍微描述下asp.net處理http請求的過程,之因此寫這個,是由於前面大段篇幅介紹了MVC的路由,是個中間處理過程,那前面的過程是個什麼樣的,過來一個http請求,怎麼處理呢,怎麼就到路由了。

現在基於asp.net開發web程序,基本上都是發佈部署到安裝了IIS的windows服務器上。如此,客戶端瀏覽器向服務器發出一個http請求,當IIS檢測到某個HTTP Request後,先根據擴展名判斷請求的是不是靜態資源(好比.html,.img,.txt,.xml等),若是是則直接將文件內容以HTTP Response的形式返回。若是是動態資源(好比.aspx,asp,php等等),則經過擴展名從IIS的腳本影射(Script Map)找到相應的ISAPI Dll。接着它又經過Http Pipeline的管道,傳到工做進程(IIS5.x爲aspnet_wp.exe,IIS6.x和IIS7.x爲w3wp.exe)後,工做進程實例中經過ISAPIRuntime(主要做用是調用一些非託管代碼生成HttpWorkerRequest對象,HttpWorkerRequest對象包含當前請求的全部信息,而後傳遞給HttpRuntime)傳遞HttpWorkerRequest對象給HttpRuntime,並調用HttpRuntime的ProcessRequest方法,HttpRuntime爲管道模型的入口此時正式進入管道模型。

HttpRuntime根據HttpWorkerRequest對象生成HttpContext, 再調用HttpApplicationFactory的GetApplicationInstance方法生成HttpApplication,HttpApplication對象包含多個HttpModule對象,當HttpApplication,調用HttpHandlerFactory的GetHandler方法生成具體的HttpHandler對象,將控制權交給HttpHandler,來處理http請求並返回http響應,再通過HttpApplication對象的一系列事件最終返回到客戶端。

注:當一個HTTP請求到達HttpModule時,整個ASP.NET Framework系統還並無對這個HTTP請求作任何處理,也就是說此時對於HTTP請求來說,HttpModule是一個HTTP請求的「必經之路」,因此能夠在這個HTTP請求傳遞到真正的請求處理中心(HttpHandler)以前附加一些須要的信息在這個HTTP請求信息之上,或者針對截獲的這個HTTP請求信息做一些額外的工做,或者在某些狀況下乾脆終止知足一些條件的HTTP請求,從而能夠起到一個Filter過濾器的做用。

httpContext,稱爲上下文,msdn:

附上兩張圖:

HttpApplication

HttpApplication是整個ASP.NET基礎架構的核心,它負責處理分發給它的HTTP請求。因爲一個HttpApplication對象在某個時刻只能處理一個請求,只有完成對某個請求的處理後,HttpApplication才能用於後續的請求的處理。因此,ASP.NET採用對象池的機制來建立或者獲取HttpApplication對象。具體來說,當第一個請求抵達的時候,ASP.NET會一次建立多個HttpApplication對象,並將其置於池中,選擇其中一個對象來處理該請求。當處理完畢,HttpApplication不會被回收,而是釋放到池中。對於後續的請求,空閒的HttpApplication對象會從池中取出,若是池中全部的HttpApplication對象都處於繁忙的狀態,ASP.NET會建立新的HttpApplication對象。

HttpApplication處理請求的整個生命週期是一個相對複雜的過程,在該過程的不一樣階段會觸發相應的事件。咱們能夠註冊相應的事件,將咱們的處理邏輯注入到HttpApplication處理請求的某個階段。

 

HttpModule

ASP.NET爲建立各類.NET Web應用提供了強大的平臺,它擁有一個具備高度可擴展性的引擎,而且可以處理對於不一樣資源類型的請求。那麼,是什麼成就了ASP.NET的高可擴展性呢? HttpModule功不可沒。

從功能上講,HttpModule之於ASP.NET,就比如ISAPI Filter之於IIS同樣。IIS將接收到的請求分發給相應的ISAPI Extension以前,註冊的ISAPI Filter會先截獲該請求。ISAPI Filter能夠獲取甚至修改請求的內容,完成一些額外的功能。與之類似地,當請求轉入ASP.NET管道後,最終負責處理該請求的是與請求資源類型相匹配的HttpHandler對象,可是在Handler正式工做以前,ASP.NET會先加載並初始化全部配置的HttpModule對象。HttpModule在初始化的過程當中,會將一些功能註冊到HttpApplication相應的事件中,那麼在HttpApplication整個請求處理生命週期中的某個階段,相應的事件會被觸發,經過HttpModule註冊的事件處理程序也得以執行。

 

HttpHandler

若是說HttpModule至關於IIS的ISAPI Filter的話,咱們能夠說HttpHandler則至關於IIS的ISAPI Extension,HttpHandler在ASP.NET中扮演請求的最終處理者的角色。對於不一樣資源類型的請求,ASP.NET會加載不一樣的Handler來處理,也就是說.aspx page與.asmx web service對應的Handler是不一樣的。

全部的HttpHandler都實現了接口IHttpHandler。下面是IHttpHandler的定義,方法ProcessRequest提供了處理請求的實現。

public interface IHttpHandler
    {
        bool IsReusable { get; }
        void ProcessRequest(HttpContext context);
    }

 

十、MVC路由核心

http請求被IIS和Asp.net處理流程

1請求被UrlRoutingModule部件攔截

2封裝請求上下文HttpContext,成爲HttpContextWrapper對象。

3根據當前的HttpContext,從Routes集合中獲得與當前請求URL相符合的RouteData對象。

4將RouteData與HttpContext請求封裝成一個RequestContext對象。

5根據RequestContext對象,從RouteData的RouteHandler中獲取IHttpHandler(MVC裏面會有一個IHttpHandler的實現類MvcHandler)。

6執行IHttpHandler(MvcHandler),而後就是經過反射激活具體的controller,執行具體的action。

下面是路由註冊的源碼:

上述請求在asp.net管道事件中註冊的事件:

一、請求被UrlRoutingModule部件攔截————經過註冊HttpApplication對象的PostResolveRequestCache事件來實現攔截

二、封裝請求上下文HttpContext,成爲HttpContextWrapper對象。————將UrlRoutingModule的Init()方法轉到定義,能夠看到這麼一句: HttpContextBase context = new HttpContextWrapper(((HttpApplication) sender).Context);

三、根據當前的HttpContext,從Routes集合中獲得與當前請求URL相符合的RouteData對象。————將UrlRoutingModule的Init()方法轉到定義,最終會找到PostResolveRequestCache()方法,方法裏面有一句 RouteData routeData = this.RouteCollection.GetRouteData(context);

四、將RouteData與HttpContext請求封裝成一個RequestContext對象。————一樣在上述方法裏面 RequestContext requestContext = new RequestContext(context, routeData);

五、根據RequestContext對象,從RouteData的RouteHandler中獲取IHttpHandler(MVC裏面會有一個IHttpHandler的實現類MvcHandler)。————一樣在該方法裏面 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);

執行IHttpHandler(MvcHandler)。———— context.RemapHandler(httpHandler); 將請求交給MvcHandler處理。

六、而後就是經過反射激活具體的controller,執行具體的action。————在MvcHandler的ProcessRequest()方法裏面的執行邏輯。

 

筆停此處,後續再進行補充

相關文章
相關標籤/搜索