文章引导

MVC路由解析---IgnoreRoute

MVC路由解析---MapRoute

MVC路由解析---UrlRoutingModule

Area的使用

引言

前面我们讲了IgnoreRoute链接

现在我们讲讲核心的MapRoute,还是提前准备Reflection工具,若是没准备,可以看“”MVC路由深入详解1---IgnoreRoute”中的System.Web.dll源码

一.RouteCollection

我们来看看RouteCollection.MapRoute,截图如下:

相信大家看到了RouteCollectionExtensions是一个静态类,是对RouteCollection的扩展(关于扩展方法的大家可以百度,此处不做详细描述)。好家伙,我们看看这个扩展方法走向何方(这个时候就是Reflection发挥作用的时候了)。

引用“”MVC路由深入详解1---IgnoreRoute”中的内容,看看MapRoute的参数传入的是什么:

name:       "Default"

url:           "{controller}/{action}/{id}"

defaults:   new { controller = "Home", action = "Index", id = UrlParameter.Optional }  --->这是个匿名类型

我们看看扩展方法去了哪里:

public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults) => routes.MapRoute(name, url, defaults, null); 

我们接着往下走       

public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints) =>routes.MapRoute(name, url, defaults, constraints, null);

接着

        public Route MapRoute(string name,string url,object defaults,object constraints=null,string[] param=null)
{
if(url==null)
{
throw new ArgumentNullException("url");
}
Route route = new Route(url, new MvcRouteHandler())
{
Defaults=CreateRouteValueDictionaryUncached(defaults),
Constraints=CreateRouteValueDictionaryUncached(constraints),
DataTokens=new System.Web.Routing.RouteValueDictionary()
};
ConstraintValidation.Validate(route);
if ((param != null) && (param.Length > ))
{
route.DataTokens["Namespaces"] = param;
}
Add(name, route);
return route;
}

上面新建了一个Route,Route就是一条具体的路由 ,new Route(url, new MvcRouteHandler())传入规则url和new MvcRouteHandler()。

二.CreateRouteValueDictionaryUncached

        private static System.Web.Routing.RouteValueDictionary CreateRouteValueDictionaryUncached(object values)
{
IDictionary<string, object> dictionary = values as IDictionary<string, object>;
if (dictionary != null)
{
return new System.Web.Routing.RouteValueDictionary(dictionary);
}
return System.Web.WebPages.TypeHelper.ObjectToDictionaryUncached(values);
}          

三.MvcRouteHandler

我们继续拆解MvcRouteHandler

public class MvcRouteHandler: System.Web.Routing.IRouteHandler
{
System.Web.Mvc.IControllerFactory _controllerFactory;
public MvcRouteHandler() { }
public MvcRouteHandler(System.Web.Mvc.IControllerFactory controllFactory)
{
_controllerFactory = controllFactory;
}
protected virtual IHttpHandler GetHttpHandler(System.Web.Routing.RequestContext requestContext)
{
//SetSessionStateBehavior:在派生类重写时,设置支持HTTP请求所必须的会话状态行为的类型
requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext));
return new MvcHandler(requestContext);
}
IHttpHandler System.Web.Routing.IRouteHandler.GetHttpHandler(System.Web.Routing.RequestContext requestContext)
{
return GetHttpHandler(requestContext);
} protected virtual System.Web.SessionState.SessionStateBehavior GetSessionStateBehavior(System.Web.Routing.RequestContext requestContext)
{
string str = (string)requestContext.RouteData.Values["controller"];
if (string.IsNullOrEmpty(str))
{
throw new InvalidOperationException(System.Web.Mvc.Properties.MvcResources.MvcRouteHandler_RouteValuesHasNoController);
}
IControllerFactory factory = _controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();
return factory.GetControllerSessionBehavior(requestContext, str);
}
}

四.RouteValueDictionary

RouteValueDictionary是对Dictionary<string,object>进行包装,下面是RouteValueDictionary拆解

public RouteValueDictionary(object values)
{
this._dictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
this.AddValues(values);
}
private void AddValues(object values)
{
if (values != null)
{
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(values))
{
object obj2 = descriptor.GetValue(values);
this.Add(descriptor.Name, obj2);
}
}
}

我们来看看效果

五.Add(name,route)

我们来看看这个Add方法:

        public void Add(string name, RouteBase item)
{
if (item == null)
{
throw new ArgumentNullException("item");
}
if (!string.IsNullOrEmpty(name) && this._namedMap.ContainsKey(name))
{
object[] args = new object[] { name };
//throw new ArgumentException(string.Format(CultureInfo.CurrentUICulture, System.Web.SR.GetString("RouteCollection_DuplicateName"), args), "name");
}
base.Add(item);
if (!string.IsNullOrEmpty(name))
{
this._namedMap[name] = item;
}
}

