MVC如何在Pipeline中接管请求的?

文章内容

上个章节我们讲到了,可以在HttpModules初始化之前动态添加Route的方式来自定义自己的HttpHandler,最终接管请求的,那MVC是这么实现的么?本章节我们就来分析一下相关的MVC源码来验证一下我们的这个问题。

先创建一个MVC3的Web Application,选择默认的模板以便创建以后就默认包含HomeController和AccountController。我们知道MVC要先接管请求才能通过这些Controller来处理,那我们先去Global.asax.cs文件里看代码(定义接管请求要在初始化HttpModule之前,所以只能到这里来找代码(或者是利用WebActivator之类的特性来动态添加),Global.asax.cs文件里代码很少,但是有我们需要的东西,首先在Application_Start的方法里发现一行代码:

RegisterRoutes(RouteTable.Routes);

这行代码,看调用的方法名称RegisterRoutes是注册Route的意思,但是为什么参数却是全局的RouteTable.Routes集合呢?找到RegisterRoutes方法来看看具体的内容:

public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}

该方法有2行代码,第一行是忽略一个Route(我们先不看这个),第二行是使用MapRoute方法注册一个新的Route,默认是映射到Home Controller的Index Action上,我们可能想到了,RouteCollection(也就是刚才传入的RouteTable.Routes)的MapRoute方法就是提供我们所说的接管请求的入口,但是如何把MVC自己的HttpHandler传进去的呢?我们Go to一下这个MapRoute方法(需要安装ReShaper来查找MVC的源码),调整到了MVC的RouteCollectionExtensions类,发现MapRoute并不是RouteCollection自带的方法,而是在MVC源码里提供的一个扩展方法,代码如下:

public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces)
{
if (routes == null)
{
throw new ArgumentNullException("routes");
}
if (url == null)
{
throw new ArgumentNullException("url");
} Route route = new Route(url, new MvcRouteHandler())
{
Defaults = new RouteValueDictionary(defaults),
Constraints = new RouteValueDictionary(constraints),
DataTokens = new RouteValueDictionary()
}; if ((namespaces != null) && (namespaces.Length > 0))
{
route.DataTokens["Namespaces"] = namespaces;
} routes.Add(name, route); return route;
}

该代码的主要作用是new一个新的Route,然后将该Route添加到我们刚才提到的静态集合RouteTable.Routes里,以便后期查找Handler的时候使用,OK,这一步符合我们前面章节的分析。

接下来看,Route是如何new出来的,代码里的参数传入的分别是我们知道的url,以及一个MVCRouteHandler的实例,这一步也符合我们前面的分析,那我们来看一下MVCRouteHandler的GetHttpHandler方法是如何实现的获取MVCHandler的:

    public class MvcRouteHandler : IRouteHandler {
private IControllerFactory _controllerFactory; public MvcRouteHandler() {
} public MvcRouteHandler(IControllerFactory controllerFactory) {
_controllerFactory = controllerFactory;
} protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) {
requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext));
return new MvcHandler(requestContext);
} protected virtual SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext) {
string controllerName = (string)requestContext.RouteData.Values["controller"];
IControllerFactory controllerFactory = _controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();
return controllerFactory.GetControllerSessionBehavior(requestContext, controllerName);
} #region IRouteHandler Members
IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext) {
return GetHttpHandler(requestContext);
}
#endregion
}

看以上的粗体代码,MvcRouteHandler在实现了IRouteHandler的GetHttpHandler,该方法调用了MvcRouteHandler自身定义的GetHttpHandler虚方法,而在这个虚方法里我们看到了一个非常重要而又期待已久的代码——返回MvcHandler的实例,大概看一下MvcHandler这个类,得到它就是我们所猜想的:继承于IHttpHandler接口的一个类,并且也继承了 IHttpAsyncHandler接口,我们先不管MvcHandler内部是如何实现的,但我们前面几章节的全部分析终于得到了验证,也就说在这里得到了Mvc的专用处理Handler,然后调用它的BeginProcessRequest方法进入Mvc自身的Pipeline进行处理了。

