三、控制器

正如第一章所讨论的,所有 web 应用都从服务器接收请求并生成响应,然后返回给最终用户。 在 ASP 中,控制器接收请求并根据输入数据生成输出。 净 MVC。

在本章中,你将学习以下主题:

  • 控制器在 ASP 中的作用。 净 MVC 应用
  • 路由介绍和概念
  • 创建您的第一个 ASP。 净 5 应用
  • 安装 ASP.NET CoreNuGet
  • 创建第一个控制器和action方法,它返回一个简单的Hello World
  • 添加一个视图,并做一些改变,以允许你的控制器使用那个视图
  • 添加一个模型并将模型数据传递给视图

控制器在 ASP 中的作用 净 MVC 应用

在高层,控制器在模型和视图之间进行协调,并将输出发送回用户。 这也是通常通过操作过滤器进行身份验证的地方。 动作滤波器将在本章的滤波器部分详细讨论。 下图说明了在 ASP 中请求的高级流程(包括步骤).NET MVC,并向我们展示了控制器如何融入全局:

Role of the Controller in ASP.NET MVC applications

下面是用户访问 ASP 时在高层将发生的事件序列.NET Core 应用:

  1. 用户在浏览器中输入 URL。
  2. 路由引擎根据 URL 的模式选择适当的控制器。
  3. Controller 与 Model 对话,通过它的操作方法获取任何相关数据。 动作方法是controller类中的方法。
  4. 然后控制器将数据传递给视图,以一种可查看的格式(通常是 HTML 元素)显示数据。
  5. 视图最终被交付给用户,用户将在浏览器中查看视图。

在讨论控制器之前,让我们先讨论路由概念的基础知识,因为路由引擎在运行时只选择合适的controlleraction方法。

路由介绍

路由引擎负责获取传入请求,并根据 URL 模式将该请求路由到适当的 Controller。 我们可以配置路由引擎,以便它可以根据相关信息选择适当的控制器。

按照惯例,ASP.NET MVC 遵循以下模式:Controller/Action/Id

如果用户输入 URLhttp://yourwebsite.com/Hello/Greeting/1,路由引擎选择HelloController中的Hello controller类和Greeting action方法,并将Id值作为1传递。 您可以为某些参数设置默认值,并使某些参数成为可选参数。

配置示例如下:

The template: "{controller=Hello}/{action=Greeting}/{id?}");

在前面的配置中,我们给路由引擎提供了三条指令:

  • 使用路由模式controller/action/id
  • 如果 URL 中没有提供controlleraction的值,则对controlleraction分别使用默认值HelloGreeting
  • Id参数设置为可选参数,这样 URL 就不需要包含这些信息。 如果 URL 包含这个Id信息,它将使用它。 否则,Id信息将不会传递给action方法。

让我们来讨论路由引擎如何为不同的 url 选择controller类、action方法和Id值:

URL1:http://localhost/ 
Controller: Hello 
Action method: Greeting 
Id: no value is passed for the id parameter 

原因:根据路由配置,Hello控制器作为默认值传递,因为 URL 中没有作为控制器传递值。

下面的action方法将被路由处理程序在前面的 URL 被传递时拾取:

public class HelloController : Controller { 
  public ActionResult Greeting(int id) { 
    return View(); 
  } 
}

URL2: http://localhost/Hello/Greeting2 
Controller: Hello 
Action method: Greeting2 
Id: no value is passed for the id parameter 

推理:URL 包含Hello作为第一个参数,选择Hello控制器;URL 包含Greeting2作为第二个参数,选择Greeting2动作方法。 请注意,只有当 URL 中没有值时,才会选择配置中提到的默认值。 由于id参数是可选的,且 URL 不包含id的值,所以没有值传递给id参数。

下面的动作方法Greeting2将被路由处理程序拾取,当前面的 URL 被传递时:

public class HelloController : Controller { 
  public ActionResult Greeting(int id) { 
    return View(); 
  } 

