C#ASP.NETMVC5路由系统机制详细讲解(转)

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

C#MVC5路由系统机制详细讲解(转)
MVC5路由系统机制详细讲解
转载
MVC5路由系统机制详细讲解
https:///slowlifes/article/details/72461440
请求⼀个 mvc的⽹站和以前的web form是有区别的, MVC框架内部给我们提供了路由机制,当IIS接受到⼀个请求时,会先看是否请求了⼀个静态资源(.html,css,js,图⽚等),这⼀步是web form和mvc都是⼀样的,如果不是则说明是请求的是⼀个动态页⾯,就会⾛的管道,mvc的程序请求都会⾛路由系统,会映射到⼀个Controller对应的Action⽅法,⽽web form请求动态页⾯是会查找本地实际存在⼀个aspx⽂件。

下⾯通过⼀个 MVC5项⽬来详细介绍⼀下 MVC5路由系统的机制。

⼀、认识Global.asax.cs
当我们创建⼀个 MVC5的项⽬的时候会在项⽬的根⽬录中⽣成⼀个Global.asax⽂件。

1public class MvcApplication : System.Web.HttpApplication
2 {
3protected void Application_Start()
4 {
5//注册 MVC 应⽤程序中的所有区域
6 AreaRegistration.RegisterAllAreas();
7//注册全局的Filters
8 FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
9//注册路由规则
10 RouteConfig.RegisterRoutes(RouteTable.Routes);
11//注册打包绑定(js,css等)
12 BundleConfig.RegisterBundles(BundleTable.Bundles);
13 }
14 }
这个Application_Start⽅法会在⽹站启动的⾃动调⽤,其中我们看到:RouteConfig.RegisterRoutes(RouteTable.Routes);这个就是向 MVC 框架注册我们⾃定义的路由规则,让之后的URL能够对应到具体的Action。

接下来我们再来看看RegisterRoutes⽅法做了些什么?
1public class RouteConfig
2 {
3public static void RegisterRoutes(RouteCollection routes)
4 {
5 routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
6 routes.MapRoute(
7 name: "Default",
8 url: "{controller}/{action}/{id}",
9 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
10 );
11 }
12 }
上⾯代码是vs⾃动为你们⽣成,只定义了⼀个默认规则。

1、规则名:Default
2、URL分段:{controller}/{action}/{id},分别有三段,第⼀段对应controller参数,第段为action参数,第三段为id参数
3、URL段的默认值:controller为Home,action为Index,id = UrlParameter.Optional表⽰该参数为可选的。

之所以我们访问/ 这样的URL⽹址能正确返回,是因为我们设置了URL段的默认值,相当于访问:
/Home/Index
RegisterRoutes调⽤的是RouteCollection的MapRoute⽅法,RouteCollection是⼀个集合,继承于Collection<RouteBase>
⼆、 MVC默认的命名约定
1、Controller命名约定
Controller类必须以Controller结尾,⽐如:HomeController,ProductController。

我们在页⾯上⽤HTML heper来引⽤⼀个Controller的时只需要前⾯Home,Product就可以, MVC框架⾃带的DefaultControllerFactory⾃动为我们在结尾加上Controller,并开始根据这个名字开始找对应的类。

我们创建⼀个 MVC项⽬新加的Controller,⽂件会⾃动放在根⽬录的Controllers⽂件夹⾥⾯,我们刚开始可以看到有⼀个HomeController.cs。

当然你也可以实现接⼝IControllerFactory,定义⾃⼰的ControllerFactory来改变查找Controller⽂件的⾏为。

我会再以后的⽂章中介绍。

2、View命名约定
MVC的视图View默认情况是放在根⽬录的Views⽂件下的,规则是这样的:/Views/ControllerName/ActionName.cshtml。

⽐如:HomeController的Action名字为Index的视图对应⽂件为:/Views/Home/Index.cshtml
因此是通过Controller和Action的名字来确定视图⽂件的位置的。

采⽤这个命名约定的好处是在Action返回视图的时候会MVC框架会按照这个约定找到默认的视图⽂件。

