以 MVC 风格学习 ASP.Net:第 2 部分 - ASP.Net MVC 5 中的控制器






4.77/5 (23投票s)
关于 ASP.NET MVC 5 中控制器 (Controller) 的详细描述。
目录
本文将涵盖以下主题
路线图
这是“以 MVC 风格学习 ASP.NET”系列文章的第二篇,讨论 ASP.NET MVC5 中的控制器。以下是该系列迄今为止的路线图
引言
在我第一篇文章中,我向您概述了 ASP.NET MVC5 框架,并将其描述为微软对模型-视图-控制器 (MVC) 模式的实现。MVC 模式是一种架构模式,它基于“关注点分离”原则将应用程序划分为三个相互关联的部分。这三个部分称为模型 (Model)、视图 (View) 和控制器 (Controller)。本文侧重于该模式的控制器部分,并为您提供有关 ASP.NET MVC5 中控制器的作用和重要性的详细描述。
入门
控制器是 ASP.NET MVC5 框架中最重要的部分,负责 Web 应用程序的整体流程。如果我们看看 ASP.NET MVC 应用程序的请求/响应模式,它与传统的 Web Forms 应用程序略有不同,在后者中,请求直接映射到放置在 Web 服务器上的 (.aspx)
文件。
为了简化说明,请考虑一个名为“Developer Cafe”的 Web 应用程序,该应用程序已使用 ASP.NET Web Forms 开发并部署在 Web 服务器上。如果用户想访问应用程序的联系页面,他应该键入 URL,如 [www.developercafe.com/contact.aspx]
。'contact.aspx'
指的是位于 Web 服务器根目录的网页,其中包含有关该应用程序的联系信息。这是使用 ASP.NET Web Forms 开发的 Web 应用程序的传统方法。
如果我们使用 ASP.NET MVC5 框架开发相同的应用程序,其请求/响应模式将完全不同。它不直接指向 Web 服务器根目录中的文件,而是将 URL 映射到称为控制器的特殊类。这些类包含称为“操作方法”或简称为“操作”的目标方法,它们负责处理传入的请求。因此,如果同一用户要访问在 ASP.NET MVC5 中开发的“Developer Cafe”应用程序的联系信息,他应该键入类似这样的内容:[www.developercafe.com/Contact/Index]
在这里,'Contact
' 指的是处理用户请求的控制器类,而 'Index
' 指的是控制器类中包含应用程序联系信息的动作方法。这种 URL 请求到控制器类的映射是通过 ASP.NET MVC5 框架的路由机制完成的。这种 URL 方法的主要好处是更好的搜索引擎优化 (SEO)。本文不讨论路由机制,而是专注于控制器类的结构。
ASP.NET MVC5 中的控制器
控制器 (Controller)
控制器是一个特殊的类,它继承自抽象的 ControllerBase 类,并负责 ASP.NET MVC 应用程序的整体流程。发送到 ASP.NET MVC5 应用程序的每个请求都由控制器处理。它处理 HTTP 请求,检索和存储数据,返回适当的响应并执行应用程序逻辑。
操作方法 (Action Methods)
控制器提供公共方法来响应对 ASP.NET MVC 应用程序发出的传入 HTTP 请求。这些方法称为操作方法 (Action Methods),通常返回 ActionResult 对象。ActionResult 是一个抽象类,这些操作方法可以向传入请求返回各种类型的对象,这些对象都继承自 ActionResult 类。结果可以是简单的文件下载、纯文本、要处理的 JSON 或 HTML 页面,具体取决于用户的请求。
操作结果 (Action Results)
操作方法可以返回的各种操作结果类型在下表中进行了描述。
如何创建控制器
步骤 1: 为了深入了解控制器,让我们创建一个空的 ASP.NET MVC5 应用程序。打开 Visual Studio,然后通过转到 文件 => 新建 => 项目
来创建一个新项目
将出现一个对话框窗口,要求您选择项目类型。在对话框窗口的左侧单击“Web”,选择 ASP.NET Web 应用程序。给它一个合适的名称,然后单击“确定”。
步骤 2: 一个新的对话框窗口会要求您选择 Web 应用程序的模板。由于我们正在使用 ASP.NET MVC 框架创建一个 Web 应用程序,并且必须从头开始学习所有内容,因此请选择“Empty”模板。此外,为了获得应用程序的基本必要文件,请选中 MVC 复选框,然后单击“确定”。
步骤 3: Visual Studio 为我们创建了一个具有 Models、Views 和 Controllers 等目录以及 Web.config
和 Global.asax
文件的最小布局项目。
这里有趣的一点是创建的 App_Start 文件夹,其中包含一个 RouteConfig.cs
文件。此文件包含整个应用程序的路由配置。它定义了当传入请求到达服务器时要查找的类和方法。我将在该系列未来的文章中详细解释 ASP.NET MVC 应用程序的路由机制。现在需要记住的一点是,该文件包含应用程序执行请求/响应模式所需的路由配置。
步骤 4: ASP.NET MVC5 框架在“约定优于配置”原则上投入了大量精力。默认情况下,每个控制器类都应以“Controller”关键字作为后缀,并放置在 ASP.NET MVC 应用程序的 Controllers 文件夹中。我们将在本文中遵循相同的原则。
要创建控制器,请右键单击 Controllers 文件夹,然后选择 Add,然后选择 Controller。
步骤 5: 将出现一个对话框窗口,其中包含多个选项可供选择。选择“MVC 5 Controller - Empty”选项,然后单击 Add。
步骤 6: 由于我之前已经告诉过您“约定优于配置”原则,因此它会为您提供一个命名控制器的对话框窗口。您应该注意,Controller 关键字应附加在名称之后。
最初,我提到了 RouteConfig.cs
文件。ASP.NET MVC 框架使用此类将传入的用户请求路由到控制器。它有一个 ASP.NET MVC 应用程序遵循的默认路由配置。如果我们打开 RouteConfig.cs
文件,您会注意到一个静态方法 RegisterRoutes()
,其中包含路由信息。
public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } }
它包含 routes.IgnoreRoute
和 routes.MapRoute
两个属性。如果这让您感到害怕,请不要担心。您现在不需要理解它。现在,只需记住 routes.MapRoute
属性包含应用程序所需的路由信息。
routes.MapRoute 的三个参数描述如下。
- name: 它描述了路由的名称
- url: 它描述了用户应在浏览器中键入的请求 URL 的语法。
- defaults: 它包含传入 URL 的默认配置。默认值为:
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional
}
这意味着将映射到所有传入请求的默认控制器是 **Home**,控制器类中要指向的默认操作方法应该是 **Index**,并且参数在 URL 中是可选的。
在查看 ASP.NET MVC 应用程序的默认路由配置后,我们应该将控制器命名为 **HomeController**。但是,这只是默认配置,可以根据应用程序的需求进行更改。但为了简单起见,我们将坚持默认设置,并输入以 Controller 关键字结尾的 Home 名称,然后单击 Add。
步骤 7: 瞧,**HomeController** 已在 Controllers 文件夹中创建。值得注意的一点是,在 Views 文件夹中创建了一个与控制器同名的文件夹。这是因为控制器负责响应传入的请求,通常用户希望将 HTML 页面呈现到浏览器。为了向用户响应 HTML 页面,我们使用视图。
视图只不过是一个作为响应返回给用户的 HTML 文件,并且根据“约定优于配置”原则,对于每个控制器,都应在 Views 文件夹中创建一个同名的视图文件夹。
步骤 8: 打开 HomeController.cs
文件,准备开始使用它:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace ControllerDemo.Controllers { public class HomeController : Controller { // GET: Home public ActionResult Index() { return View(); } } }
使用控制器
到目前为止,我们已经了解了什么是控制器以及如何创建一个。让我们看看控制器类内部,并使用控制器提供的响应传入请求的方法。
public class HomeController : Controller { public ActionResult Index() { return View(); } }
当我们创建一个控制器类时,它继承了 System.Web.Mvc.Controller
类,并为我们提供了一个名为“Index”的公共方法。我提到了“public
”这个词,因为在 C# 中,默认情况下类中的所有方法都是私有的。因此,如果我们必须在控制器中手动创建一个方法以将其视为操作方法,则应将其声明为 public。
操作结果的类型
控制器类中的公共方法称为操作方法 (Action Methods)。默认情况下,这些方法向用户的传入请求返回 ActionResult 对象。ActionResult 是一个抽象类,该类的实例代表操作方法响应用户请求的结果。操作方法可以返回几种类型的结果。以下描述了一些常见结果
1. ViewResult: 这是返回给用户最常见也是默认的结果。它从 Views 文件夹向用户呈现一个网页。如果我们查看 HomeController 类,它会调用辅助方法 View()
,该方法查找要渲染到浏览器的关联的“Index”视图。
public class HomeController : Controller { public ActionResult Index() { return View(); } }
如果执行代码,将显示以下输出
出现此错误是因为我们没有创建任何名为 Index 的视图以在浏览器中呈现,但您对用于向用户返回视图的 ViewResult 类有了概念。
2. ContentResult: 用于向用户返回用户定义的 MIME 类型(文本、XML)。我们可以使用 Content()
方法向用户返回一个简单的字符串。考虑以下代码
public class HomeController : Controller { public ActionResult Index() { return Content("This method returns plain text."); } }
如果执行代码,将显示以下输出。
3. JsonResult: 我们可以使用此类向用户返回 JavaScript Object Notation (JSON) 对象。我们需要使用 Json()
辅助方法来返回 JSON。考虑以下代码
public class HomeController : Controller { public ActionResult Index() { var employee = new { Name = "Sahil Sharma", Age = "25", Occupation = "Software Developer" }; return Json(employee,JsonRequestBehavior.AllowGet); } }
在这里,我们需要为 Json()
方法提供一个额外的参数 'JsonRequestBehaviour.AllowGet'
,以允许它在 GET 请求上工作。
4. FileResult: 我们还可以为用户提供一种设施,让他们可以从应用程序请求下载文件。为此,我们需要使用 File()
方法返回 FileResult 对象。考虑以下代码:
public class HomeController : Controller { public ActionResult Index() { return File(Server.MapPath("~/App_Data/documentation.pdf"), contentType: "application/pdf", fileDownloadName: "documentation.pdf"); } }
如果我们运行以下代码,它将为下载到我们本地系统的 PDF 文件提供一个选项。
5. RedirectToAction: 此类用于重定向到控制器类的另一个操作方法。考虑以下代码:
public class HomeController : Controller { public ActionResult Index() { return RedirectToAction("Contact"); } public ActionResult Contact() { return Content("This is the redirected Contact method."); } }
在这里,我使用 Contact()
操作方法作为当请求到达 Index()
方法时要显示的重定向方法。
有用的特性 (Attributes)
有几个属性可以应用于控制器的操作方法,以进行各种调整。这些被称为操作选择器。正如 MSDN 所说,“操作选择器表示用于影响操作方法选择的属性”。以下描述了一些常用的选择器。
1. ActionName: 此属性允许我们为操作方法指定一个与其实际名称不同的名称。考虑以下代码:
public class HomeController : Controller { [ActionName("Contact")] public ActionResult Index() { return Content("This method is originally named Index"); } }
如果我们执行以下代码并查找 Index 操作方法,则会得到一个 HTTP 404
错误。
这是因为该方法的名称现已更改为 Contact,我们应该查找 Home 控制器的 Contact 方法。
2. NonAction: 假设我们希望将控制器类的一个方法视为普通方法而不是操作方法。我们该怎么做?我们在文章开头就了解到,只有公共方法才被视为操作方法。因此,我们可以通过将其声明为 private 或 protected 来将操作方法转换为普通方法。
但是也可能存在其他情况。如果存在一个需求,要求控制器类的公共方法不被视为操作方法。我们也可以做到吗?答案是肯定的。有一个名为 NonAction
的属性,它指示公共方法不应被视为操作方法。考虑以下代码:
public class HomeController : Controller { [NonAction] public ActionResult Index() { return View(); } }
如果我们执行以下代码,我们将收到一个 HTTP 404
错误,因为 Index 方法不再是操作方法,并且无法调用。
3. HttpGet: 此属性用于限制操作方法仅响应 HTTP GET 请求。它也是操作方法的默认请求属性。考虑以下代码:
public class HomeController : Controller { [HttpGet] public ActionResult Index() { return View(); } }
如果我们为操作方法指定 HttpGet 属性,它仅适用于 GET 请求,否则将返回 HTTP 404
错误。
4. **HttpPost:** 此属性与 HttpGet 属性类似,唯一的区别是它响应 **POST** 请求,而不是响应 GET 请求。考虑以下代码:
public class HomeController : Controller { [HttpPost] public ActionResult Index() { return View(); } }
关于操作方法的一个微小问题?
在 MVC 面试中有一个关于操作方法的有趣问题。我们在本文中学到,控制器是一个类,一个类可以有多个方法。根据 OOP 原则,一个类可以有两个同名的方法。这个原则被称为函数重载,它是**静态**或**编译时多态**的一个例子。
如果我们在这里遵循相同的原则,那么结论是我们可以有一个控制器中有两个同名的操作方法。让我们看下面的代码
public class HomeController : Controller { //First Index Method public ActionResult Index() { return Content("This is the first Index action"); } //Second Index Method public ActionResult Index(string something) { return Content("This is the second Index action"); } }
如果我们执行代码,它会显示以下内容
哇,它抛出了一个 System.Reflection.AmbiguousMatchException
而不是执行代码。错误消息是:“控制器类型“HomeController”上的操作“Index”的当前请求含糊不清”。
那么,我们如何解决这个错误呢?有两种方法可以解决这种情况,并在不出现异常的情况下使用两个同名的操作方法。
1. 使用 ActionName 属性: 为了在控制器中拥有两个同名的操作方法,我们将使用 ActionName 属性。考虑以下代码:
public class HomeController : Controller { //First Index Method public ActionResult Index() { return Content("This is the first Index action"); } //Second Index Method [ActionName("IndexNew")] public ActionResult Index(string something) { return Content("This is the second Index action"); } }
在这里,我为第二个操作方法提供了一个备用名称 IndexNew
,以区别于第一个。通过这种方式,我们可以在控制器中拥有两个同名的操作方法。
2. 使用 HTTP 请求属性: 这种方法利用 HttpGet 和 HttpPost 属性在控制器中拥有两个同名的操作方法。考虑以下代码:
public class HomeController : Controller { //First Index Method [HttpGet] public ActionResult Index() { return Content("This is the first Index action called using GET request."); } //Second Index Method [HttpPost] public ActionResult Index(string something) { return Content("This is the second Index action called using POST method."); } }
如果我们执行以下代码,将不会出现任何错误,代码也将正常工作。当应用程序收到一个 GET 请求时,它会自动由第一个方法处理;对于 POST 请求,第二个方法会自动调用。
关注点
在本文中,我们了解了控制器、它们的作用以及它们在 ASP.NET MVC 应用程序中的工作方式。在接下来的系列文章中,我们将深入研究 ASP.NET MVC5 的世界。您的反馈和建设性批评始终受到赞赏,请继续提出。在那之前,请努力在宇宙中留下印记。