  public ActionResult Greeting2(int id) { 
    return View(); 
  } 
} 

URL3: http://localhost/Hello2/Greeting2 
Controller: Hello2 
Action method: Greeting2 
Id: no value is passed for the id parameter 

推理:作为第一个参数传递Hello2,选择Hello2控制器,作为第二个参数传递Greeting2,选择Greeting2动作方法。 由于id参数是可选的,并且没有为参数id传递值,所以没有为id传递值。

下面的action方法将被路由处理程序在前面的 URL 被传递时拾取:

public class Hello2Controller : Controller { 
  public ActionResult Greeting2(int id) { 
    return View(); 
  } 
} 
URL4: http://localhost/Hello3/Greeting2/1 
Controller: Hello3 
Action method: Greeting2 
Id: 1 

推理:Hello3是作为第一个参数选择的控制器,Greeting4是动作方法,1是作为id传递的值。

下面的action方法将会在前面的 URL 被传递时被路由处理程序拾取:

public class Hello3Controller : Controller { 
  public ActionResult Greeting2(int id) { 
    return View(); 
  } 
} 

我们将在后面的章节中详细讨论路由。

一旦请求到达控制器,控制器将通过与模型对话创建响应,并可能将数据传递给视图,然后视图将呈现给最终用户。

创建 ASP。 净 5 应用

是时候动手了。 让我们创建一个简单的 ASP。 净 5 应用。 启动 Visual Studio 并遵循以下步骤:

  1. Create a project by selecting File | New Project in Visual Studio. The first option is for creating an earlier version of the ASP.NET Web application. The second option is for creating the ASP.NET Core application using the .NET Core framework. The third option is for creating the ASP.NET Core application using the .NET framework. The difference between the second and third option is that the .NET framework supports all the functionalities of existing .NET frameworks whereas .NET Core supports only the core functionalities. The advantage of using the .NET core library is that it can be deployed on any platform.

    Creating ASP.NET 5 application

  2. Select the Empty template from the list of ASP.NET 5 templates. The second option is for creating the Web API application (for building the HTTP-based services) and the third option is for creating a web application containing some basic functionalities which you can run just from out of the box without you ever needing to write anything.

    Creating ASP.NET 5 application

  3. Once you click  OK in the window as shown in the preceding screenshot, (after selecting the Empty template option) a solution will be created as shown in the following screenshot:

    Creating ASP.NET 5 application

  4. When you run the application (by pressing F5 ) without any changes, you'll get the simple Hello World! text on your screen as shown in the following screenshot:

    Creating ASP.NET 5 application

我们没有在这个新创建的应用中进行任何编码。 那么,您是否考虑过它如何显示文本Hello World! ?

答案在Startup.cs文件中,该文件包含一个名为Startup的类。 这个类包含了Main方法,它作为 web 应用的入口点。 如果您使用过任何以前版本的 ASP.NET MVC,甚至 asp.net.NET Web 窗体,这将不是情况。

ASP.NET 5 运行时调用ConfigureServicesConfigure方法。 例如,如果您想配置任何服务,您可以将其添加到这里。 您的应用的任何自定义配置都可以添加到这个Configure方法:

public void ConfigureServices(IServiceCollection services) { 

} 

// This method gets called by the runtime. Use this method to  configure the HTTP request pipeline. 
public void Configure(IApplicationBuilder app) { 
  app.UseIISPlatformHandler(); 
  app.Run(async (context) => { 
    await context.Response.WriteAsync("Hello World!"); 
  }); 

} 

Configure方法中只有几个语句。 第一条语句告诉运行时使用IISPlatformHandler来处理所有传入的 HTTP 请求。 让我们暂时把第二句中的asyncawaitcontext放在一边,我们将在后面讨论。 实质上,第二条语句告诉运行时为所有传入的请求返回Hello World!,而不管传入的 URL 是什么。

