.NetCore MVC中的路由(1)路由配置基础

0x00 路由在MVC中起到的作用

前段时间一直忙于别的事情,终于搞定了继续学习.NetCore。这次学习的主题是MVC中的路由。路由是所有MVC框架都会实现的一个组件,核心功能就是根据接收到的Http请求中的Path(对于http://localhost/Home/Index/12?test=555 来说,http是协议,localhost是域,Home/Index/12是Path,test=555是参数)部分,依次和路由规则集合中的规则进行匹配,匹配成功后由对应的Controller中的对应Action进行Http请求的处理。匹配不到则返回404错误。

大多数MVC框架路由规则的配置都大同小异,一般都是通过模板的方式来配置路由规则。有的还支持在Controller和Action上通过Attribute(Java中叫注解)进行更细粒度的配置。

.NetCore MVC支持通过全局的路由模板配置路由规则,也支持在Controller和Action上通过Attribute进行细粒度的路由配置。下面先说一下在Startup.cs中配置全局路由规则。

0x01 在Startup.cs中配置路由

所谓的路由的模板就是一串字符串,当接收到Http请求后取出其中的Path部分,和模板进行对照,如果匹配模板则路由到对应的Controller和Action进行处理。我们可以在Startup.cs文件中的Configure方法中,添加MVC功能时进行路由配置,例如:

app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action}/{id}");
});

其中name为路由规则的名称,template为路由模板。这也引出了我们第一个概念,路由模板中的变量。

1.路由模板中的变量

在模板"{controller }/{action }/{id}"中,用花括号括起来的是路由模板中的变量。例如其中变量的作用并不是必须在Path中匹配某个固定的字符串,而是起到一个占位的作用,例如上面的模板就可以匹配由“/”隔开的共三部分的Path,例如a/b/c可以匹配成功。而各个变量的值从Path中对应部分提取出来。例如

Home/Index/12可以匹配,其中controller为Home,action为Index,id为12

Home/Index则匹配失败,因为只有2部分

Home/Index/12/34同样匹配失败,因为超过了3部分。

模板匹配成功后,会根据controller和action提取出的值路由:

Home/Index/12会路由到HomeController的Index方法,变量id为12

Test/Show/ab会路由到TestController的Show方法,变量id为ab

2.变量值得获取

在Index或Show方法中,我们可以有两种方法提取变量:

一种是在方法的参数列表中加入和变量相同名称的参数,MVC会自动从变量列表中寻找并转换为对应类型:

public IActionResult Index(string id, string controller, string action)
{
ViewData["Message"] = "id is " + id + ", controller is " + controller + ", action is " + action;
return View();
}

另一种就是从RouteData中取出:

public IActionResult Index()
{
var controller = RouteData.Values["controller"].ToString();
var action = RouteData.Values["action"].ToString();
var id = RouteData.Values["id"].ToString();
ViewData["Message"] = "id is " + id + ", controller is " + controller + ", action is " + action;
return View();
}

路由模板中的变量名称是可以自己定义的,但controller和action(包括后面讲的area)都是比较特殊的变量。其中controller提取出的值作为Controller的名称,action提取出的值作为Controller中方法的名称。为了让每条路由规则都能够路由到Controller和Action,在路由模板中都应该出现controller和action变量,但我们也可以给controller和action变量指定默认值,这样在Path中省略了这部分时会用默认值代替。

3.变量的默认值

由两种方法可以配置变量的默认值:

app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id=0}");
});

或者

routes.MapRoute(
name: "default",
template: "{controller}/{action}/{id}",
defaults: new
{
controller = ”Home”,
action = ”Index”,
id = ,
});

这样配置后Path中带有默认值的部分可以省略,省略的规则和C#中带默认值的参数一样,例如:

空Path会被路由到HomeController,Index方法

Test会被路由到TestController,Index方法

Test/Show依然会被路由到TestController,Show方法

