Asp.Net MVC4 系列-- 进阶篇之路由

原文  http://blog.csdn.net/lan_liang/article/details/22993839

创建一个路由

打开 RouteConfig.cs  ,发现已经创建了一个默认路由 :

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

为了说明路由的url匹配过程,暂时comment掉default参数。

打开Global.cs ,可以看到路由配置文件已经注册:

protected void Application_Start()
        {
    AreaRegistration.RegisterAllAreas();

    WebApiConfig.Register(GlobalConfiguration.Configuration);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);
        }

关于路由工作方式

Asp.net MVC Framework 的路由部分,是插入在http pipeline中的,当接受到http请求,会寻找注册的路由表(在ApplicationStart时候注册,就是应用启动时候),找到路由规则,获取每个路由规则的pattern,试图匹配当前请求合适的那个route,匹配成功,则解析出controller和action,从controllerfactory找到相应的controller,把请求传递给action,如果请求中传参,路由还会解析出参数,给action。

下面是几种url匹配的例子:


http://mysite/Admin/Index


Controller =Admin,Action=Index


http://mysite/Index/Admin


Controller=Index,Action=Admin


http://mysite/Apples/Oranges


Controller=Apples,Action=Oranges


http://mysite/Admin


匹配失败,Segment太少


http://mysite/Admin/Index/Soccer


匹配失败,Segment太多

路由会调用route handler来完成路由过程,默认的,mvc应用会使用MVCRouteHandler.手动添加一个Route,就可以体现出来:

routes.Add("MyRoute",newRoute("{controller}/{action}", new MvcRouteHandler()));

指定默认(default)

刚才说明url匹配时候,拿掉了default参数,这时我们一起看看default参数的作用。

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

可以看到最后一个参数,指定了一个默认的controller和action。


Mydomain.com


Controller = Home ,action=Index


Mydomain.com/Customer


Controller=Customer ,action=Index


Mydomain.com/Customer/List


Controller=Customer, action=List


Mydomain.com/Customer/List/All


匹配失败,segment太多

定值Segment

场景1,所有请求中第一个Segment为”public”的,需要统一处理,因此定义一个路由:

routes.MapRoute(name: "PublicReqRoute", url:"Public/{controller}/{action}",
                           defaults: new {controller = "PublicHome", action ="Index"});

示例url: http://mysite/Public

匹配结果:controller = PublicHome,action=Index

场景2,请求中以public开始的,需要统一处理,定义路由:

routes.MapRoute(name: "PublicReqRoute", url:"Public{controller}/{action}",
                           defaults: new {controller = "PublicHome", action ="Index"});

示例url: Http:/mysite/PublicX/

匹配结果:controller=X,action=Index

场景3:有个controller或action不工作,需要改个bug,把所有原来指向这个controller的请求暂时路由到首页:

routes.MapRoute("myshop","Shop/OldAction",
new { controller = "Home", action ="Index" });

注意:路由是按着添加顺序依次解析的,因此把最特殊的那个路由放在前面,避免先fall 到相对generall的那个。

获取参数

对于Route:

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

请求: http://mysite/Home/Index/15

Action 中使用RouteData.Values获取传入的id:

public ActionResult CustomVariable() {
ViewBag.Controller = "Home";
ViewBag.Action = "CustomVariable";
ViewBag.CustomVariable = RouteData.Values["id"];
return View();
}

使用mvcframework Route System自动传参机制

除了使用RouteData.Values取出传入的参数,可以更简单的定义个参数在action,但是 参数名要和 route 定义的相同 (id)

Public  ActionResult  CustomVariable(string id) {
ViewBag.Controller = "Home";
ViewBag.Action = "CustomVariable";
ViewBag.CustomVariable = id;
return View();
}

对于url: http://mysite/Home/Index/15 ,id就会自动被赋值为15传入action

定义可选参数

依然对于url:

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

Id=UrlParameter.Optional,此时id就是可选参数


Mydomain.com


Controller=home ,action=Index


Mydomain.com/customer


Controller=customer, action=Index


Mydomain.com/customer/List


Controller=customer, action=List


Mydomain.com/customer/List/All


Controller=customer , action=List, Id=All


Mydomain.com/customer/List/All/Delete


url 匹配失败

如果没有传参,action提供了id参数,那么id此时就为null;