⽐如在ProductController的Action⽅法List最后是这样的代码:
return View();
会⾃动去路径,/Views/Product/找⽂件List.cshtml(或者List.aspx如果使⽤的⽼的视图引擎)。

当然也可以指定视图的名字:
return View("~/Views/Product/List.cshtml")
或者
return View("MyOtherView")
MVC框架在查找具体的默认视图⽂件时,如果在/Views/ControllerName/下⾯没有找到,会再在/Views/Shared下⾯找,如果都没找到就会找错:找不到视图。

三、 MVC的URL规则说明
最开始我们在⽹站的Application_Start事件中注册⼀些路由规则routes.MapRoute,当有请求过来的时候,mvc框架会⽤这些路由规则去匹配,⼀旦找到了符合要求就去处理这个URL。

例如有下⾯这个URL:
/Admin/Index
URL可以分为⼏段,除去主机头和url查询参数,MVC框架是通过/来把URL分隔成⼏段的。

上⾯的URl分为两段。

如下图:
第⼀段的值为Admin,第⼆段的值为Index,我们是很容易看出Admin对应就是Controller,Index就是Action。

但是我们要告诉MVC框架这样的规则,因此为下⾯的Application_Start有下⾯的代码:
1 routes.MapRoute(
2 name: "Default",
3 url: "{controller}/{action}/{id}",
4 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
5 );
上⾯表⽰URL规则是:
{controller}/{action}
这个路由规则有两个段,第⼀个是controller,第⼆个是action。

声明url段每个部分要且{}括起来,相当于占位符,是变量。

当⼀个URL请求到来的时候MVC路由系统就负责把它匹配到⼀个具体的路由规则,并把URL每段的值提取出来。

这⾥说“⼀个具体的路由规则”,是因为可能会注册多个路由规则,MVC路由系统会根据注册顺序⼀个⼀个的查找匹配,直到到为⽌。

默认情况,URL路由规则只匹配与之有相同URL段数量的URL。

如下表:
URL URL段
/Admin/Index controller = Admin action = Index
/Index/Admin controller = Index action = Admin
/Apples/Oranges controller = Apples action = Oranges
/Admin⽆匹配-段的数量不够
/Admin/Index/Soccer⽆匹配-段的数量超了
四、mvc创建⼀个简单的Route规则
我们在前⾯注册路由规则都是通过下⾯的⽅式:
1public static void RegisterRoutes(RouteCollection routes) {
2
3 routes.MapRoute("MyRoute", "{controller}/{action}");
4 }
⽤到了RouteCollection的MapRoute⽅法。

其实我们还可以调⽤ Add⽅法,传⼀个Route的实例给它⼀样的达到相同的效果。

1public static void RegisterRoutes(RouteCollection routes) {
2
3 Route myRoute = new Route("{controller}/{action}", new MvcRouteHandler());
4 routes.Add("MyRoute", myRoute);
5 }
五、mvc路由的默认值的设定
之前有说:URL路由规则只匹配与之有相同URL段数量的URL,这种是严格,但是我们⼜想有些段不⽤输⼊,让⽤户进⼊指定的页⾯。

像,/Home/就是进⼊进⼊Home的Index。

只需要设定mvc路由的默认值就可以了。

1public static void RegisterRoutes(RouteCollection routes) {
2 routes.MapRoute("MyRoute", "{controller}/{action}", new { action = "Index" });
3 }
要设置Controller和Action的默认值。

1public static void RegisterRoutes(RouteCollection routes) {
2
3 routes.MapRoute("MyRoute", "{controller}/{action}",
4new { controller = "Home", action = "Index" });
5 }
下⾯是⼀个具体的Url对应的Route映射。

Url段的数量实例Route映射
controller = Home action = Index
/Customer controller = Customer action = Index
/Customer/List controller = Customer action = List
/Customer/List/All⽆匹配—Url段过多
六、mvc使⽤静态URL段
前⾯定义路由规则都是占位符的形式,{controller}/{action},我们也可以使⽤在使⽤静态字符串。

