对于从ASP.NET WebForms背景转向MVC的开发者来说,理解请求是如何映射到控制器的动作方法,最终在屏幕上渲染视图,可能会感到有些困难。在WebForms中,请求实际上是映射到应用程序目录中实际存在的页面上。但在ASP.NET MVC中,每个请求都需要映射到一个控制器。ASP.NET MVC中没有物理ASPX文件的概念。
在ASP.NET MVC中,有控制器。每个控制器将有多个动作,每个动作都应该处理一个用户请求。用户请求的URL是如何映射到控制器的动作方法的呢?答案是ASP.NET MVC中的路由机制。路由是用户请求的URL和控制器的动作方法之间的桥梁。路由负责映射请求URL到控制器和动作方法的映射策略。
这并不意味着必须手动将所有URL映射到控制器和动作。ASP.NET MVC提供了一种通过指定路由来自动化路由的机制,这些路由是一些提示/模式。这些提示/模式将让路由模块理解请求URL和相应的控制器和动作来调用。
为了让路由引擎更好地理解,可以在路由中指定以下内容:
patterns:
这有助于解析器理解如何分解请求URL并将它们映射到特定的控制器和动作。
route parameters:
这让引擎理解传入路由的参数,并将它们映射到动作方法的参数。
route constraints:
这让解析器知道传递的参数类型(字符串/整数)。
ignore rules:
这让解析器知道应该忽略哪些路由或路由模式。
为了更好地理解上述概念,让创建一个简单的空MVC 4 Web应用程序。将看看如何为这个应用程序指定各种路由。
所有路由信息都存储在一个名为
RouteCollection
的集合中。如果想为应用程序指定路由模式,需要在应用程序启动时将这些模式添加到这个集合中。MVC 4项目模板已经在
RouteCollection
中添加了一个默认路由。这个默认路由存在于
RouteConfig.cs
文件的
RegisterRoutes
方法中。这个方法是从
global.asax
文件的
Application_Start
生命周期事件中调用的。让看看这个默认路由,并尝试理解模式。
C#
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
第一个属性是路由的名称。添加到路由集合中的每个路由都应该有一个唯一的名称,以便它可以被唯一识别。第二个属性是URL,它指定了请求URL的模式
{controller}/{action}/{id}
。所以如果用户请求一个URL,如:
www.example.com/Customer/Edit/1
,这个URL将调用Customer控制器的Edit动作方法,参数id为1。第三个属性指定了控制器、动作和id参数的默认值。即如果用户请求
www.example.com
,由于URL中缺少控制器、动作和id部分,路由引擎将采用默认值,并调用Home控制器的Index动作,没有id参数(因为它被指定为可选)。
现在脑海中出现的第一个问题是 - 如果需要向动作方法提供2个参数怎么办。这相当容易做到。只需要在路由中指定多个参数值:
C#
routes.MapRoute(
name: "multipleparams",
url: "{controller}/{action}/{param1}/{param2}",
defaults: new { controller = "Home", action = "Action2", param1 = UrlParameter.Optional, param2 = UrlParameter.Optional });
以类似的方式,可以在路由模式中指定任意数量的路由参数。同时,可以选择使这些参数中的任何一个为可选。
路由约束主要用于限制与特定路由匹配的请求URL,这可以用来限制对应用程序无效的路由。路由约束以正则表达式的形式指定。让通过定义一个简单的动作方法来编辑一个实体来理解问题。让为它定义一个动作方法。
C#
public class HomeController : Controller
{
public ActionResult Edit(int id)
{
ViewBag.Message = "Home/Edit/" + id;
return View();
}
}
现在这个动作方法期望传递一个整数值。这个动作方法有一个视图,它简单地显示路由。所以让用以下URL调用这个动作方法:
Home/Edit/1
。结果将是:
发生的事情是,默认路由足以使路由引擎解析请求的URL并调用Edit动作。现在让尝试传递一个无效的整数值在路由中,即
Home/Edit/test
。现在的结果是:
发生这种情况的原因是,非整数值对于这个动作方法来说不是有效的。因此,对于应用程序来说,这是一个无效的路径,为了将这个路径标记为无效路径,可以定义一个带有路由约束的自定义路由。让尝试定义这个带有路由约束的路由。
C#
routes.MapRoute(
name: "home_Edit",
url: "Home/Edit/{id}",
defaults: new { controller = "Home", action = "Edit" },
constraints: new { id = @"\d+" });
这个路由现在只接受这个路由的有效数字值。现在如果再次尝试访问同一个URL,浏览器将清楚地表明这是一个无效的URL。
忽略一些路由
在某些场景中,也希望完全忽略一些路由。如果查看
RouteConfig
类,可以看到类似这样的代码:
C#
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
这样做是限制访问所有以
.axd
扩展名结尾的URL,因为不希望最终用户能够访问Trace.axd或其他资源axd文件。
在结束这个小提示之前,有一件重要的事情要记住。将路由插入到
RouteCollection
中的顺序非常重要。路由模块即路由处理器将从上到下开始查找这个映射。所以如果需要将一些特定的路由与通用路由一起放置,应该将特定的路由放在通用路由之前。