public  ActionResultCustomVariable(string id) {
ViewBag.Controller = "Home";
ViewBag.Action = "CustomVariable";
ViewBag.CustomVariable = id == null ? "<novalue>" : id;
return View();
}

作为另一个选择,可以指定一个默认参数,如果没url没传值,默认参数值就会被使用。

Public  ActionResultCustomVariable(string id = "DefaultId") {
ViewBag.Controller = "Home";
ViewBag.Action = "CustomVariable";
ViewBag.CustomVariable = id;
return View();
}

使用{*catchall}捕捉超出数量的segment

例如,对于这条route:

routes.MapRoute("MyRoute","{controller}/{action} /{*catchall}",
new { controller = "Home", action ="Index",
id = UrlParameter.Optional });

由于使用了{*catchall},对于url:

http://mysite/Home/Index/All/More/More/More

此时,controller=Home,Action=Index, catchall=”All/More/More/More”

这样,就把解析剩下segment的工作交给了自己处理

解决在不同namespace的同名controller

例如现在有两个controller,在不同的命名空间:

namespace UrlsAndRoutes.Controllers {
public class HomeController : Controller {
public ActionResult Index() {
ViewBag.Controller = "Additional Controllers - Home";
ViewBag.Action = "Index";
return View("ActionName");
}
}
}

namespace UrlsAndRoutes.AdditionalControllers {
public  classHomeController : Controller {
public  ActionResultIndex() {
ViewBag.Controller = "Additional Controllers -Home";
ViewBag.Action = "Index";
return View("ActionName");
}
}
}

对于这种情况,可能希望路由先在一个指定的命名空间里找,找到了返回:

routes.MapRoute("MyRoute","{controller}/{action}/{id}/{*catchall}",
new { controller = "Home", action ="Index",
id = UrlParameter.Optional
},
new[] { "URLsAndRoutes.AdditionalControllers" });

关键在于最后的那个命名空间参数,mvc framework会优先找 “URLsAndRoutes.AdditionalControllers”里面的controller,如果没找到,会搜索其余的命名空间。

注意,这个new []{}数字里的参数是平行的,也就是说,如果mvc framework在这些命名空间里找到多个同名controller,不会找到第一个就返回,依然会抛出异常,例如:

new[] { "URLsAndRoutes.AdditionalControllers","UrlsAndRoutes.Controllers"});

对于url:

http://mysite/home/Index/15

mvc framework会在指定的命名空间数组里找,由于找到了多个homecontroller,因此抛出异常。

如果希望这样:指定多个命名空间,找到了第一个就返回,怎么做?

可以配置多条route:

routes.MapRoute("AddContollerRoute","Home/{action}/{id}/{*catchall}",
new { controller = "Home", action ="Index",
id = UrlParameter.Optional },
new[] { "URLsAndRoutes.AdditionalControllers" });

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

mvc framework就会按着添加的顺序依次查找匹配的controller和action,找到了把解析好的参数(如果有)传递给action就返回了。

只允许mvc framework在指定的 namespace里找,如何做?

Route myRoute=routes.MapRoute("AddContollerRoute",
"Home/{action}/{id}/{*catchall}",
new { controller = "Home", action ="Index",
id = UrlParameter.Optional },
new[] { "URLsAndRoutes.AdditionalControllers" }); 

myRoute.DataTokens["UseNamespaceFallback"] = false;

由于把DataTokens[“UseNamespaceFallback”] 设为false,因此mvcframework在指定的命名空间:URLsAndRoutes.AdditionalControllers"里面找完了,就不去其他地方找了。

给路由加限制

正则限制

可以加正则限制在controller和action:

routes.MapRoute("MyRoute","{controller}/{action}/{id}/{*catchall}",
new { controller = "Home", action ="Index", id = UrlParameter.Optional },
new { controller = "^H.*"},
new[] { "URLsAndRoutes.Controllers"});

由于给controller加上了正则表达式:”^H.*”的限制,因此对于url匹配了urlpattern,解析出controller,如果不是以H开头的,也会被过滤掉。

类似的,也可以使用给action加正则限制:

new { controller = "^H.*", action ="^Index$|^About$"}

这样,就限制了action必须是Index和About。

使用Http Method限制

可以限制请求是Get ,Post亦或是Put ,Delete等类型的。

new { controller = "^H.*", action ="Index|About",
httpMethod = new HttpMethodConstraint("GET") } ,

