控制器 - 应用路由(二)

从设计角度出发,一个 ASP.NET 应用程序并非强制地依赖物理页面。在 ASP.NET MVC 中,用户发出请求并在某个资源上做相应处理。然而,就整个框架而言,并非授权这种语法来描述资源和行为。我深信这样的表述使你很可能地想到了 REST(Representational State Transfer)。

虽然你可以在一个 ASP.NET MVC 应用中使用纯粹的 REST 方案, 但是我想说的是ASP.NET 是松散的面向 REST,它承认例如资源和行为的概念。举个例子,在一个纯 REST 解决方案中,你会使用 HTTP 动词来表示对应的行为 - GET,POST,PUT 和 DELETE,并使用 URL 来定位资源。在 ASP.NET 中实现一个纯 REST 解决方案是可行的,但是你需要做额外的工作。

ASP.NET MVC 的默认行为是使用自定义的 URL,并且路由到哪些行为和资源,这都是由你负责的。这些语法形式一系列 URL 模式集合表达的,也被称为路由。

URL 模式和路由

一个路由就是一个模式匹配字符串,换句话说就是通过它来匹配一个 URL 的绝对路径,URL 字符串是不包含协议,服务器,和端口信息的。一个路由是一个常量字符串,但是它往往也包含一些占位符。底下就是一个例子:

/home/test

这个路由是一个常量字符串,而且仅能匹配 URL 为 /home/test 的路由。绝大多数的情况下,我们更多的是处理那些包含一个或多个占位符的路由。这里有两个例子:

/{resource}/{action}

/Customer/{action}

这两个路由仅会匹配那些包含两个片段的 URL。后者仅会匹配首个片段字符串为“Customer”的 URL。二前者,并没有对片段设置相应的要求。

占位符通常被称作 URL 参数,它是由{}包裹的。你可以在一个路由中添加多个占位符,只要它们之间被常量或者分隔符分开。斜杠/在路由的多个片段之间充当分隔符。占位符的名称(比如 action)就是键,通过它,你可以在代码中通过编程的方式获取对应片段中的值。

这就是 ASP.NET MVC 应用程序的默认路由:

{controller}/{action}/{id}

在这个路由中,包含了三个占位符,并且被斜杠所分割开。而底下的 URL 就可以匹配这个路由:

/Customers/Edit/ALFKI

你可以添加任意多的路由,在路由中也可以添加任意多的占位符。同样,你也可以删除程序默认的路由。

定义应用路由

一个应用程序的路由通常定义在 global.asax 文件中,而且他们是在程序启动的时候被处理。现在,就看一下 global.asax 处理路由的代码片段:

 1 public class MvcApplication : HttpApplication
 2 {
 3     proteted void Application_Start()
 4     {
 5         RouteConfig.RegisterRoutes(RouteTable.Routes);
 6
 7         // Other code
 8         ...
 9     }
10 }

RegisterRoutes 是 RouteConfig 类中的一个方法,这个类被定义在项目中其他的文件夹下。通常这个文件夹叫 App_Start (你也可以随意定义这个文件夹的名称)。底下就是这个类的定义:

 1 public class RouteConfig
 2 {
 3     public static void RegisterRoutes(RouteCollection routes)
 4     {
 5         // Other code
 6         ...
 7
 8         // Listing routes
 9         routes.MapRoute(
10             "Default",
11             "{controller}/{action}/{id}",
12             new {
13                 controller = "Home",
14                 action = "Index",
15                 id = UrlParameter.Optional
16             });
17     }
18 }

通过上面代码可以看到,Application_Start 事件处理器通过调用 RegisterRoutes 公共静态方法来注册所有路由。注意到 RegisterRoutes 方法的名称和原型是随意的,所以如果你有充分的理由,你就可以修改它。
应用所支持的路由必须注册到 Route 对象的一个静态集合中,它是由 ASP.NET MVC 管理,这个集合就是 RouteTable.Routes。你一般通过 MapRoute 方法来创建路由集合。MapRoute  方法提供了许多重载,并且多数情况上都已够用。但是,这个方法并不会让你配置一个路由对象的各个方面。如果你想在一个路由对象上设置一些内容但是 MapRoute 方法并不提供,你可能就会求助于底下的代码了:

1 // Create a new route and add it to the system collection
2 var route = new Route(...);
3 RouteTable.Routes.Add("NameOfTheRoute", route);

一个路由通常包含一些属性,比如名称,URL 模式,默认值,限制,数据标记,和路由处理器。而最常设置的属性包括名称,URL 模式,和默认值。现在让我们再来回顾一下默认路由:

1 routes.MapRoute(
2     "Default",
3     "{controller}/{action}/{id}",
4     new {
5         controller = "Home",
6         action = "Index",
7         id = UrlParameter.Optional
8     });

第一个参数是路由的名称,每一个路由都应该有一个独有的名称。第二个参数是 URL 模式。第三个参数是一个对象,用来设置默认值的。
注意,一个不完整的 URL 也可能匹配这个路由。让我们看一下根 URL - http://yourserver.com。第一眼看下去,这样的路由应该不会匹配默认路由,但是,如果路由参数有默认值提供,那么这些参数片段是可以为空的。因此,在上面的例子中,当你请求了根 URL,这个请求就会被解析,并调用 Home 控制器的 Index 方法。

处理路由

ASP.NET URL routing module 应用了一系列规则将入站请求 URL 与一个定义好的路由进行匹配。最重要的一个规则就是,程序会按照路由在 global.asax 注册的顺序进行检查匹配。

为了确保路由在正确的顺序下被处理,你必须将他们按最可能的情况到最不可能的情况进行排序。但是不管在什么情况下,搜寻一个匹配路由的过程总是在找到第一个匹配的路由时结束。这就表示,如果在路由列表的最后添加一个新的路由可能并不奏效,或者将会产生一些错误。另外,要清楚在列表顶部放置的 catch-all 模式将会使特定格式的路由被忽略掉。

除了外观上显而易见的排序外,也有其他的因素干扰匹配路由的过程。正如之前提到的,为路由提供的默认值。默认值会被自动赋值到定义好的占位符上,如果当前 URL 没有提供相应的值。考虑以下的两个路由:

{Orders}/{Year}/{Month}

{Orders}/{Year}

在第一个路由上,你为{Year}和{Month}两个占位符设置了默认值,那么第二个路由将永远不会被匹配到,这是由于设置了默认值而导致的。第一个路由将会永远被匹配到不管 URL 是否已经指定了一个年份或者月份。

斜杠也是一个陷阱。{Orders}/{Year}和{Orders}/{Year}/是两个完全不同的路由,其中一个永远不会匹配另一个。

另一个影响路由匹配的因素就是可选属性 - 约束。一个路由约束就强制路由参数必须满足约束要求,不然就无法匹配。URL 不仅需要与路由模式相适应,而且也要包含满足约束要求的数据。一个路由限制可以通过多种方法来定义,包括通过使用正则表达式。这里就有一个约束的例子:

1 routes.MapRoute(
2     "ProductInfo",
3     "{controller}/{productId}/{locale}",
4     new { controller = "Product", action = "Index", locale = "en-us" },
5     new { productId = @"\d{8}", locale = "[a-z]{2}-[a-z]{2}" }
6 );

因此,这个路由的 productId 占位符必须是八位数的数字,而 locale 占位符必须是一对由连字符连接而成的两个字符的字符串。约束并不能保证将所有无效的产品编号和本地代码拦截在门外,但是它至少削减了许多额外的工作。
路由处理器

根据 routing module 决定入站请求 URL 是否被应用所接受,路由仅定义了一小部分规则。而最终决定怎么重新映射请求进来的 URL 的组件就是 route handle。路由处理器就是一个处理所有匹配过的请求的对象。它的唯一目标就是返回真正处理请求的 HTTP handler。

技术上讲,一个路由处理器就是一个实现了 IRouteHandler 接口的类。这个接口定义如下:

1 public interface IRouteHandler
2 {
3     IHttpHandler GetHttpHandler(RequestContext requestContext);
4 }

RequestContext 类定义在 System.Web.Routing 命名空间下,它封装了请求的 HTTP 上下文和一些与路由相关的可用信息,比如 Route 对象本身, URL 参数,和约束。这些数据被封装在一个 RouteData 对象里。底下就是 RequestContext 类的声明:

1 public class RequestContext
2 {
3     public RequestContext(HttpContextBase httpContext, RouteData routeData);
4
5     // Properties
6     public HttpContextBase HttpContext { get; set; }
7     public RouteData RouteData { get; set; }
8 }

ASP.NET MVC 框架并没有提供太多内置的路由处理器,这也就暗示了需要自定义的路由处理器并不常见。但是,如果需要,你可以利用它,过会,我们还会回到自定义路由处理器上并在后面章节上介绍一个例子。

为物理文件处理请求

在路由系统中,另一个可配置的方面并且可能会影响到是否能够成功匹配的方面就是路由系统是否需要处理一个能顾匹配物理文件的路由请求。

默认情况下,ASP.NET 路由系统会忽略那些可以匹配到物理文件的请求。注意,如果这个服务器文件存在,路由系统会忽略当前请求即使这个请求可以匹配一个路由。