如:
1public static void RegisterRoutes(RouteCollection routes) {
2
3 routes.MapRoute("MyRoute", "{controller}/{action}",
4new { controller = "Home", action = "Index" });
5
6 routes.MapRoute("", "Public/{controller}/{action}",
7new { controller = "Home", action = "Index" });
8 }
上⾯匹配:/Public/Home/Index
路由:"Public/{controller}/{action}"只匹配有三段的url,第⼀段必须为Public,第⼆和第三可以是任何值,分别⽤于controller和action。

除此这外,路由规则中可以既包含静态和变量的混合URL段,如:
1public static void RegisterRoutes(RouteCollection routes) {
2
3 routes.MapRoute("", "X{controller}/{action}");
4
5 routes.MapRoute("MyRoute", "{controller}/{action}",
6new { controller = "Home", action = "Index" });
7
8 routes.MapRoute("", "Public/{controller}/{action}",
9new { controller = "Home", action = "Index" });
10
11 }
七、mvc的路由中⾃定义参数变量
mvc框架除了可以定义⾃带的controller和action的参数之外,还可以定义⾃带的变量。

如下:
1public static void RegisterRoutes(RouteCollection routes) {
2
3 routes.MapRoute("MyRoute", "{controller}/{action}/{id}",
4new { controller = "Home", action = "Index", id = "1" });
5 }
上⾯定义了⼀个id,默认值我们设为1。

这个路由可以匹配0-3个url段的url,第三个url段将被⽤于id。

如果没有对应的url段,将应⽤设置的的默认值。

⾃定义参数变量使⽤:
⽅法⼀、
1public ViewResult CustomVariable() {
2
3 ViewBag.CustomVariable = RouteData.Values["id"];
4return View();
5 }
MVC框架从URL获取到变量的值都可以通过RouteData.Values["xx"],这个集合访问。

⽅法⼆、
1public ViewResult CustomVariable(int id) {
2
3 ViewBag.CustomVariable = id;
4return View();
5 }
MVC框架使⽤内置的Model绑定系统将从URL获取到变量的值转换成Action参数相应类型的值。

这种转换除了可以转换成基本int,string等等之外还可以处理复杂类型,⾃定义的Model,List集合等。

⼋、mvc定义可选URL段、可选参数
mvc定义参数是也可以设置为可选的,这样⽤户可以不⽤输⼊这部分的参数。