里面做了重复路由名称验证,Add方法的第二个参数是RouteBase,我们看看MapRoute方法里传入给Add方法的参数是Route。Route是继承于RouteBase,RouteBase是一个抽象类,这个类是为继承类服务的,里面定义了GetRouteData和GetVirtualPath两个抽象方法。

GetRouteData:解析请求url,提取数据,如:/home/index 得到:controller/home,action/index提取得到的数据会包装成RouteData

GetVirtualPath:生成URL

六.RouteBase Route

public abstract class RouteBase
{
// Methods
protected RouteBase();
public abstract RouteData GetRouteData(HttpContextBase httpContext);
public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
}
public class Route : RouteBase
{
// Fields
private ParsedRoute _parsedRoute;
private string _url;
private const string HttpMethodParameterName = "httpMethod"; // Methods
public Route(string url, IRouteHandler routeHandler);
public Route(string url, RouteValueDictionary defaults, IRouteHandler routeHandler);
public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler);
public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler);
public override RouteData GetRouteData(HttpContextBase httpContext);
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
protected virtual bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection);
private bool ProcessConstraints(HttpContextBase httpContext, RouteValueDictionary values, RouteDirection routeDirection); // Properties
public RouteValueDictionary Constraints { get; set; }
public RouteValueDictionary DataTokens { get; set; }
public RouteValueDictionary Defaults { get; set; }
public IRouteHandler RouteHandler { get; set; }
public string Url { get; set; }
}

Route添加了几个属性

Constraints:保存约束规则,最终保存为RouteValueDictionary

DataTokens:附加参数,指定controller的空间命名也放在这里。

Defaults:保存规则的默认值

url:规则URL

我们来看看GetRouteData,我们看看RouteData routeData=RouteCollection.GetRouteData(context)。

public RouteData GetRouteData(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
if (httpContext.Request == null)
{
throw new ArgumentException(RoutingResources.RouteTable_ContextMissingRequest, "httpContext");
}
if (!this.RouteExistingFiles)
{
string appRelativeCurrentExecutionFilePath = httpContext.Request.AppRelativeCurrentExecutionFilePath;
if (((appRelativeCurrentExecutionFilePath != "~/") && (this._vpp != null)) && (this._vpp.FileExists(appRelativeCurrentExecutionFilePath) || this._vpp.DirectoryExists(appRelativeCurrentExecutionFilePath)))
{
return null;
}
}
using (this.GetReadLock())
{
foreach (RouteBase base2 in this)
{
RouteData routeData = base2.GetRouteData(httpContext);
if (routeData != null)
{
return routeData;
}
}
}
return null;
}

上述代码中最后通过递归遍历自己所有的路由规则,分别调用我们所有注册在RouteTable.Routes--->RouteCollection。

我们还注意到上面有这么一段:

if (!this.RouteExistingFiles)
    {
        string appRelativeCurrentExecutionFilePath = httpContext.Request.AppRelativeCurrentExecutionFilePath;
        if (((appRelativeCurrentExecutionFilePath != "~/") && (this._vpp != null)) && (this._vpp.FileExists(appRelativeCurrentExecutionFilePath) || this._vpp.DirectoryExists(appRelativeCurrentExecutionFilePath)))
        {
            return null;
        }
    }

RouteCollection 有这么一个属性RouteExistingFiles.当为false时,就检测请求的路径地址是否己经存在文件或目录,如果存在,则直接不走路由了,直接返回null,默认就是false,我们可以实验一下。当然这里是忽略了根目录的,不然默认我们 http://www.xxx.com/ 也不能访问了。

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
                new string[] { "MVCTest.Controllers" }
            );
        }

这里是默认的路由注册,按理说我们访问 home 时,会去到 home controller 的 index,但是我们在在项目里加一个 home 目录,如下图。

我们再访问:http://localhost:2144/home/ 我们发现,无法找到该资源,也就是检测到home这个目录存在时,就不走路由了。

为尊重原创,本文的编写参考了以下博文和文章:

程序园: http://www.cnblogs.com/lindaohui/archive/2012/08/31/2664047.html