如果你需要的话,你可以强制路由系统处理所有请求,只要设置 RouteCollection 对象的 RouteExistingFiles 属性为 true 即可。代码如下:

1 // In global.asax.cs
2 public static void RegisterRoutes(RouteCollection routes)
3 {
4     routes.RouteExistingFiles = true;
5     ...
6 }

注意,在一个 ASP.NET MVC 应用程序中,如果所有请求都通过路由处理可能会产生一些问题。比如,如果你在一个简单的 ASP.NET MVC 应用中的 global.asax.cs 文件中添加之前的代码,并且运行程序,如果你访问 default.aspx 页面,就会立即收到 HTTP 404 错误。
阻止路由定义好的 URL

ASP.NET URL routing module 并没有限制你维护一堆可接受的 URL 模式。你也可以将某些特定的 URL 排除在路由机制之外。你可以通过两部方法来阻止路由系统来处理特定的 URL。第一,你为这些 URL 定义一个模式,并将它保存在一个路由对象中。第二,你将这个路由对象与一个特殊的路由处理器相连,这个路由处理器就是 StopRoutingHandler 类。它所做的就是当它的 GetHttpHandler 方法被调用的时候抛出一个 NotSupported 异常。

比如,底下的代码指导路由系统忽略所有 .axd 请求:

1 // In global.asax.cs
2 public static void RegisterRoutes(RouteCollection routes)
3 {
4     routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
5 }

所有的 IgnoreRoute 方法都与 StopRoutingHandler 路由处理器相关联。
最后,为 URL 中的 {*pathInfo} 占位符做一点解释是有必要的。pathInfo 记号表示任意跟着 .axd 后面的内容。而星号表示最后的参数应该匹配 URL 的剩余部分。换句话说,任何紧跟着 .axd 扩展的内容都将进入 pathInfo 参数中。这个参数也被认为是 catch-all 参数。

属性路由

