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

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.77/5 (23投票s)

2015 年 1 月 3 日

CPOL

13分钟阅读

viewsIcon

34646

关于 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 请求,检索和存储数据,返回适当的响应并执行应用程序逻辑。

ASP.NET MVC5 框架遵循“约定优于配置”原则,因此所有控制器类都应以“Controller”一词作为后缀,并放置在应用程序的 Controllers 文件夹中。

操作方法 (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.configGlobal.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.IgnoreRouteroutes.MapRoute 两个属性。如果这让您感到害怕,请不要担心。您现在不需要理解它。现在,只需记住 routes.MapRoute 属性包含应用程序所需的路由信息。

routes.MapRoute 的三个参数描述如下。

  1. name: 它描述了路由的名称
  2. url: 它描述了用户应在浏览器中键入的请求 URL 的语法。
  3. 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 的世界。您的反馈和建设性批评始终受到赞赏,请继续提出。在那之前,请努力在宇宙中留下印记。

© . All rights reserved.