1、注册路由时定义可选URL段
public static void RegisterRoutes(RouteCollection routes) {
routes.MapRoute("MyRoute", "{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}
2、通过Action参数来定义可选参数
public ViewResult CustomVariable(string id = "DefaultId") {
ViewBag.CustomVariable = id;
return View();
}
通过Action参数来定义可选参数是没有加默认值的,⽽通过注册路由时定义可选URL段是加了默认值的,是利⽤c#参数的默认参数特性。

这样如果⽤户没有输⼊这部分url段,就会默认值就会被使⽤。

九、mvc使⽤*来定义变长数量的URL段
除了在路由规则中声明固定的数量的URL段,我们也可以定义变长数量的URL段,如下⾯代码:
public static void RegisterRoutes(RouteCollection routes) {
routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}
通过变量前⾯加⼀个星号(*)开头就能匹配任意变长数量的URL。

匹配URL如下:
Url段的数量实例Route映射
controller = Home action = Index
/Customer controller = Customer action = Index
/Customer/List controller = Customer action = List
/Customer/List/All controller = Customer action = List
id = All
/Customer/List/All/Delete controller = Customer action = List
id = All
catchall = Delete
/Customer/List/All/Delete/Perm controller = Customer action = List
id = All
catchall = Delete /Perm
⼗、mvc使⽤命名空间来为路由的Controller类定优先级
当⼀个⽤户输⼊⼀个URL请求 MVC的⽹站时, MVC会根据URL的获取请求是找到是哪⼀个Controller类,如果⼀个项⽬有多相同的类名的Controller,就会有问题。

⽐如:当请求的变量controller的值为Home时,MVC框架就会去找⼀个Controller名字为HomeController的类,这个类(HomeController)默认是不受限制的,如果多个命名空间都有名字为HomeContoller的类, MVC 就不知道怎么办了。

当这种情况发⽣是,就会报错:
“/”应⽤程序中的服务器错误。

找到多个与名为“Home”的控制器匹配的类型。

如果为此请求(“{controller}/{action}/{id}”)提供服务的路由没有指定命名空间以搜索与此请求相匹配的控制器,则会发⽣这种情况。

如果是这样,请通过调⽤带有 'namespaces' 参数的 "MapRoute" ⽅法的重载来注册此路由。

“Home”请求找到下列匹配的控制器:
WebApplication1.Controllers.HomeController
WebApplication1.Controllers1.HomeController
[InvalidOperationException: 找到多个与名为“Home”的控制器匹配的类型。

如果为此请求(“{controller}/{action}/{id}”)提供服务的路由没有指定命名空间以搜索与此请求相匹配的控制器,则会发⽣这种情况。

如果是这样,请通过调⽤带有 'namespaces' 参数的 "MapRoute" ⽅法的重载来注册此路由。

“Home”请求找到下列匹配的控制器:
解决办法:
1public static void RegisterRoutes(RouteCollection routes) {
2
3 routes.MapRoute("Default",
4"{controller}/{action}/{id}",
5new { controller = "Home", action = "Index", id = UrlParameter.Optional },
6new string[] { "WebApplication1.Controllers" }
7 );
8 }
上⾯MapRoute的最后⼀个参数,new string[] { "WebApplication1.Controllers" }就是指定先去命名空间为WebApplication1.Controllers查找在controller,如果找到就停⽌往下找,没找到还是会去其它命名空间中去找的。

因此当你指定的这个命名空间如果没存在要找的controller 类,⽽在其它命名空间是有的,是会正常执⾏的,所以这⾥指定命名空间并不是限定了命名空间,⽽只是设了⼀个优先级⽽已。

⼗⼀、mvc定义路由规则的约束
在前⾯我们介绍了为mvc路由的规则设置路由默认值和可选参数,现在我们再深⼊⼀点,我们要约束⼀下路由规则。

1、⽤正则表达式限制 mvc路由规则
1public static void RegisterRoutes(RouteCollection routes) {
2
3 routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",
4new { controller = "Home", action = "Index", id = UrlParameter.Optional },
5new { controller = "^H.*"},
6new[] { "URLsAndRoutes.Controllers"});
7 }
上⾯⽤到正则表达式来限制 mvc路由规则,表⽰只匹配contorller名字以H开头的URL。

2、把 mvc路由规则限制到到具体的值
1public static void RegisterRoutes(RouteCollection routes) {
2
3 routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",
4new { controller = "Home", action = "Index", id = UrlParameter.Optional },
5new { controller = "^H.*", action = "^Index$|^About$"},
6new[] { "URLsAndRoutes.Controllers"});
7 }
上例在controller和action上都定义了约束,约束是同时起作⽤是,也就是要同时满⾜。

上⾯表⽰只匹配contorller名字以H开头的URL,且action变量的值为Index或者为About的URL。

3、把 mvc路由规则限制到到提交请求⽅式(POST、GET)
1public static void RegisterRoutes(RouteCollection routes) {
2
3 routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",
4new { controller = "Home", action = "Index", id = UrlParameter.Optional },
5new { controller = "^H.*", action = "Index|About",
6 httpMethod = new HttpMethodConstraint("GET") },
7new[] { "URLsAndRoutes.Controllers" });
8 }
上⾯表⽰只匹配为GET⽅式的请求。

4、使⽤接⼝IRouteConstraint⾃定义⼀个 mvc路由约束
下⾯我⾃定义⼀个约束对特定浏览器进⾏处理。