在 ASP.NET MVC 5 中内置了一个流行的 NuGet 包,就是AttributeRouting(参见 http://attributerouting.net)。属性路由就是通过属性直接在控制器的行为上定义路由。正如前面所说,传统的路由是基于在程序启动时建立在 global.asax 中的协定。

任何时候如果有请求进来,URL 会与注册的路由模板进行匹配。如果一个路由匹配成功,那么处理这个请求的控制器和当中的方法就被确定下来了。如果没有匹配成功,那么请求就会被拒绝,并且会产生一个 404 错误消息。但是现在,在大型应用中,或者在一个具有浓浓 REST 风味的中型项目中,路由的数量可能会非常多,并且这样的路由记录可能就有上百条。为此,属性路由应运而生,并且现在已经集成到 ASP.NET MVC 5 中,甚至在 Web API 中。我们将会在后面详细讨论。

1 [HttpGet("orders/{orderId}/show")]
2 public ActionResult GetOrderById(int orderId)
3 {
4     ...
5 }

这段代码设置了 GetOrderById 方法仅可以通过 HTTP GET 请求,并且 URL 模板必须匹配所指定的模式的时候才可以被访问。路由参数 - orderId 指令 - 必须与定义在方法签名中的参数所匹配。这里也有更多的属性可用。但是属性路由的主旨全都在这里。想要获取更多的信息(比如,配置)你可以参阅 http://attributerouting.net ,因为 ASP.NET MVC 中集成的路由属性就是直接来源于现有的 NuGet 包。

【声明:本文是个人翻译而来。当中可能会存在许多不当之处,万望指出,谢谢。文章会持续更新】

时间: 2024-10-10 17:54:31

控制器 - 应用路由(二)的相关文章

ASP.Net MVC开发基础学习笔记(3):Razor视图引擎、控制器与路由机制学习

首页 头条 文章 频道                         设计频道 Web前端 Python开发 Java技术 Android应用 iOS应用 资源 小组 相亲 频道 首页 头条 文章 小组 相亲 资源 设计 前端 Python Java 安卓 iOS 登录 注册 首页 最新文章 经典回顾 开发 Web前端 Python Android iOS Java C/C++ PHP .NET Ruby Go 设计 UI设计 网页设计 交互设计 用户体验 设计教程 设计职场 极客 IT技术

ASP.Net MVC开发基础学习笔记:三、Razor视图引擎、控制器与路由机制学习

一.天降神器“剃须刀” — Razor视图引擎 1.1 千呼万唤始出来的MVC3.0 在MVC3.0版本的时候,微软终于引入了第二种模板引擎:Razor.在这之前,我们一直在使用WebForm时代沿留下来的ASPX引擎或者第三方的NVelocity模板引擎. Razor在减少代码冗余.增强代码可读性和Visual Studio智能感知方面,都有着突出的优势.Razor一经推出就深受广大ASP.Net开发者的喜爱. 1.2 Razor的语法 (1)Razor文件类型:Razor支持两种文件类型,分

ASP.NET Web API 控制器创建过程(二)

ASP.NET Web API 控制器创建过程(二) 前言 本来这篇随笔应该是在上周就该写出来公布的,因为身体跟不上节奏感冒发烧有心无力,这样的天气感冒发烧生不如死,也真正的体会到了什么叫病来如山倒,病去如抽丝.这两天状态才好了一点,让我理解了什么才是革命的本钱,希望大家也多保重身体. 好了,还是回归主题,对于上一篇的内容解说的仅仅是ASP.NET Web API控制器创建过程中的一个局部知识,在接着上篇内容解说的之前,我会先回想一下上篇的内容,而且在本篇里进行整合,让我们要看到的是一个整个的创

ASP.NETWeb API 控制器创建过程(二)

ASP.NET Web API 控制器创建过程(二) 前言 本来这篇随笔应该是在上周就该写出来发布的,由于身体跟不上节奏感冒发烧有心无力,这种天气感冒发烧生不如死,也真正的体会到了什么叫病来如山倒,病去如抽丝.这两天状态才好了一点,让我理解了什么才是革命的本钱,希望大家也多保重身体. 好了,还是回归主题,对于上一篇的内容讲解的只是ASP.NET Web API控制器创建过程中的一个局部知识,在接着上篇内容讲解的之前,我会先回顾一下上篇的内容,并且在本篇里进行整合,让我们要看到的是一个整个的创建过

ASP.NET MVC路由(二)

 ASP.NET MVC路由(二) 前言 在上一篇中,提及了Route.RouteCollection对象的一些信息,以及它们的结构所对应的关系.按照处理流程走下来还有遗留的疑问没有解决这个篇幅就来讲解一下. URL规则的生成 Url规则看名字挺吓唬人的,其实就是根据我们自定义的Url来解析出一个模式,然后等待请求的Url来的时候,跟我们定义的模式进行匹配(如下图).这是后续的内容. 在上篇中说到URL规则的定义是在Route对象中的,下面来讲解在Route对象中怎么根据用户注册的URL转变成U

[.net 面向对象程序设计深入](6).NET MVC 6 —— 模型、视图、控制器、路由等的基本操作

[.net 面向对象程序设计深入](6).NET MVC 6 —— 模型.视图.控制器.路由等的基本操作 1. 使用Visual Studio 2015创建Web App (1)文件>新建>项目,选择Web>ASP.NET Web 应用程序 (2)在新项目MyFirstWebApp对话框中,选择ASP.NET 5模板>Web Application 由于是RC版,这里的”添加单元测试“暂时不能选,上面的WebForms MVC WebAPI将合并,前面一节介绍过了,因此也不需要再选

MVC 多级目录(控制器) 路由重写 及 多级Views目录 的寻找视图的规则

转自:[原]Asp.net Mvc   多级控制器 路由重写 及 多级Views目录 的寻找视图的规则 asp.net mvc 为了更好的控制views的页面存放,和控制器的可读性,需要分开多级目录来存放. 1.那么我们再来看我们需要的访问方式,如下图 如果我们要访问Admin下的TestController里面的Index页面,那么我们输入Test/Index,这个肯定不行的.因为TestController根本就不在Controllers的根目录下,而是在Controllers/Admin下

控制器和路由

1.控制器以Controller做为后缀,继承自 yii\web\Controller 2.动作以action为前缀,public访问修饰 3.控制器ID去类名后缀,全部转为小写,如果是驼峰命名多个单词,转为中杠分隔 4.动作ID去前缀,全部转为小写,如果是驼峰命名多个单词,转为中杠分隔. 5.路由如何指向动作 控制器ID.动作ID 6.URL访问规则index.php?r=路由   传参数使用&参数=值的方式 7.默认动作ID index定义在 yii\base\Controller::$de

大数据:数据分片和数据路由(二)

分布式存储中常见的一项技术就是 :分布式哈希表.它是哈希表的分布式的扩展,就是在多台机器的情况下,每个机器只存储一些数据,如何通过 哈希方式 对 数据 进行增,删,改,查等一些数据操作.    一致性哈希算法就是其中的一种实现方式. 上图是表示长度为5的二进制数值的 一致性哈希算法 的环状序列 的示意图 (m=5),所以这个哈希数值空间可以表达的值是从 0~31. 每个机器可以通过 Ip和端口号 经过 哈希函数 映射到 哈希数值空间内. 所以上面的每个 大圆 均表示了 一个机器节点. N (x)