ASP.NET MVC之路由

URL是Uniform Resource Location的缩写,即统一资源定位符。资源是一个抽象概念,既可以指一个文件也可以指方法调用的结果或服务器上的一些其他内容第一部分是协议(服务方式)第二部分是存有该资源的主机IP地址(有时称为端口号)第三部分是主机资源的具体地址(如目录文件名或某段代码)。

高质量的URL应该满足以下几点要求:

  • 域名便于记忆和拼写
  • 简短
  • 便于输入
  • 可以反映出站点结构
  • 应该是“可破解的”,用户可以通过移除URL的末尾,进而到达更高层次的信息体系结构
  • 持久,不能改变

传统的Web框架,URL代表磁盘上的物理文件,与内容存在直接的对应关系。对于ASP.NETMVC来说会应用不同的方法把URL映射到某个类的方法调用。这些类通常称为控制器,用来控制用户输入和系统组件之间的交互。而其中用来响应用户请求的方法通常称为操作。

ASP.NET MVC 框架中的路由主要有两种用途:

  1. 匹配传入的请求(该请求不匹配服务器文件系统中的文件),并把这些请求映射到控制器操作
  2. 构造传出的URL,用来响应控制器操作。

这里的路由和URL重写有很大的不同:

重写关注的是将一个URL映射到另一个URL,路由关注则是如何将URL映射到资源,表示以资源为中心的URL视图,此时URL代表Web上的一个资源(未必是页面)。在ASP.NET路由中,这个资源就是一段代码,在匹配传入URL时用到的映射规则来帮助生成URL,当传入的请求与路由匹配时就会执行该段代码。路由决定如何根据URL特征调度请求,而不会重写URL。

ASP.NET MVC中路由方法:通过定义URL模板来指定与路由相匹配的模式

  • 特性路由:MVC5中在控制器类或操作方法上使用声明式特性的选项
  • 传统路由:集中的强制的基于代码风格来定义路由

特性路由

启用特性路由需要在RegisterRoutes中加入routes.MapMvcAttributeRoutes();

设置特性路由有以下几种方式:

  • 简单路由特性设置:在操作方法上标识[Route("home/index")]
 1 public class HomeController:Controller 
 2 {
 3   [Route("home/Index")]
 4   public ActionResult Index()
 5   {
 6       return View();
 7   }
 8   [Route("home/about")]
 9   public ActionResult About()
10   {
11      return View();
12   }  
13 }

 

  • 定义控制器级别特性,避免重复编写某些相同的代码。如:
 1 [Route("home/{action}")] 
 2 public class HomeController:Controller 
 3  {
 4     
 5     public ActionResult Index()
 6     {
 7         return View();
 8     }
 9   
10    public ActionResult About()
11    {
12      return View();
13     }  
14  }
  • 在操作方法级别指定路由特性时,会覆盖控制器级别指定的任何路由特性
 1 [Route("home/{action}")] 
 2 public class HomeController:Controller 
 3  {
 4     [Route("home")]
 5     [Route("home/Index")]
 6     public ActionResult Index()
 7     {
 8         return View();
 9     }
10     
11     public ActionResult About()
12    {
13       return View();
14    }  
15  }
  • 利用RoutePrefix来设置统一路由
 1 [RoutePrefix("home")]
 2 [Route("{action}")] 
 3 public class HomeController:Controller 
 4  {
 5     [Route("")]
 6     [Route("index")]
 7     public ActionResult Index()
 8     {
 9         return View();
10     }
11     
12     public ActionResult About()
13    {
14       return View();
15    }  
16  }
  •  利用方法重载的方式,为每个URL运行不同的操作
 1 [Route("person/{id}")]
 2 public ActionResult Details(int id)
 3 {
 4   return View();
 5 }
 6 
 7 [Route("person/{name}")]
 8 public ActionResult Details(string name)
 9 {
10   return View();
11 }
  • 上面的方式可能会出现二义性,路由约束,消除路由二义性
 1 [Route("person/{id:int}")]
 2 public ActionResult Details(int id)
 3 {
 4   return View();
 5 }
 6 
 7 [Route("person/{name}")]
 8 public ActionResult Details(string name)
 9 {
10   return View();
11 }
  • 路由默认值,设置某个操作方法称为默认的action  [Route("home/{action=index}")],除了提供了默认值方式也可以设其为可选参数