UserAgentConstraint.cs:
1using System.Web;
2using System.Web.Routing;
3
4namespace URLsAndRoutes.Infrastructure {
5
6public class UserAgentConstraint : IRouteConstraint {
7private string requiredUserAgent;
8
9public UserAgentConstraint(string agentParam) {
10 requiredUserAgent = agentParam;
11 }
12
13public bool Match(HttpContextBase httpContext, Route route, string parameterName,
14 RouteValueDictionary values, RouteDirection routeDirection) {
15
16return erAgent != null &&
17 erAgent.Contains(requiredUserAgent);
18 }
19 }
20 }
mvc⾃定义路由约束的使⽤:
1public static void RegisterRoutes(RouteCollection routes) {
2
3 routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",
4new { controller = "Home", action = "Index", id = UrlParameter.Optional },
5new {
6 controller = "^H.*", action = "Index|About",
7 httpMethod = new HttpMethodConstraint("GET", "POST"),
8 customConstraint = new UserAgentConstraint("IE")
9 },
10new[] { "URLsAndRoutes.Controllers" });
11 }
上⾯表⽰这个路由规则只匹配⽤户使⽤IE浏览器的请求。

利⽤这点我们就可以实现不同浏览器使⽤不同的Controller,进⾏不同的处理。

虽然这样做的意义不⼤,但是不排除有时会有这种变态的需求。

⼗⼆、mvc将URL路由到磁盘⽂件
mvc的⽹站并不是所以的url请求都是对应controller,action,我们仍然要⼀种⽅式来提供⼀些静态内容,⽐如:html⽂件,css,图
⽚,javascript⽂件些,其实默认情况下mvc框架在在收到url请求时会先判断这个url是否是对应⼀个磁盘中真实存在的⽂件,如果是直接返回,这时路由是没有使⽤到的,如果不是真实存在的⽂件时才会⾛路由系统,再去匹配注册的路由规则。

这种默认的处理url机制顺序我们也可以改变它,让在检查物理⽂件之前就应⽤路由,如下:
1public static void RegisterRoutes(RouteCollection routes) {
2
3 routes.RouteExistingFiles = true;
4
5 routes.MapRoute("DiskFile", "Content/StaticContent.html",
6new {
7 controller = "Account", action = "LogOn",
8 },
9new {
10 customConstraint = new UserAgentConstraint("IE")
11 });
12
13 routes.MapRoute("MyRoute", "{controller}/{action}/{id}/{*catchall}",
14new { controller = "Home", action = "Index", id = UrlParameter.Optional },
15new {
16 controller = "^H.*", action = "Index|About",
17 httpMethod = new HttpMethodConstraint("GET", "POST"),
18 customConstraint = new UserAgentConstraint("IE")
19 },
20new[] { "URLsAndRoutes.Controllers" });
21 }
我们把RouteExistingFiles属性设置为true,表⽰存在的⽂件也⾛路由,上⾯我们把Content/StaticContent.html这个⽂件映射到controller 为Account,action 为LogOn中了,⽽并不是指磁盘中存在的⽂件。

基于 mvc的这个特性我们就可以实现mvc以.html结尾的伪静态⼗三、mvc跳过、绕开路由系统设定
上⾯我们⽤使⽤routes.RouteExistingFiles = true,让所有的请求都⾛路由系统过⼀下,难免有⼀些性能影响,因为⼀些图⽚,⼀些真正的html,⽂件是没有必要的。

我们可以对些⽂件做⼀些起特殊设定让它们跳过、绕开路由系统。

下⾯就是让Content⽬录下的所有⽂件都绕开mvc的路由系统:
1public static void RegisterRoutes(RouteCollection routes) {
2
3 routes.RouteExistingFiles = true;
4
5 routes.MapRoute("DiskFile", "Content1/StaticContent.html", 6new {
7 controller = "Account", action = "LogOn",
8 },
9new {
10 customConstraint = new UserAgentConstraint("IE")
11 });
12
13 routes.IgnoreRoute("Content/*{filename}");
14 routes.MapRoute("", "{controller}/{action}");
15
16 }。

相关文档
最新文档