这样,就限制了请求必须是Get方式的。

注意,给route加httpmethod限制,和给controller还有action加httpmethod区别在于,route在httppipeline很早的位置就被处理了,而到controller和action的时候,已经是httppipeline很晚的时候了,controller和action已经被解析了,参数也已经被解析了(如果有)。

自定义限制

Mvc 提供了IRouteConstranit 接口,可以自己定义限制,通过实现这个接口:

public class UserAgentConstraint : IRouteConstraint {
private string requiredUserAgent;
public UserAgentConstraint(string  agentParam) {
requiredUserAgent = agentParam;
}
Public  boolMatch(HttpContextBase  httpContext, Routeroute, string  parameterName,
RouteValueDictionary  values,RouteDirection routeDirection) {
return httpContext.Request.UserAgent != null &&
httpContext.Request.UserAgent.Contains(requiredUserAgent);
}
}

以上代码,实现了一个限制,必须Request对象中的代理对象不为空,也就是必须使用代理才能访问,并且代理名称包含指定的名称。

使用自定义限制:

routes.MapRoute("ChromeRoute","{*catchall}",
new { controller = "Home", action ="Index" },
new {
customConstraint = newUserAgentConstraint("Chrome")
},
new[] { "UrlsAndRoutes.AdditionalControllers" });

这样,就实现了,必须使用chrome才能访问的限制。

让访问物理文件目录的请求也参与路由

默认情况下,route system会先检查url是否指向了一个物理文件目录,如果是,返回找到这个文件,返回;否则,执行注册到路由表里面的每一条route。也就是说,指向物理文件目录的请求实际在mvc路由机制之前已经被处理掉了,默认没有参与路由。

如果要改变这个行为,例如访问

http://mysite/resource/pic/   ,不希望route去找resource/pic物理目录,而希望route找resourcecontroller ,pic action,那么:

在注册路由时,加上

routes.RouteExistingFiles = true;

这样,本地文件的检测 就会在路由机制执行之后了。

在路由前后,httppipeline的示意图

其中,httpmodule 1和 httpmodule2 在pipeline上面routesystem前后的两个module。

加上route.RouteExistingFiles=true

Route system中,就会先执行MVC Route,后执行CheckDisk Files .最后,为物理文件指定一个route:

routes.MapRoute("DiskFile","Content/StaticContent.html",
new {
controller = "Resource",
action = "List",
});

By Passing Route System

有些访问资源文件的请求,是需要ignore的,这种需求直接用ignoreRoute就可以了:

routes.IgnoreRoute("Content/{filename}.html");

这样,对于url: http://mysite/Content.test.html 的请求就被route忽略了,访问页面,就会出现资源无法找到和404错误:

时间: 2024-10-15 07:27:31

Asp.Net MVC4 系列-- 进阶篇之路由的相关文章

SQL Server调优系列进阶篇(深入剖析统计信息)

前言 经过前几篇的分析,其实大体已经初窥到SQL Server统计信息的重要性了,所以本篇就要祭出这个神器了. 该篇内容会很长,坐好板凳,瓜子零食之类... 不废话,进正题 技术准备 数据库版本为SQL Server2008R2,利用微软的以前的案例库(Northwind)进行分析,部分内容也会应用微软的另一个案例库AdventureWorks 相信了解SQL Server的朋友,对这两个库都不会太陌生. 概念理解 关于SQL Server中的统计信息,在联机丛书中是这样解释的 查询优化的统计信

SQL Server调优系列进阶篇(如何索引调优)

前言 上一篇我们分析了数据库中的统计信息的作用,我们已经了解了数据库如何通过统计信息来掌控数据库中各个表的内容分布.不清楚的童鞋可以点击参考. 作为调优系列的文章,数据库的索引肯定是不能少的了,所以本篇我们就开始分析这块内容,关于索引的基础知识就不打算深入分析了,网上一搜一片片的,本篇更侧重的是一些实战项内容展示,希望通过本篇文章各位看官能在真正的场景中找到合适的解决方法足以. 对于索引的使用,我希望的是遇到问题找到合适的解决方法就可以,切勿乱用!!! 本篇在分析出索引的优越性的同时也将负面影响

SQL Server调优系列进阶篇(如何维护数据库索引)