至此,我们终于弄明白了Mvc在整个ASP.NET Runtime是如何接管请求的了,也应该大概清楚整个ASP.NET Runtime的运行机制了,至于MvcHandler的实现方式,我们会在后面的很多章节逐一给大家分析每行代码,今天我们还有一个小任务,那就是:看完了Mvc的实现机制,我们能否自己来写一个自定义的HttpHandler通过Route动态注册进去,来实现我们自己的自定义扩展,我们来尝试着做一下吧。

第一步:建立HttpHandler类

    public class TomHandler : IHttpHandler
{
public TomHandler(RequestContext requestContext)
{
// do nothing
} public virtual void ProcessRequest(HttpContext context)
{
string url = context.Request.Url.AbsoluteUri;
context.Response.Write("当前地址为:" + url);
context.Response.End();
// 这里我们什么都不做,只输出URL地址
} public virtual bool IsReusable
{
get
{
return false;
}
}
}

第二步:建立RouteHandler类

public class TomRouteHandler : IRouteHandler
{
IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
{
return new TomHandler(requestContext);
}
}

在GetHttpHandler实现里,返回我们定义的TomHandler实例。

第三步:注册我们的Route和RouteHandler

protected void Application_Start(object sender, EventArgs e)
{
Route route = new Route("tom/{other}", new TomRouteHandler());
RouteTable.Routes.Add(route);
}

我们设置成,只要访问tom文件夹下的任意文件或者子目录,都提示该URL。下面是我的测试结果:

访问:Http//localhost/tom/

结果:没有提示我们预想的结果(原因是不符合我们的规则)

访问:Http//localhost/tom/123/

结果:输出正常(说明TomHandler已经接管了该请求)

访问:Http//localhost/tom/123.aspx?id=123

结果:输出正常(也说明TomHandler已经接管了该请求)

在建立真实的tom文件夹,然后在里面建立一个 index.html文件(内容为123),然后访问Http//localhost/tom/index.html,规则符合我们的Route定义,但输出结果却不是我们预期的结果,而是123,怎么回事?还记得上一章节里谈到的RouteCollection的GetRouteData方法么?该方法是先判断URL对应的文件是否真实存在,如果存在就直接输出,如果不存在就再来找RouteData的数据,这就解释了上面的index.html路径为什么不是我们期望结果的原因了吧?。

