路由机制

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()方法里面的执行逻辑。

 

笔停此处,后续再进行补充

相关文章
相关标签/搜索