.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。

.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. ASP.NET MVC 及 Areas 简单控制路由

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

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

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

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

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

  6. 浅谈MVC中路由

    引言 学习ASP.NET MVC 路由这一关是肯定必不可少的.这一节,我们就来简单介绍下MVC中的路由机制.简单的路由机制相信大家都已了解,这一节主要介绍路由中很少使用的部分. 使用静态URL片段 在 ...

  7. MVC中的URL路由(一)

    URL路由系统通过对请求地址进行解析从而得到以目标Controller名称为核心的路由数据.Url路由系统最初是为了实现请求url与物理文件路径分离而建立的,MVC的Url Route是将Url地址与 ...

  8. mvc中路由的映射和实现IHttpHandler挂载

    首先我们了解一下一般的方法 我们只需要在web.config配置文件中做映射处理即可. 第一种形式: <system.web> <urlMappings enabled=" ...

  9. 《Entity Framework 6 Recipes》中文翻译系列 (21) -----第四章 ASP.NET MVC中使用实体框架之在页面中创建查询和使用ASP.NET URL路由过虑

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 4.2. 构建一个搜索查询 搜索数据是几乎所有应用的一个基本功能.它一般是动态的,因 ...

随机推荐

  1. Curator leader 选举(一)

    要想使用Leader选举功能,需要添加recipes包,可以在maven中添加如下依赖: <dependency> <groupId>org.apache.curator< ...

  2. No.005:Longest Palindromic Substring

    问题: Given a string S, find the longest palindromic substring in S. You may assume that the maximum l ...

  3. 百度网盘生成二维码api

    分享出自精神,灵感来自大脑,在百度云网盘分享每一个文件,都会在页面生成一个二维码扫描的图片: 我就进一步看了该图片的地址: 发现没有,圈圈内是不是有点眼熟,就跟其他二维码api接口一样,只要盗用这段东 ...

  4. c++ 操作符重载和友元

    操作符重载(operator overloading)是C++中的一种多态,C++允许用户自定义函数名称相同但参数列表不同的函数,这被称为函数重载或函数多态.操作符重载函数的格式一般为: operat ...

  5. Linux下各个目录的作用

    /binbin是binary的缩写.这个目录沿袭了UNIX系统的结构,存放着使用者最经常使用的命令.例如cp.ls.cat,等等. /boot这里存放的是启动Linux时使用的一些核心文件. /dev ...

  6. svn出现权限不足时的解决方法

    将所有svn目录设置为当前用户所有....即可 sudo chown will:will . -R

  7. Python画图形界面

    使用QTdesigner 生成.ui文件,输入命令行pyuic4 -o test.py test.ui 在生成的Python文件后面输入下面代码 if __name__=="__main__ ...

  8. eclipse调试web项目

    Eclipse上的Web项目调试 在Eclipse中开发Web项目的首要难题就是如何进行代码调试.本文简要说明一下在Eclipse中使用Tomcat和Jetty调试Java Web项目的方法. Tom ...

  9. Xcode——创建你自己的Framework

    (注:以下内容是基于Xcode7.2.1操作的,版本不一,可能界面内容不同!) 如果你想将你开发的控件与别人分享,一种方法是直接提供源代码文件.然而,这种方法并不是很优雅.它会暴露所有的实现细节,而这 ...

  10. sublime text3 配置插件包记录

    前言: 很多插件已经开始放弃支持ST2了,所以推荐使用ST3,大量的最新插件和最新功能已经不再支持st2了. 下载地址戳这里:http://www.sublimetext.com/3 1.所有插件 易 ...