65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET MVC 的“为什么”和“怎么做”:第三部分

2013年12月6日

CPOL

6分钟阅读

viewsIcon

36874

ASP.NET MVC 控制器和路由的基本理念、优点以及控制器验证。

引言

承接我们上一篇关于 第二部分 的讨论,希望现在大家对 Model 已经有了一定的了解。现在我们可以转向下一个主题,即 Controllers(控制器)和我的个人最爱——Routing(路由)。我喜欢将 Controllers 和 Routing 放在同一篇文章中介绍,因为我认为这会更方便读者。和往常一样,我喜欢跳过“怎么做”,而是专注于“为什么”。

为什么需要控制器 (Why Controllers)

控制器的作用是避免请求混乱。没有控制器,事情会变得一团糟,这不仅适用于 MVC,在现实生活中也是如此。现在想想一个现实生活中的例子。在机场,有数百架飞机使用跑道,但机场会为每架飞机提供独立的跑道吗?不会,飞机共享同一条跑道。想想如果没有对哪架飞机何时使用跑道进行控制,情况就会像下图一样混乱。

为了避免这种混乱,机场需要 ATC(空中交通管制)。正如维基百科所说:

ATC 的主要目的是防止碰撞、组织和加快交通流量,并为飞行员提供信息和其他支持。

与 ATC 类似,MVC 中也有一个 Controller(控制器)。控制器控制应用程序逻辑,并充当视图和模型之间的协调者。

这个过程非常简单。当用户的请求到达 MVC 框架时,它首先会进入控制器。此时,控制器的职责是确定应该调用哪个模型,然后调用该模型中的相应函数。调用函数后,它会捕获结果。

现在的问题可能是,这是 MVC 中才引入的新概念吗?答案是否定的。如果您阅读了第一篇 文章,我们非常熟悉的 Web Forms 也使用了类似的概念。Web Forms 使用 Page Controller 模式来处理定向到特定网页的所有 HTTP 请求。MVC 在这方面超越了传统的 Web Forms,它将控制流结构化为控制器,使应用程序逻辑更简单、更易于测试。

当我们讨论控制器处理用户请求时,路由(Routing)就发挥作用了。路由在 MVC 应用程序中预先配置,用于调用控制器中定义的动作方法。

路由和控制器 (Routing and Controller)

您可能见过旅行者用来识别、导航和组织县/州内路线的高速公路标志或路线标记。

就像高速公路标志指引旅行者一样,ASP.NET 路由将传入的请求导向相应的控制器动作。

顾名思义,路由是一个处理器,实际上是一个处理请求的类,例如 MVC 应用程序中的控制器。ASP.NET 路由模块负责将传入的浏览器请求映射到特定的 MVC 控制器动作。

路由使用 URL 模式来识别正确的控制器和动作。MVC 应用程序中路由的典型 URL 模式是:domain/{controller}/{action}/{id}

当收到请求时,它会被路由到 UrlRoutingModule --> MvcHandler。MvcHandler HTTP 处理程序确定要调用哪个 {controller}。URL 中的 action 值决定调用哪个 {action} 方法,如果指定了 {id},则会带上 ID 参数。

如何选择控制器 (How to Choose Controllers)

通常,ASP.NET MVC 控制器动作在动作完成时会阻塞 ASP.NET 线程池。这称为同步编程模型。但在某些情况下,我们希望动作不会阻塞线程池中的线程。对于这些场景,我们可以使用 .NET Framework 4.5 中引入的异步控制器。

使用异步控制器的好处如下:

  1. 对于 I/O、网络绑定或长时间运行且可并行化的请求,编写异步方法将提高应用程序的响应能力
  2. 用户比同步请求更容易取消。

现在您可能会想,这是什么鬼东西?什么时候使用同步控制器,什么时候使用异步控制器?

别担心,您不必盲目选择使用异步还是同步。适合同步动作的场景:

  1. 简单且运行时间短的操作。
  2. 简单比效率更重要的场景。
  3. CPU 密集型操作。
  4. 当动作必须执行多个独立的操作时。

适合异步动作的场景:

  1. 长时间运行的操作导致性能瓶颈。
  2. 网络或 I/O 密集型操作。
  3. 当应用程序需要用户能够取消长时间运行的操作时。
  4. 当 IIS 需要通过异步方法处理更多请求时。

请注意:如果控制器动作是 CPU 绑定的,使其异步调用不会带来任何好处,反而可能为这些操作增加开销。因为异步控制器不会改变请求的执行时间,它只是释放执行请求的线程,使其可以重新分配到 ASP.NET 线程池中。

如何定义控制器 (How to Define Controllers)

物理上,控制器只是另一个类,它必须以“Controller”后缀结尾,并且可以暴露控制器动作。当用户在浏览器中输入 URL 时,MVC 应用程序会使用路由规则解析 URL 并确定控制器的路径。任何被定义为公共的方法都被认为是动作方法。控制器动作还需要满足一些额外的要求。

  1. 用作控制器动作的方法不能被重载。
  2. 控制器动作不能是静态方法。

如何定义自定义路由 (How to Define Custom Routing)

任何派生自 ControllerBase 类且名称以“Controller”结尾的类,都不需要任何自定义路由声明。但您也可以定义自己的路由。自定义路由约束使您能够在满足某个自定义条件时才匹配路由。如果您想在 MVC 应用程序中添加自定义路由,可以在 Global.ascx 中使用 MapRoute(RouteCollection, String, String) 方法。

定义路由时,您可以为参数分配默认路由值。如果 URL 中不存在该参数的值,则会使用默认值。

测试控制器 (Testing Controllers)

如果我们不测试控制器的行为,使用 MVC 的初衷就无法实现。ASP.NET MVC 控制器不过是简单的类,控制器动作不过是简单的方法。因此,这种方法使编写测试用例变得容易得多。

我们可以对相应的控制器进行两种类型的测试:

  1. 控制器动作是否返回了正确的动作结果。
  2. 是否为视图结果返回了正确的视图。

作为示例,让我们为HomeController 编写一个单元测试。这是我们自己的默认 HomeController:

为了测试 HomeController 控制器的动作,我们所需要做的就是使用默认构造函数创建一个新实例,并像下面这样调用该动作。

上面展示的测试可能无法满足或涵盖控制器单元测试的每一个方面,但作为开始已经不错了。高级读者可以查看下一节中的参考资料。我希望能够写一篇完整的文章来介绍控制器的各种测试用例。

参考和好读

© . All rights reserved.