[Route("{action=Index}/{id?}")]  ,可选路由参数是默认值的特例。从路由角度看,将参数标记为可选参数与列出参数的默认值之间没有太大区别;路由实际上有一个默认值,可选参数只是有一个特殊的默认值UrlParameter.Optional。

     除了让id可选,也可将id的默认值设置为空串{id=}来让路由匹配。不同之处在于当我们把一个参数标记为可选,并且在URL中并没有提供值时,路由就不在字典中添加条目,如果该默认值被设置为空串,那么路由字典将添加一个键,名称为id。

  • 对于像[Route("contacts/{action}/{id}")],路由模板来说,为{action}参数定义了默认值,也需为{id}参数定义默认值或使其成为可选参数
  • 存在字面值的URL像[Route("{action}-{id?}")],对于URL为details-1匹配成功,对于"detail-"形式则不会。任何带有字面值的URL段在匹配URL时每个参数值都需要匹配
  • 对于路由模板形式,只能用于控制器之上,不能用于某个操作方法上。

 

传统路由

      RegisterRoutes是集中配置路由的地方,传统路由设置也在此处。

      routes.MapRoute("simple","{controller}/{action}/{id}")

      在操作方法上使用特性路由时,不需要任何参数,路由就可以工作。路由特性直接放到了操作方法上,当路由匹配时,MVC知道去运行该操作方法。将特性路由放到控制器类上,MVC知道使用哪个类,但是不知道运行哪个方法,所以我们使用特殊的action参数来通过名称指明要运行的方法。

      针对{first}/{second}/{third}形式,如果请求a/b/c这样的URL会收到500错误。是由于传统路由不会自动连接控制器或操作。要指定操作,需要使用action参数,要指定控制器使用controller。否则,mvc则理解不了我们要请求到哪个代码块。

      与特性路由一样,默认值相对与其他路由参数的位置非常重要,当为一个参数指定默认值时,确保也为该参数后面的所有参数指定默认值,否则默认值将被忽略。

  • 路由约束

特性路由的正则表达式的匹配行为和传统路由相反。传统路由总是进行精确匹配,而特性路由的regex内联约束支持部分匹配。传统路由约束year=@"\d{4}"相当于特性路由内联约束{year:regex(^\d{4}$)}。在特性路由中如果想要进行精确匹配,必须显示包含^和$字符。传统路由总是会替我们添加这些字符,不是编写自定义约束,是无法进行部分匹配的。将regex约束在传统路由和特性路由之间移动时,要知道这种区别。

 

传统路由与特性路由选择上:

考虑使用传统路由

  • 想要集中配置所有路由
  • 使用自定义约束对象
  • 存在现有可工作的应用程序,而又不想修改应用程序。   

考虑使用特性路由:

  • 想把路由与操作代码保存在一起
  • 创建新应用程序,或者对现有应用程序进行巨大修改

两者配合使用最好将MapMvcAttrubuteRoutes放在首位,让特性路由具有较高优先级

引入区域概念,如何避免路由冲突

   在路由没有指定名称空间情况下,很容易导致二义性,阻止该异常的一种方法就是在整个项目中使用唯一的控制器名称。如下:

routes.MapRoute(
  "Default",
  "{controller}/{action}/{id}",
  new {controller="Home",action="index",id=""},
  new[] {"AreasDemoWeb.Controllers"}
)

Catch-all参数

  允许路由匹配任意个段的URL,只能作为路由模板中最后一段。如:

routes.MapRoute("catchallroute","Home/{action}/{*other}");

特性路由使用相同语法。上面形式可以匹配:/Home/Index/a/b/c;  /Home/Index/等

贪婪匹配

 对于URL模板{filename}.{ext}形式,当请求的URL为/Foo.xml.aspx时,会把Foo.xml当做filename,把aspx当匹配到ext。所以是尽可能匹配他能匹配的文本。

路由调试

可以使用Route Debugger使用InstallPackage RouteDebugger命令安装

 

posted @ 2017-07-04 12:20  贝同学  阅读(202)  评论(0)    收藏  举报