一般我会用第一种方法配置默认值,更加直观和方便。但有时候有些需求是第一种方法难以做到的。例如我想给TestController的Show方法配置路由为TestShow,使用第一种方法可以这样配置:”TestShow/{controller=Test}/{action=Show}”,这样配置当Path为TestShow时的确可以路由到TestController的Show方法,但当Path为TestShow/Home/Index时会路由到HomeController的Index方法。

使用第二种方法配置:

routes.MapRoute(
name: "test",
template: "TestShow",
defaults: new
{
controller = ”Test”,
action = ”Show”,
});

当Path为TestShow时可以路由到TestController的Show方法,但Path为Test/Home/Index则无法匹配模板。关于细粒度的路由配置更好的方法是给Test方法使用Route特性(Attribute)进行配置,后面会说到。

4.路由规则中的静态字符

除了使用变量来配置路由模板,还可以使用静态字符串。静态字符串可以单独使用,也可以与变量混合使用。

例如模板为:

”Durow/{controller}/{action}”

Durow/Home/About会路由到HomeController,About方法

Durow/Test/Show会路由到TestController,Show方法

也可以把静态字符和变量混合起来,例如配置模板为:

”My{controller}/My{action}”

MyHome/MyAbout会被路由到HomeController,About方法

MyTest/MyShow会被路由到TestController,Show方法

5.使用?标记变量可选

除了通过给变量提供默认值使其可选外,也可以使用?把变量标记为可选。例如模板

“{controller}/{action}/{id?}”

其中id为可选变量,这样配置后

Home/Index和Home/Index/12都会成功匹配。

6.使用*提取Path中剩余的所有部分

如果一个模板需要匹配包含任意多个部分(Segments)的Path,可以使用*符号指定变量,使用*制定过的变量会把Path中匹配完成后剩余部分全部提取出来,例如模板:

”{controller}/{action}/{id?}/{*others}”

Home/Index/12/a/b/c/d,会路由到HomeController的Index方法,id为12,others为a/b/c/d

实际上仅从模板匹配的角度来说,上面的模板可以匹配所有的Path。唯一的问题就是匹配后对应的Controller和Action可能不存在。

7.多条路由规则的选择

实际应用中很可能会配置多条路由规则,当接收到Path时很可能不止一条规则能够匹配。

最简单的,我们配置以下两条模板:

“{controller }/{action =About}”

“{controller }/{action =Index }”

当Path为Home时两条路由都能匹配,那要怎么选择呢?其实很简单粗暴,就是看哪条路由在前面。也就是说Path一旦成功匹配到模板后就会立即实施路由并忽略后面的模板。对于上面的配置来说Home会被路由到HomeController的About方法。所以在配置路由时一定要注意顺序。

0x02 使用Attribute配置路由

除了在Startup.cs中配置全局路由规则外,也可以针对单个Controller和其中的Action配置路由。方法就是在Controller类和Action方法上使用Route特性。例如在TestController的Show方法上使用Route特性:

[Route("TestShow")]
public IActionResult Show()
{
return View();
}

当Path为TestShow时,即可路由到TestController的Show方法。

上面我们在介绍默认值时提到过,通过全局模板配置:

routes.MapRoute(
name: "test",
template: "TestShow",
defaults: new
{
controller = ”Test”,
action = ”Show”,
});

也可以达到同样的目的。不过区别在于,使用后一种方法时,如果还有”{controller}/{action}”这样的模板,除了TestShow外,当Path为Test/Show可以匹配这个模板并路由到TestController的Show方法。而通过在Show方法上配置Route特性后,只有TestShow才可以路由,即使同时存在”{controller}/{action}”这样的模板,Test/Show也无法路由。

第一次接触用Route特性配置路由时,我很疑惑路由组件是如何把Path路由到对应的Controller和Action的,后来下了个断点看了下RouteData对象,发现对于配置了路由的Action方法,其controller为方法所在的Controller的名称,action为方法的名称,而且在Route特性配置的路由模板中不能够使用{controller}变量和{action}变量。这样就保证了匹配模板的Path总能路由到这个Action。