注:如果你建立一个index.aspx文件,并在index.aspx.cs文件里写Response.Write代码输出123的话,该文件也会按照aspx页面的正常周期来执行(也就是说输出123字符串),如果你在<system.web>里的httpHandlers节点用remove命令把*.aspx的匹配设置去掉,那结果就只会输出index.aspx这个文件里的字符串了(包括里面内嵌的任何C#代码)。

最后总结了,到这里,我们已经知道了MvcHandler是如何接管请求的了,而且自己也做了一个简单的例子来验证这套机制。重新回顾一下前面的这么多篇文章,我们应该大概对ASP.NET RunTime, Pipeline以及ASP.NET MVC切入点应该有个整体的了解了。

同步与推荐

本文已同步至目录索引:MVC之前的那点事儿系列

MVC之前的那点事儿系列文章,包括了原创,翻译,转载等各类型的文章,如果对你有用,请推荐支持一把,给大叔写作的动力。

 
 

MVC如何在Pipeline中接管请求的?的更多相关文章

  1. MVC之前的那点事儿系列(9):MVC如何在Pipeline中接管请求的?

    文章内容 上个章节我们讲到了,可以在HttpModules初始化之前动态添加Route的方式来自定义自己的HttpHandler,最终接管请求的,那MVC是这么实现的么?本章节我们就来分析一下相关的M ...

  2. Netty源码分析 (五)----- 数据如何在 pipeline 中流动

    在上一篇文章中,我们已经了解了pipeline在netty中所处的角色,像是一条流水线,控制着字节流的读写,本文,我们在这个基础上继续深挖pipeline在事件传播 Unsafe 顾名思义,unsaf ...

  3. Netty数据如何在 pipeline 中流动

    前言 在之前文章中,我们已经了解了pipeline在netty中所处的角色,像是一条流水线,控制着字节流的读写,本文,我们在这个基础上继续深挖pipeline在事件传播 Unsafe对象 顾名思义,u ...

  4. ASP.NET MVC如何在Action中返回脚本并执行

    我们都知道在aspx页面的cs文件中只要用Respos.Write("<script></scritp>")就可以在前台执行脚本 但是在MVC中就不一样了, ...

  5. asp.net mvc 如何在View中获取Url参数的值

    如果url是 /home/index?id=3 直接Request就ok. 但是如果路由设定为:{controller}/{action}/{id} url是 /home/index/3   这时想在 ...

  6. MVC 如何在action中获取当前网站的根路径

    如果基于MVC搭建的网站在IIS发布的是一个单独的端口,那么可以直接通过后面语句获得跟路径:Request.Url.GetLeftPart(UriPartial.Authority).ToString ...

  7. ASP.NET CORE MVC 2.0 如何在Filter中使用依赖注入来读取AppSettings,及.NET Core控制台项目中读取AppSettings

    问: ASP.NET CORE MVC 如何在Filter中使用依赖注入来读取AppSettings 答: Dependency injection is possible in filters as ...

  8. 如何在WinForm中发送HTTP请求

    如何在WinForm中请求发送HTTP 手工发送HTTP请求主要是调用 System.Net的HttpWebResponse方法 手工发送HTTP的GET请 求: string strURL = &q ...

  9. [Web 前端] 如何在React中做Ajax 请求?

    cp from : https://segmentfault.com/a/1190000007564792 如何在React中做Ajax 请求? 首先:React本身没有独有的获取数据的方式.实际上, ...

随机推荐

  1. Base64中文不能加密问题

    最近用到了Base64.js来对url参数进行加密,字母和数字都可以很好地加密/解密. 但测试中文时发现不能进行转换,貌似Base64.js不支持中文字符. 联想到encodeURI()对url的编码 ...

  2. (大数据工程师学习路径)第四步 SQL基础课程----select详解

    准备 在正式开始本内容之前,需要先从github下载相关代码,搭建好一个名为mysql_shiyan的数据库(有三张表:department,employee,project),并向其中插入数据. 具 ...

  3. POJ 2762 Going from u to v or from v to u?(强连通分量+拓扑排序)

    职务地址:id=2762">POJ 2762 先缩小点.进而推断网络拓扑结构是否每个号码1(排序我是想不出来这点的. .. ).由于假如有一层为2的话,那么从此之后这两个岔路的点就不可 ...

  4. jsp跳转后台代码页的简易方式~

    jsp跳转到代码隐藏页.有几种方法,例如,: action方式: jquery方式,码如下面: function regCust(){         $('#containerFRM').form( ...

  5. Telephone Lines USACO 月赛

    以前做过这套题目 这个题又重新写了:http://www.cnblogs.com/jh818012/archive/2013/05/05/3182681.html 还是以前的思路 一直错在一个地方:决 ...

  6. Git与TortoiseGit基本操作(转)

    1. GitHub操作 本节先简单介绍 git 的使用与操作, 然后再介绍 TortoiseGit 的使用与操作. 先看看SVN的操作吧, 最常见的是 检出(Check out ...), 更新 (U ...

  7. 删RAC中间ASM和LISTENER 资源的正确方法

    在我们的安装RAC什么时候.有时因各种错误加载错误ASM实例或LISTENER显示器,或增加CLUSTERWARE资源,但并没有真正的工作,例如: 如图所看到的,这里在2个节点分别创建过ASM1和AS ...

  8. 一个IIS网站的异常配置的安全解决方案

    一个.如下面的错误: "/"应用server错. 安全异常 说明: 应用程序试图运行安全策略不同意的操作.要授予此应用程序所需的权限.请与系统管理员联系,或在配置文件里更改该应用程 ...

  9. ASP.NET MVC+EF框架+EasyUI实现权限管理系列(18)-过滤器的使用和批量删除数据(伪删除和直接删除)

    原文:ASP.NET MVC+EF框架+EasyUI实现权限管理系列(18)-过滤器的使用和批量删除数据(伪删除和直接删除) ASP.NET MVC+EF框架+EasyUI实现权限管系列 (开篇)   ...

  10. ASP.NET MVC2.0 自定义filters

    今天大家共同学习下ASP.NET MVC2.0中自定义filters,这一节主要学习下ActionFilterAttribute, ActionFilterAttribute继承IActionFilt ...