路由机制

VC框架中路由具有重要作用,本文主要介绍路由的一些知识。目录如下:

1、引言

2、什么是路由

3、特性路由

4、传统路由

5、MVC区域

6、路由调试

7、路由的其它一些信息

8、选择特性路由还是传统路由

9、asp.net处理http请求的大致过程

 

1、引言

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路由。

 

2、什么是路由

上述通过url打开页面,可以说是浏览器http请求,通过路由机制到具体的页面。那么,什么是路由呢?路由相当于是一个中转,是一个配置。也许单纯说路由的定义,显得很抽象,那直接说路由能干什么:

ASP.net MVC的路由主要用途有两种:

1、 匹配传入的请求,该请求不匹配服务器文件系统中的文件,而是把请求映射到控制器操作;

2、 构造传出的URL,用来响应控制器操作;

表面来看,路由和URL重写很相似,此处简单说说二者区别:

URL重写关注的是将一个URL映射到另一个URL,路由关注的则是如何将URL映射到资源;

URL重写只能用于传入的请求URL,而不能帮助生成原始的URL,路由却可以使用它在匹配传入URL时用到的映射规则来帮助生成URL。

 

3、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/,不能打开页面。侧面反映了可选参数也是基于操作方法来做具体调整的。

 

4、传统路由

此处先禁用特性路由,编写一个最传统的路由规则,传统路由都需要在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 })

结果达到预期。

 

 5、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,即开启路由调试。

 

 7、路由如何生成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属性保存着外界路由值。

 8、选择特性路由还是传统路由

特性路由和传统路由结合使用时,路由系统按顺序检查每个路由,并选择第一个匹配的路由。在实际使用过程中,建议将routes.MapMvcAttributeRoutes();放到首位,特性路由通常更加具体,而传统路由更加宽泛。

选择传统路由:
想要集中配置所以路由;
使用自定义约束对象;
存在现有可工作的应用程序,而又不想修改应用程序。

选择特性路由:
想把路由与操作代码保存在一起;
创建新应用程序,或者对现有应用程序进行巨大修改;

传统路由的集中配置意味着可以在一个地方理解请求如何映射到操作,传统路由对自定义约束对象也比特性路由来说更加容易。优先选择特性路由的原因是特性路由很好地把关于控制器的所有内容放到了一起,包括控制器使用的URL和运行的操作。

 

9、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);
    }

 

10、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管道事件中注册的事件:

1、请求被UrlRoutingModule部件拦截————通过注册HttpApplication对象的PostResolveRequestCache事件来实现拦截

2、封装请求上下文HttpContext,成为HttpContextWrapper对象。————将UrlRoutingModule的Init()方法转到定义,可以看到这么一句: HttpContextBase context = new HttpContextWrapper(((HttpApplication) sender).Context);

3、根据当前的HttpContext,从Routes集合中得到与当前请求URL相符合的RouteData对象。————将UrlRoutingModule的Init()方法转到定义,最终会找到PostResolveRequestCache()方法,方法里面有一句 RouteData routeData = this.RouteCollection.GetRouteData(context);

4、将RouteData与HttpContext请求封装成一个RequestContext对象。————同样在上述方法里面 RequestContext requestContext = new RequestContext(context, routeData);

5、根据RequestContext对象,从RouteData的RouteHandler中获取IHttpHandler(MVC里面会有一个IHttpHandler的实现类MvcHandler)。————同样在该方法里面 IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);

执行IHttpHandler(MvcHandler)。———— context.RemapHandler(httpHandler); 将请求交给MvcHandler处理。

6、然后就是通过反射激活具体的controller,执行具体的action。————在MvcHandler的ProcessRequest()方法里面的执行逻辑。

 

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

posted @ 2018-06-12 10:37  小项目笔记  阅读(3057)  评论(1编辑  收藏  举报

更多文章请关注公众号:小项目笔记

小项目笔记