MVC路由解析---MapRoute的更多相关文章

  1. MVC路由解析---IgnoreRoute

    MVC路由解析---IgnoreRoute   文章引导 MVC路由解析---IgnoreRoute MVC路由解析---MapRoute MVC路由解析---UrlRoutingModule Are ...

  2. MVC路由解析---UrlRoutingModule

    文章引导 MVC路由解析---IgnoreRoute MVC路由解析---MapRoute MVC路由解析---UrlRoutingModule Area的使用 引言: 此文全文内容90%转自 一.前 ...

  3. AspNet Mvc 路由解析中添加.html 等后缀 出现404错误的解决办法

    使用Mvc 有时候我们希望,浏览地址以.html .htm 等后缀名进行结尾. 于是我们就在RouteConfig 中修改路由配置信息,修改后的代码如下 routes.IgnoreRoute(&quo ...

  4. ASP.NET MVC路由解析

    继续往下看<ASP.NET MVC5框架揭秘>. ASP.NET系统通过注册路由和现有的物理文件路径发生映射.而对于ASP.NET MVC来说,请求的是某个Controller中的具体的A ...

  5. c# mvc 路由规则学习片段

    1.初步接触mvc 路由 routes.MapRoute(               "CM",               "CM/{controller}/{act ...

  6. 在ASP.NET非MVC环境中(WebForm中)构造MVC的URL参数,以及如何根据URL解析出匹配到MVC路由的Controller和Action

    目前项目中有个需求,需要在WebForm中去构造MVC的URL信息,这里写了一个帮助类可以在ASP.NET非MVC环境中(WebForm中)构造MVC的URL信息,主要就是借助当前Http上下文去构造 ...

  7. 2016/5/6 thinkphp ①框架 ② 框架项目部署 ③MVC模式 ④控制器访问及路由解析 ⑤开发和生产模式 ⑥控制器和对应方法创建 ⑦视图模板文件创建 ⑧url地址大小写设置 ⑨空操作空控制器 ⑩项目分组

    真实项目开发步骤: 多人同时开发项目,协作开发项目.分工合理.效率有提高(代码风格不一样.分工不好) 测试阶段 上线运行 对项目进行维护.修改.升级(单个人维护项目,十分困难,代码风格不一样) 项目稳 ...

  8. 【基础】MVC路由规则

    一.RouteData解析过程 在ASP.NET MVC中,服务器收到来自客户端的请求后,会经过一些列的处理拿到请求的数据,比如在Pipeline 管线事件中,通过订阅适当的事件,将HttpConte ...

  9. ASP.NET MVC 路由(一)

    ASP.NET MVC路由(一) 前言 从这一章开始,我们即将进入MVC的世界,在学习MVC的过程中在网上搜索了一下,资料还是蛮多的,只不过对于我这样的初学者来看还是有点难度,自己就想看到有一篇引导性 ...

随机推荐

  1. 异常的处理try-catch

    Java异常处理 Java采用的异常处理机制,是将异常处理的程序代码集中在一起, 与正常的程序代码分开,使得程序简洁.优雅,并易于维护. * 异常的处理: 抓抛模型*** 过程一 : 抛, 程序在执行 ...

  2. 嵌入式C语言4.1 C语言内存空间的使用-指针

    指针:就是内存资源的地址.门牌号的代名词 假如你所在的城市是一个内存(存储器),如果找到你家,就是通过你的家庭住址(指针)寻找,而你家里的摆设面积之类的就是内存的内容(指针指向的内容). 指针变量:存 ...

  3. [未解决]报错:ssh_exchange_identification: read: Connection reset by peer

    报错代码: ssh_exchange_identification: read: Connection reset by peer fatal: 无法读取远程仓库. 请确认您有正确的访问权限并且仓库存 ...

  4. Rikka with Nickname (简单题)

    Rikka with Nickname  链接:https://www.nowcoder.com/acm/contest/148/J来源:牛客网 时间限制:C/C++ 2秒,其他语言4秒空间限制:C/ ...

  5. ps学习记录

    基本快捷键: ctrl + 放大 ctrl - 缩小 ctrl 空格键 放大工具 ctrl 0 适合屏幕大小 ctrl 1 显示实际大小 ctrl n 新建画布 ctrl v 移动工具 按住alt键 ...

  6. Node中的Cookie和Session

    1.Cookie HTTP是无状态协议.例:打开一个域名的首页,进而打开该域名的其他页面,服务器无法识别访问者.即同一浏览器访问同一网站,每次访问都没有任何关系. Cookie的原理是:客户端浏览器在 ...

  7. 一、bootstrap-fontawesome-iconpicker组件

    一.bootstrap-fontawesome-iconpicker组件 <!DOCTYPE html> <html lang="en"> <head ...

  8. 三、TortoiseSVN 单独拉取项目某个文件

    一.项目拉取后,单独对某个文件拉取 实践中会出现这样的问题,在svn 中 我项目 ,已经拉取了,但是 某个文件改乱了 ,想从新对某个文件拉取 . 解决方案:1.删除某个文件,从新更新获取-右击该文件上 ...

  9. Codeforces 354C 暴力 数论

    题意:给你一个数组,你可以把数组中的数减少最多k,问数组中的所有数的GCD最大是多少? 思路:容易发现,GCD的上限是数组中最小的那个数,而因为最多可以减少k,及可以凑出来的余数最大是k,那么GCD的 ...

  10. 力扣 ——Linked List Cycle II(环形链表 II) python实现

    题目描述: 中文: 给定一个链表,返回链表开始入环的第一个节点. 如果链表无环,则返回 null. 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始). ...