当您在浏览器中键入 URLhttp://localhost:50140/Hello时,它仍然会返回相同的Hello World!

这就是我们得到Hello World 的原因! 当我们运行应用时。

因为我们在创建 ASP 的时候选择了空的模板.NET 5 应用,没有安装任何组件。 当你像我们一样选择了模板时,MVC 也不会被默认安装。

您可以通过打开project.json文件来确认它,在该文件中您看不到任何 ASP.NET MVC 在依赖项列表中被提到:

"dependencies": { 
  "Microsoft.AspNet.IISPlatformHandler": "1.0.0-rc1-final", 
  "Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final" 
}, 

首先,让我们安装 ASP。 我们的应用的 Net Core 包。

安装 ASP.NET Core NuGet 包在您的应用

按照以下步骤安装 ASP 的NuGet包.NET MVC:

  1. Right click on the project, and select the Manage NuGet Packages option:

    Installing the ASP.NET Core NuGet package in your application

  2. Select the Include Prerelease checkbox so that the NuGet Package Manager will list out all the prerelease packages. Search for MVC and you'll get the Microsoft.AspNet.MVC package, as shown in the following result, and click on the Install button on the right-hand side:

    Installing the ASP.NET Core NuGet package in your application

  3. Review the changes:

    Installing the ASP.NET Core NuGet package in your application

  4. Once you click on Review Changes , the following dialog box will appear where you need to accept the license terms:

    Installing the ASP.NET Core NuGet package in your application

NuGet 包管理器将下载并安装 ASP.NET Core 和将更新project.json文件和相关的引用。

现在,您的project.json文件将有更新的依赖项。 添加了第二行Microsoft.AspNet.Mvc:

"dependencies": { 
  "Microsoft.AspNet.IISPlatformHandler": "1.0.0-rc1-final", 
  "Microsoft.AspNet.Mvc": "6.0.0-rc1-final", 
  "Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final" 
}, 

另外,您还可以使用NuGet包和版本信息更新project.jsonNuGet 包管理器会自动下载并安装。

ASP.NET Core 安装在我们的应用中。 现在,我们需要告诉我们的应用使用 ASP。 净 MVC。