原文:SQL Server调优系列进阶篇(如何维护数据库索引) 前言 上一篇我们研究了如何利用索引在数据库里面调优,简要的介绍了索引的原理,更重要的分析了如何选择索引以及索引的利弊项,有兴趣的可以点击查看. 本篇延续上一篇的内容,继续分析索引这块,侧重索引项的日常维护以及一些注意事项等. 闲言少叙,进入本篇的主题. 技术准备 数据库版本为SQL Server2012,前几篇文章用的是SQL Server2008RT,内容区别不大,利用微软的以前的案例库(Northwind)进行分析,部分内容也会

SQL Server调优系列进阶篇(查询语句运行几个指标值监测)

原文:SQL Server调优系列进阶篇(查询语句运行几个指标值监测) 前言 上一篇我们分析了查询优化器的工作方式,其中包括:查询优化器的详细运行步骤.筛选条件分析.索引项优化等信息. 本篇我们分析在我们运行的过程中几个关键指标值的检测. 通过这些指标值来分析语句的运行问题,并且分析其优化方式. 通过本篇我们可以学习到调优中经常利用的几个利器! 废话少说,开始本篇的正题. 技术准备 数据库版本为SQL Server2008R2,利用微软的一个更简洁的案例库(Northwind)进行分析. 利器一

asp.net core 系列 8 Razor框架路由(下)

三.页面路由操作约定 接着上篇讲asp.net core 系列 7 Razor框架路由.在上篇继续第三节 "页面路由操作约定" 的最后一小节 AddPageRoute . 3.3. 配置页面路由AddPageRoute 使用 AddPageRoute 配置路由,该路由与指定页面关联, 使用指定的路由生成页面链接. AddPageRoute 使用 AddPageRouteModelConvention 建立路由. 示例应用为 Privacy.cshtml 创建指向 /ThePrivacy

《手把手教你》系列进阶篇之2-python+ selenium自动化测试 - python基础扫盲(详细教程)

1. 简介 这篇文章主要是分享讲解一下,如何封装自己用到的方法和类.以便方便自己和别人的调用,这样就可以避免重复地再造轮子. 封装(Encapsulation)是面向对象的三大特征之一(另外两个是继承和多态),它指的是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问. 就好比使用计算机,我们只需要使用计算机提供的键盘,就可以达到操作计算机的目的,至于在敲击键盘时计算机内部是如何工作,我们根本不需要知道. 封装机制保证了类内部

SQL Server调优系列进阶篇(查询优化器的运行方式)

前言 前面我们的几篇文章介绍了一系列关于运算符的基础介绍,以及各个运算符的优化方式和技巧.其中涵盖:查看执行计划的方式.几种数据集常用的连接方式.联合运算符方式.并行运算符等一系列的我们常见的运算符.有兴趣的童鞋可以点击查看. 本篇介绍在SQL Server中查询优化器的工作方式,也就是一个好的执行计划的形成,是如何评估出来的,作为该系列的进阶篇. 废话少说,开始本篇的正题. 技术准备 数据库版本为SQL Server2008R2,利用微软的一个更简洁的案例库(Northwind)进行分析. 正

asp.net core 系列 7 Razor框架路由(上)

一.概述 在上二篇中,主要是介绍了asp.net core mvc中路由的使用,这篇继续介绍路由在ASP.NET Core Razor中的使用.Razor Pages应该使用默认的传统路由,从应用程序的Pages文件夹中提供命令资源.还可以使用其他约定来自定义 Razor Pages 路由行为. 在ASP.NET Core MVC 中是使用路由中间件来匹配传入请求的 URL 并将它们映射到操作(action).而ASP.NET Core  Razor使用页面路由和应用模型提供程序约定,来控制 R

asp.net core 系列 5 MVC框架路由(上)

一. 概述 介绍asp.net core路由时,我初步想了下,分几篇来说明.  路由的知识点很多,参考了官方文档提取出一些重要的知识点来说.    在ASP.NET Core中是使用路由中间件来匹配传入请求的 URL 并将它们映射到操作(action方法).路由是在程序启动时进行传统路由或属性路由定义. 路由描述如何将 URL 路径与操作相匹配. 它还用于在响应中生成送出的 URL(用于链接). 路由操作既支持传统路由,也支持属性路由.也可混合使用.通常传统路由用于为浏览器处理 HTML 页面的