概述

本节学习如何创建控制器(Controller)和它们的操作(Action)。

本节主要内容:

  • Controller 和 Action 的基本概念

  • Controller 的设计

  • Action 的设计

什么是 Controller 和 Action

Controller 是定义了一组 Action 的类。Action 是 Controller 内用于处理 HTTP 请求的方法。

HTTP 请求通过 URL 路由映射到 Controller 的具体 Action,路由的细节在下一节讨论。

Action 的返回结果一般分两种:

  1. 启动视图的渲染,并返回渲染结果

  2. 调用其它 Action,这也称为重定向

Controller 的设计

为了设计好控制器,要遵循一些约束和规范。

约束是必需遵守的;规范是大家普遍遵守的,为了写出更容易合作的代码。

约束:将类标记为 Controller

类至少要满足以下任意一个约束,才能被 ASP.NET Core 框架识别为 Controller:

  • 类名以 Controller 为后缀

  • 类继承自名为 Contoller 或以 Controller 为后缀的类

  • 类使用 [Controller] 特性标注

下例的类都算 Controller:

  1. using Microsoft.AspNetCore.Mvc;
  2. namespace App.Controllers
  3. {
  4. // Controller 1: the class name is suffixed with "Controller"
  5. public class ProductController
  6. {
  7. // Actions
  8. }
  9. // Controller 2: the class inherits from a class whose name is or is suffixed with "Controller"
  10. public class Product : Controller
  11. {
  12. // Actions
  13. }
  14. // Controller 3: the class is decorated with the [Controller] attribute
  15. [Controller]
  16. public class Product
  17. {
  18. // Actions
  19. }
  20. // Controller 4: preferred name
  21. public class ProductController : Controller
  22. {
  23. // Actions
  24. }
  25. }

实际项目中最常见的是第四种,即继承自 Controller 又以 Controller 为名称后缀。这样做的好处是将 Product 的控制器类与模型类区分开来。

规范:设计 Controller

我们应遵循以下规范,以便使 Controller 适用于真实项目:

  • 将所有 Controller 类放在项目根目录下的 Controllers 文件夹中。这使得控制器类都在 Controllers 名称空间下,大幅降低维护成本

  • 使控制器类继承自 Microsoft.AspNetCore.Mvc.Controller。这允许你重用 Controller 基类实现的属性和方法。例如,调用 View 渲染视图,调用 RedirectToAction 进行重定向

  • ASP.NET Core Web 程序的控制器命名都使用单数名词

    • 究其原因是 ASP.NET Core Web API 程序的控制器命名会使用复数名词。如此规范有助于我们区分不同功能的控制器类,尤其是当我们在同一个项目中既有 Web 程序又有 Web API 程序时

请参考课程 Build Web APIs using ASP.NET 了解如何使用 ASP.NET Core 创建 Web API 程序。

Action 的设计

Controller 中的 public 方法,除了用 [NonAction] 标注的都算是 Action

Action 主要职责就是处理业务逻辑,然后返回渲染的视图(HTML 内容)或重定向,没有太多的设计规范,下面以按产品名搜索产品为例。

搜索到该名称产品就筛选并渲染出来,搜索不到就依然展示所有产品。

  1. public class ProductController : Controller
  2. {
  3. public DatabaseContext dbContext { get; set; }
  4. public IActionResult GetProductsByName(string name)
  5. {
  6. IList<Product> products = null;
  7. if(String.IsNullOrWhiteSpace(name))
  8. {
  9. products = dbContext.GetAllProducts();
  10. }
  11. else
  12. {
  13. products = dbContext.GetProductsByName(name);
  14. }
  15. return RedirectToAction("Index", products);
  16. }
  17. public IActionResult Index(IList<Product> products)
  18. {
  19. return View(products);
  20. }
  21. }

Action 的返回值

理论上 Action 方法可以返回任意返回值:

  1. public class SampleController
  2. {
  3. public string SayHello()
  4. {
  5. return "Hello, ASP.NET Core!";
  6. }
  7. public double Add(double a, double b)
  8. {
  9. return a + b;
  10. }
  11. public IActionResult CylinderVolume(double r, double h)
  12. {
  13. double v = Math.PI * Math.Pow(r, 2) * h;
  14. return new JsonResult(v);
  15. }
  16. }

假设包含该 SampleController 的 Web 程序运行在 TCP 5000 端口上监听 HTTP请求。

  • 如果在浏览器的地址栏中键入 http://localhost:5000/sample/sayhello 并按 Enter 键,浏览器发送的 HTTP 请求将触发 SayHello 操作。SayHello 将返回纯文本字符串 “Hello, ASP.NET Core!”。字符串将通过 HTTP 响应返回到你的浏览器,并显示在浏览器中

  • URL http://localhost:5000/sample/add?a=10&b=20 将触发 Add 操作,你的浏览器将显示 30.0。但这个 30.0 既不是字符串也不是 double 值。它是一个 JSON 对象。也就是说,默认情况下,ASP.NET Core 会将非 IActionResult 类型的返回值转换为 JSON 对象

  • 同理,URL http://localhost:5000/sample/cylindervolume?r=10&h=20 将触发 CylinderVolume 操作,浏览器将显示 6283.1853071795867。该值也是 JSON 对象。不同之处在于它是通过 JsonResult 显式转换而来的

拥有隐式/显式将返回值转换为 JSON 对象的功能意味着 ASP.NET Core Web 程序也可以用作 Web API 程序。但这样做徒增复杂度,项目中还是应该分开它们。

所以除了 JsonResult 外,常见的 ASP.NET Core Web 程序的 Action 基本都返回 IActionResult 类型值。

作为 C# 程序员,你可能会问:“IActionResult 是个接口,我们是否需要记住它的实现类然后创建要返回的操作的实例?”答案是不必。因为大多数 Action 最终只做两件事 —— 要么渲染视图,要么重定向到另一个 Action,而这两操作在 Microsoft.AspNetCore.Mvc.Controller 中已经实现了,分别是 View 和 RedirectToAction。

总之,Action 方法的最后一句基本都是 return View(/*...*/)return RedirectToAction(/*...*/)