这需要对Startup.cs文件做一些修改:

  1. 配置应用以添加 MVC 服务。 这可以通过在Startup类的ConfigureServices方法中添加以下一行来实现:

    ``` services.AddMvc();

    ```

  2. 配置路由,以便根据输入的 URL 为传入请求选择正确的控制器。 以下代码片段需要在Startup.cs文件的Configure方法中更新:app。 路由=>{

    ``` app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); });

    ```

在前面的语句中,我们正在为应用配置路由。

在本章以及本书的大部分章节中,我们将手动编写代码,或者选择一个空的模板,而不是依赖于脚手架模板。 对于那些不熟悉脚手架这个术语的人来说,脚手架是为所选项目(例如控制器)生成所有必要的样板代码的特性,而不是需要您编写所有内容。 虽然我同意搭建模板很有用,并且在生成样板代码时节省了时间,但它们隐藏了许多初学者必须理解的细节。 一旦您手动编写代码,您就会知道每个组件是如何对全局做出贡献的所有复杂之处。 一旦你掌握了足够的基础知识,你就可以使用脚手架模板来节省编写样板代码的时间。

第一个控制器

在创建控制器之前,我们需要删除下面的app.Run语句,因为这将为所有传入的请求返回Hello World!。 由于我们希望传入的请求由控制器处理,我们需要从Startup类的Configure方法中删除以下代码:

app.Run(async (context) => { 
  await context.Response.WriteAsync("Hello World!"); 
}); 

我们已经安装了 ASP。 在我们的应用中。 因此,我们准备好创建我们的第一个 ASP.NET Core 控制器。 创建一个名为Controllers的文件夹,并通过从上下文菜单中选择添加一个新的控制器,如下图所示:

Our first Controller

一旦您选择添加|新项目,您将看到以下选项列表。 我们将添加一个 MVC 控制器类到我们的项目:

Our first Controller

将创建一个包含以下内容的类:

public class HomeController : Controller { 
  // GET: /<controller>/ 
  public IActionResult Index() { 
    return View(); 
  } 
} 

所有控制器,包括 MVC 和 Web API 控制器,都继承自Controller基类。 在早期版本的 ASP.NET MVC, MVC 控制器将继承自Controller类,Web API 控制器将继承自APIController类。

在前面的HomeController类中,我们有一个通过Index返回相应视图的操作方法。 当您按原样运行应用时,您将得到一个500 内部服务器错误。 原因是没有为HomeController和 ASP 的Index操作创建视图.NET Core 尝试搜索那个视图。 由于视图不可用,它返回一个500 内部服务器错误

让我们对这个操作方法做一个简单的更改,而不是创建并返回那个视图。 让我们返回一个字符串Hello World! I am learning MVC 6!,并更改IActionResult的返回类型:

public string Index() { 
  return "Hello World! I am learning MVC 6!"; 
} 

运行应用。 你会看到Hello World! 我正在学习 MVC 6! ,如下截图所示。 请确保您在前面提到的Configure方法中删除了app.Run语句:

Our first Controller

瞧! 我们已经改变了 ASP.NET Core 应用来渲染自定义内容,而不是无聊的Hello World。 我们所做的似乎是一个微小的改进,但是我们在我们的 ASP 中使用了控制器和动作方法.NET Core 应用,它给 web 应用开发带来了很多结构和灵活性。

Our first Controller

下面是运行应用时所发生的步骤序列:

  1. 应用在 URLhttp://localhost:50140上运行,其中50140是 IIS Express 为在本地系统上运行应用而选择的端口号。 这个数字可能有所不同。
  2. 因为我们没有传递任何参数,所以将选择Controlleraction方法的默认值。 在我们的案例中,HomeController将被选择为ControllerIndex将被选择为HomeController中的action方法。 因为ID是可选值,并且它没有被传递,所以这个ID参数被忽略。
  3. 路由引擎选择Controlleraction方法后,将控制传递给所选控制器的action方法。 在我们的例子中,它将是HomeControllerIndex动作方法。
  4. Index操作方法中,我们返回一个字符串Hello World! I am learning ASP.Net MVC 6!。 此文本从控制器返回,然后控制器返回给用户。

IActionResult

如果您注意到,控制器的action方法的默认返回类型是IActionResult,然后我们将返回类型更改为字符串,以便返回文本Hello World...

IActionResult是我们可以用来返回从简单字符串到复杂 JSON 数据的不同类型ActionResult的接口,因此,我们不需要更改action方法的return类型来返回字符串。

在前面的示例中,为了简化操作,我将return类型更改为字符串。 现在,让我们做一个简单的更改,通过保持返回类型(IActionResult)不变来返回字符串:

// GET: /<controller>/ 
public IActionResult Index() { 
  return Content("Hello World! I am learning MVC 6!"); 
} 

在返回字符串时,我们使用的是前面的action方法中的Controller类(继承HomeController的基控制器)中的virtual方法,称为Content。 这个Content()方法的目的是将字符串转换为IActionResult类型。

现在,运行应用。 我们应该得到同样的结果。

IActionResult能够返回不同的数据类型:

  • ContentResult:可以返回文本结果。
  • EmptyResult:返回null结果。
  • FileResult:返回二进制输出以写入响应。
  • HttpStatusCodeResult:提供返回方式。
  • JavaScriptResult:返回一个可以从客户端执行的脚本。
  • JSonResult:返回序列化的 JSON 对象。
  • RedirectResult:重定向到另一个action方法。
  • RedirectToRouteResult:表示使用指定路由值字典执行重定向的结果。

添加视图

我们从控制器返回一个简单的字符串。 虽然这解释了Controlleraction方法如何工作的概念,但它没有太多实际用途。

让我们创建一个名为Index2的新方法action:

public IActionResult Index2() { 
  return View(); // View for this 'Index2' action method 
} 

现在,我们已经创建了返回 View 的action方法。 但我们仍然没有添加视图。 按照惯例,ASP.NET MVC 会尝试在文件夹Views\{ControllerName}\{ActionMethod.cshtml}中搜索我们的视图。 对于前面的例子,它将尝试搜索Views\Home\Index2.cshtml。 请注意,controller文件夹的名称是Home,而不是HomeController。 根据约定,只有前缀是需要的。 由于此文件夹结构和文件不可用,当您试图通过 URLhttp://localhost:50140/Home/Index2访问此操作方法时,您将得到一个500 内部服务器错误

因此,让我们创建一个文件夹结构。 右键单击解决方案,从上下文菜单中选择Add|New Folder,创建一个名为Views的文件夹,然后在Views文件夹中创建名为Home的子文件夹:

Adding Views

右击Home文件夹,并从上下文菜单中选择添加|新建项目。 将出现如下截图所示的对话框。 将文件命名为Index2.cshtml,因为我们的action方法名为Index2cshtml是使用 c#时使用的剃刀视图引擎(这将在Views章节的ViewEngines部分详细讨论)扩展。

Adding Views

点击前面屏幕上的Add按钮,将创建一个名为Index2.cshtml的文件,其内容如下:

Adding Views

@*是 razor 视图引擎中的注释语法。 你可以在@{}块中编写任何 c#代码。

让我们在生成的代码后面添加一个简单的 HTML 块:

<html> 
  <body> 
    Hello! This is <b>my first View</b>  
  </body> 
</html> 

现在,当你运行应用时,你会得到以下输出:

Adding Views

下面的图表解释了请求流以及我们如何通过视图生成响应:

Adding Views

添加模型

模型表示您的业务领域类。 现在,我们将学习如何在控制器中使用模型。 创建一个Models文件夹并添加一个简单的Employee类。 这只是一个普通的老 c#类:

public class Employee { 
  public int EmployeeId { get; set; } 
  public string Name { get; set; } 
  public string Designation { get; set; } 
} 

在我们的HomeController中创建一个新的action方法Employee,并使用一些值创建Employee模型的一个对象,并将模型传递给视图。 我们的想法是在视图中使用模型员工价值观来呈现给用户:

using Chapter3.Models; 
public IActionResult Employee() { 
  //Sample Model - Usually this comes from database 
 Employee emp1 = new Employee { 
    EmployeeId = 1, 
    Name = "Jon Skeet", 
    Designation = " Software Architect" 
  }; 
  return View(emp1); 
} 

现在,我们需要为这个action方法添加相应的视图。 在View\Home folder中添加一个新的 Razor 视图文件。

添加以下代码片段。 在@符号之后的任何东西都被认为是剃刀代码。 在下面的代码中,我们试图访问传递给视图的Model对象的属性。 在本例中,Model表示在action方法中构造的employee对象。 你可以使用 Model 关键字从 View 中访问对象:

<html> 
  <body> 
 Employee Name : @Model.Name <br/> 
    Employee Designation: @Model.Designation <br/> 
  </body> 
</html> 

当您运行应用并输入 URLhttp://localhost:50140/Home/Employee时,您将看到以下输出:

Adding Models

从控制器传递数据到视图

我们刚刚讨论了如何使用Model对象将数据从控制器传递到视图。 在调用 View 时,我们将模型数据作为参数传递。 但有时你需要从控制器向视图传递一些临时数据。 这个临时数据可能不值得使用model类。 在这种情况下,我们可以使用ViewBagViewData

ViewData是字典,ViewBag是同一值的动态表示。

让我们使用ViewBagViewData添加公司名称和公司位置属性,如下代码片段所示:

public IActionResult Employee() { 
  //Sample Model - Usually this comes from database 
  Employee emp1 = new Employee { 
    EmployeeId = 1, 
    Name = "Jon Skeet", 
    Designation = " Software Architect" 
  }; 

  ViewBag.Company = "Google Inc"; 
  ViewData["CompanyLocation"] = "United States"; 

  return View(emp1); 
} 

在 View 文件中做相应的更改,这样我们就可以显示Companyname 和Company location值:

<html> 
  <body> 
    Employee Name : @Model.Name <br/> 
    Employee Designation: @Model.Designation <br/> 
    Company : @ViewBag.Company <br/> 
    Company Location: @ViewData["CompanyLocation"] <br /> 
  </body> 
</html> 

在进行上述更改后,运行应用:

Passing data from Controller to View

ViewBagViewData表示同一个集合,尽管集合中的条目是通过不同的方法访问的。 ViewBag值是动态值,在运行时执行,而ViewData是通过字典访问的。

为了测试这一点,让我们对我们的view文件做一个简单的更改:

Employee Name : @Model.Name <br/> 
Employee Designation: @Model.Designation <br/> 
Company : @ViewData["Company"] <br /> 
Company Location : @ViewBag.CompanyLocation <br /> 

尽管我已经使用ViewBagCompany值存储在Controller中,但我正在使用ViewData访问相同的值。 对于Company Location值也是如此,我们使用ViewData将值存储在 Controller 中,但是我们使用ViewBag访问该值。

在进行上述更改后运行应用时,您将看到与前面相同的结果。

过滤器

过滤器在 ASP.NET MVC 使您能够在执行管道中的特定阶段之前或之后运行代码。 它们可以针对每个控制器或每个操作进行全局配置。

有不同种类的过滤器,并且每个过滤器在管道中的不同阶段执行。 例如,在执行action方法时执行动作筛选器。

让我们用一个简单的例子来看看动作过滤器(一种过滤器)是如何工作的。

我已经创建了一个简单的控制器DateController,我只是在其中显示时间。 在这个action方法中,我使用了一个名为ResponseCache的预定义操作过滤器,它将响应缓存到以秒为单位指定的持续时间内。 在下面的代码片段中,我们提到了持续时间为 600 秒。 因此,响应将被缓存 10 分钟。

public class DateController : Controller { 
  [ResponseCache(Duration = 600)] 
  public IActionResult Index() { 
    return Content(DateTime.Now.ToShortTimeString()); 
  } 
} 

当我第一次运行它时,它显示预期的时间。 但是当您刷新浏览器(它间接地再次触发请求)时,时间不会更新,因为响应已经被应用缓存了。 在下面的截图中,即使时间是 7:43,应用仍然显示为 7:40:

Filters

以下是 ASP 中可用的预定义类型的过滤器。 净的核心。

授权过滤器

它们用于授权,主要用于确定当前用户是否被授权进行请求。

资源过滤器

这些是在授权后处理请求的过滤器,也是在请求离开过滤器管道之前处理请求的最后一个过滤器。 它们用于实现缓存或通过过滤器管道传递。

动作过滤器

这些封装了对单个action方法调用的调用,并且可以操纵传递到动作中的参数以及从动作返回的动作结果。

异常过滤器

它们用于管理 ASP 中未处理的异常。 净 MVC。

结果过滤器

这封装了单个操作结果,并且它们只在action方法成功执行时运行。

总结

在本章中,我们构建了我们的第一个 ASP.NET 5 应用从头开始,我们已经安装了 ASP。 在我们的 asp.NET Core。 净 5 应用。 我们已经了解了控制器是如何适应整个 ASP 的.NET MVC 应用,并学习了如何使用action方法构建您的第一个控制器。 我们还学习了如何在控制器中使用模型和视图。 我们还讨论了使用ViewBagViewData将数据从控制器传递到视图的不同方法。 我们还学习了 ASP 中的过滤器。 asp.net MVC 中预定义过滤器的使用。 净的核心。