对于在Controller类上配置的Route特性最终会分别配置到Controller中的每个Action上。例如我们在TestController上配置Route(“TestShow”),实际上就是给每个方法配置了Route(“TestShow"),所以当Path为TestShow时会报错,提示有两个action满足匹配。那么应当如何给Controller通过Route配置路由呢,可以使用[controller]和[action]。

Route特性中的[controller]和[action]

对于[controller]和[action]我也不知道该怎么叫,不能叫变量,功能上类似占位符。当我们在Controller类用Route特性配置路由时,如果使用了[controller]和[action],这样当Route特性给Controller中每个Action配置路由时,[controller]会被替换为Controller名称,[action]会被替换为Action名称。举个例子还是给TestController配置Route特性,配置为Route(“durow/[controller]/[action]”),这样对于其中的Index方法来说,其路由模板为”durow/Test/Index”,controller为Test,action为Index。而对于Show方法来说路由模板为”durow/Test/Show”,controller为Test,action为Show。前面说过MVC会为每个Action创建一个ActionDescriptor对象存储这个Action的路由信息。对于配置了Route特性的Action(再重复一下,给Controller类配置Route特性相当于给Controller中的每个Action配置Route特性),其ActionDescriptor中会有一个AttributeRouteInfo对象,对于未配置Route特性的Action,该对象为空。AttributeRouteInfo中包含了路由模板信息。

所以对于上面TestController的Route特性的配置,配置为Route(“durow/Test/[action]”)也能达到同样的效果。不过使用Route(“durow/[controller]/[action]”)语义更强更通用。

在Route特性中使用变量

在Route特性中配置模板也是可以使用变量的,同样可以使用?标记变量可选。例如可以给TestController配置Route(“durow/[controller]/[action]/{id?}”)。但需要注意的是Route特性的模板中变量不能使用默认值(包括[controller]和[action]),也不能使用*提取Path所有剩余部分。

0x03 写在最后

啰啰嗦嗦居然写了这么多,其实实际使用中很可能用不到多么复杂的路由,一般一条通用规则,一条Area相关的规则就可以了。不过详细了解了路由规则,当以后遇到有些奇葩的特殊需求时能够有更加开阔的思路。后面讲讨论一下路由模板中的约束和自定义约束。再后面讨论一下使用Areas。


更多内容欢迎访问我的博客:http://www.durow.vip

.NetCore MVC中的路由(1)路由配置基础的更多相关文章

  1. .NetCore MVC中的路由(2)在路由中使用约束

    p { margin-bottom: 0.25cm; direction: ltr; color: #000000; line-height: 120%; orphans: 2; widows: 2 ...

  2. YbSoftwareFactory 代码生成插件【二十四】:MVC中实现动态自定义路由

    上一篇介绍了 公文流转系统 的实现,本篇介绍下MVC下动态自定义路由的实现. 在典型的CMS系统中,通常需要为某个栏目指定个友链地址,通过指定友链地址,该栏目的地址更人性化.方便记忆,也有利用于搜索引 ...

  3. .NetCore Linux中安装Grafana界面及配置InfluxDB相关设置

    前面的文章已经安装好了InfluxDB 安装 wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.1. ...

  4. ASP.NET MVC 及 Areas 简单控制路由

    ASP.NET MVC中怎么去控制路由,这个想关的文章很多,我在这里就是自我总结一下,仅供参考. 1.我们新建一个项目,查看RouteConfig.cs,代码如下: public static voi ...

  5. ASP.NET没有魔法——ASP.NET MVC 直连路由(特性路由)

    之前对Controller创建的分析中,知道了Controller的创建是有两个步骤组成,分别是Controller的类型查找以及根据类型创建Controller实例. 在查询Controller的类 ...

  6. ASP.NET Core 3.0中使用动态控制器路由

    原文:Dynamic controller routing in ASP.NET Core 3.0 作者:Filip W 译文:https://www.cnblogs.com/lwqlun/p/114 ...

  7. MVC中使用Hangfire执行定时任务

    需求描述 项目中有一个通知公告的功能,在后台管理员添加公告后需要推送消息给所有注册用户,让其查看消息.消息推送移动端采用极光推送,但是消息在何时发送是个问题,比如说有一个重要的会议通知,可能希望在会议 ...

  8. mvc中Url.RouteUrl或者Html.RouteLink实现灵活超链接,使href的值随路由名称或配置的改变而改变[bubuko.com]

    mvc,超链接除了直接写在a标签的href内还可以使用路由规则来生成,这样在改变了路由规则或者路由名称时不用再去代码中更改href的值,而且还容易遗漏.借助Url.RouteUrl或者Html.Rou ...

  9. ASP.NET Core MVC 中两种路由的简单配置

    1.全局约定路由 这种方式配置优先级比较低,如果控制器或者方法上标记了特性路由那么优先走特性路由. 当建立好一个mvc项目里,路由都是默认配置好的. 如果建立的是空项目那么需要手动配置: 1.需要在C ...

随机推荐

  1. 你必须知道的EF知识和经验

    注意:以下内容如果没有特别申明,默认使用的EF6.0版本,code first模式. 推荐MiniProfiler插件 工欲善其事,必先利其器. 我们使用EF和在很大程度提高了开发速度,不过随之带来的 ...

  2. nodejs进阶(2)—函数模块调用

    函数调用 1. 文件内普通函数调用 创建一个js文件命名为2_callFunction.js,其中定义一个函数fun1,向返回对象输出了一段字符串“你好,我是fun1”. //------------ ...

  3. JavaScript动画-磁性吸附

    ▓▓▓▓▓▓ 大致介绍 磁性吸附是以模拟拖拽为基础添加一个拖拽时范围的限定而来的一个效果,如果对模拟拖拽有疑问的同学请移步模拟拖拽. 源代码.效果:点这里 ▓▓▓▓▓▓ 范围限定(可视区) 先来看一个 ...

  4. Opserver开源的服务器监控系统(ASP.NET)

    Opserver是Stack Exchange下的一个开源监控系统,系统本身由C#语言开发的ASP.NET(MVC)应用程序,无需任何复杂的应用配置,入门很快.下载地址:https://github. ...

  5. CGI与FastCGI nginx+PHP-FPM

    本文转载自CGI与FastCGI 1.当我们在谈到cgi的时候,我们在讨论什么 最早的Web服务器简单地响应浏览器发来的HTTP请求,并将存储在服务器上的HTML文件返回给浏览器,也就是静态html. ...

  6. js学习之变量、作用域和内存问题

    js学习之变量.作用域和内存问题 标签(空格分隔): javascript 变量 1.基本类型和引用类型: 基本类型值:Undefined, Null, Boolean, Number, String ...

  7. SAP CRM 树视图(TREE VIEW)

    树视图可以用于表示数据的层次. 例如:SAP CRM中的组织结构数据可以表示为树视图. 在SAP CRM Web UI的术语当中,没有像表视图(table view)或者表单视图(form view) ...

  8. Java实现多线程断点下载(下载过程中可以暂停)

    线程可以理解为下载的通道,一个线程就是一个文件的下载通道,多线程也就是同时开启好几个下载通道.当服务器提供下载服务时,使用下载者是共享带宽的,在优先级相同的情况下,总服务器会对总下载线程进行平均分配. ...

  9. docker – 你应该知道的10件事

      容器并不是一个全新的技术,但这并不妨碍Docker如风暴一样席卷整个世界. 如果你在IT圈里,你一定听说过Docker.就算与其他热门技术,如:Puppet/Chef,Hadoop或者MongoD ...

  10. 跟着老男孩教育学Python开发【第一篇】:初识Python

    Python简介 Python前世今生 Python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解 ...