ASP-NET-MVC-编程学习手册-全-

ASP.NET MVC 编程学习手册(全)

原文:Learning ASP.NET MVC Programming

协议:CC BY-NC-SA 4.0

零、前言

这本书旨在帮助你学习 ASP 的基本原理.NET Core MVC,并将这些知识应用于使用 asp.net 构建应用。 净的核心。 这本书还旨在为想学习 ASP 的初学者提供一个坚实的指南。 净 MVC。 具体而言,本书将涵盖以下主题:

  • ASP 的基本原理和目标。 净 MVC 核心
  • ASP 的哲学(关注点的分离,约定优于配置).NET Core
  • ASP 组件.NET Core mvc -控制器、模型和视图
  • 使用实体框架与数据库交互
  • 在客户端和服务器端验证用户的输入
  • 使用 Bootstrap 为应用提供一个改进
  • 利用 ASP 提供的不同部署选项。 净 MVC 核心

这本书的内容

第一章ASP 简介。 asp.net Core,涵盖了 ASP 的基本原理。 asp.net MVC 以及它如何适应 asp.net。 净生态系统。 本章解释了 web 开发的基础知识,包括客户端组件和服务器端组件,以及程序员在这两层中可以做什么和不可以做什么

第二章、环境设置向读者展示了如何设置开发环境,包括 Visual Studio 和 ASP 的安装。 净的核心。 讨论了建立开发环境的硬件和软件需求,并对 ASP 开发环境进行了剖析。 介绍了 asp.net MVC 应用。

第三章、控制器解释了控制器的构成、动作方法及其角色和职责。 在本章中,我们将创建一个简单的控制器和一个动作方法。 它将从整个 ASP 的角度向读者解释一个动作方法和一个控制器做什么。 净 MVC 应用。

第四章,Views,介绍了 Razor View 引擎的功能,并通过使用 Razor View 引擎的例子解释了各种基本的编程结构(条件语句,循环等等)。

第五章、模型介绍了模型在 ASP 中的作用.NET Core 应用。 本文讨论了 ViewModel 的概念,以及它如何为应用提供灵活性和数据划分。

第 6 章,Validation解释了使用 JavaScript 和 jQuery 库进行客户端和服务器端验证。

第 7 章Routing解释了路由模块,通过实例从接收到的请求中选择合适的控制器。 介绍了路由的各种选项和特性。 本章还将指导你如何为 ASP 构建自定义路由。 基于业务逻辑或用于 SEO 目的的 NET MVC 应用。

第八章美化 ASP.NET 应用与引导,教如何使用 Bootstrap,一个响应式前端框架,美化您的应用。 我们将指导您创建 HTML 表单控件。

第 9 章ASP 的部署 NET Core 应用,说明该项目如何进行。 json 库处理 ASP 的所有依赖项.NET Core 应用,以及版本。 它还解释了 K 运行时(ASP 的最新选项。 asp.NET Core 应用),以使一个 ASP. NET Core 应用.NET MVC 应用也可以部署在非 windows 环境中。

第 10 章,使用 Web API 构建 Web 服务,解释了基于 http 的服务以及如何使用 Web API 实现它们。 它还将向您介绍 Fiddler,并使用它编写一个 HTTP 请求。

第十一章提高 ASP 的性能.NET Core 应用,解释了在应用的各个层中分析性能和改进措施的方法。

第十二章NET Core 标识,解释应用的安全方面,并使用实体框架实现应用的安全标识。

你需要什么来写这本书

开始编程的 ASP.NET MVC 应用,你需要 Visual Studio Community 2015 IDE。 这是一个功能齐全的 IDE,可用于构建桌面和 web 应用。 您还需要各种包和框架,如 NuGet、Bootstrap 和 project。 Json,它的安装和配置将在本书中解释。

这本书是写给谁的

这本书是为那些想要学习如何使用 ASP 构建 web 应用的开发人员编写的.NET Core,那些想要利用微软技术构建 web 应用的开发人员,以及那些正在使用 Ruby on Rails 或其他 web 框架并想学习如何使用 ASP 的开发人员.NET Core MVC。

没有 ASP 的知识.NET 平台或。NET 平台是必需的。 即使您不需要有 c#的经验,理解任何现代编程语言的基本结构(循环、条件、类和对象)也会很有帮助。

约定

在这本书中,你会发现许多不同的文本样式来区分不同种类的信息。 下面是这些风格的一些例子以及对它们含义的解释。

文本中的代码字、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 url、用户输入和 Twitter 句柄如下所示:“我们需要将Kestrel HTTP Server包作为依赖项添加到project.json框架中”

一段代码设置如下:

public static void Main(string[] args)
{

var host = new WebHostBuilder()

.UseKestrel()

}

任何命令行输入或输出都写如下:

vi project.json

生词和重要词以粗体显示。 您在屏幕上看到的单词,例如菜单或对话框中的单词,会出现在这样的文本中:“本书中的快捷方式基于Mac OS X 10.5+方案。”

注意事项

警告或重要说明显示在这样的框中。

提示

提示和技巧是这样的。

读者反馈

我们欢迎读者的反馈。 让我们知道你对这本书的看法——你喜欢或不喜欢这本书。 读者反馈对我们来说很重要,因为它能帮助我们开发出你能真正从中获益最多的游戏。 要给我们发送一般性的反馈,只需发送电子邮件 feedback@packtpub.com,并在邮件主题中提到这本书的标题。 如果有一个主题,你有专业知识,你有兴趣写或贡献一本书,请参阅我们的作者指南www.packtpub.com/authors

客户支持

现在,你已经自豪地拥有了一本书,我们有一些东西可以帮助你从购买中获得最大的好处。

示例代码下载

您可以从您的帐户http://www.packtpub.com下载本书的示例代码文件。 如果您在其他地方购买这本书,您可以访问http://www.packtpub.com/support并注册,将文件直接通过电子邮件发送给您。

你可以按以下步骤下载代码文件:

  1. 使用您的电子邮件地址和密码登录或注册我们的网站。
  2. 将鼠标指针悬停在顶部的SUPPORT标签上。
  3. 点击代码下载&勘误表
  4. Search框中输入书名。
  5. 选择您想要下载代码文件的书。
  6. 从下拉菜单中选择你购买这本书的地方。
  7. 点击代码下载

下载文件后,请确保使用最新版本的解压或解压缩文件夹:

  • WinRAR / 7-Zip for Windows
  • Mac 的 Zipeg / iZip / unarx
  • 7-Zip / PeaZip for Linux

该书的代码包也托管在 GitHub 上的https://github.com/PacktPublishing/learning-asp-dot-net-core-programming。 我们在https://github.com/PacktPublishing/中还提供了丰富的图书和视频目录中的其他代码包。 检查出来!

下载这本书的彩色图片

我们还为您提供了一个 PDF 文件,其中有彩色图像的屏幕截图/图表使用在这本书。 彩色图像将帮助您更好地理解输出中的变化。 您可以从http://www.packtpub.com/sites/default/files/downloads/LearningAspDotNetCoreProgramming_ColorImages.pdf下载此文件。

勘误表

尽管我们已经竭尽全力确保内容的准确性,但错误还是会发生。 如果你在我们的书中发现错误,也许是文本或代码上的错误,如果你能向我们报告,我们将不胜感激。 通过这样做,您可以使其他读者免受挫折,并帮助我们改进这本书的后续版本。 如果您发现任何勘误表,请访问http://www.packtpub.com/submit-errata,选择您的图书,点击勘误表提交表格链接,并输入您的勘误表详细信息。 一旦您的勘误表被核实,您的提交将被接受,勘误表将被上载到我们的网站或添加到该标题的勘误表部分下的任何现有勘误表列表中。

要查看之前提交的勘误表,请访问https://www.packtpub.com/books/content/support并在搜索字段中输入书名。 所需资料将出现在勘误表部分。

盗版

在互联网上盗版受版权保护的材料是一个贯穿所有媒体的持续问题。 在 Packt,我们非常重视版权和授权的保护。 如果您在互联网上发现我们的作品以任何形式的非法拷贝,请立即提供我们的地址或网站名称,以便我们进行补救。

请通过 copyright@packtpub.com 与我们联系,并提供疑似盗版材料的链接。

我们感谢您的帮助,保护我们的作者和我们的能力,为您带来有价值的内容。

问题

如果您对本书的任何方面有任何问题,您可以通过 questions@packtpub.com 与我们联系,我们将尽最大努力解决问题。

一、ASP.NET Core 简介

ASP.NET Core 的最新版本。 来自微软的 asp.net MVC,是一个服务器端 web 应用开发框架,它可以帮助你有效地构建 web 应用。 它运行在 ASP.NET 5 平台,它使你的应用可以运行在各种各样的平台上,包括 Linux 和 Mac OS x。这打开了大量的机会,在这个时代成为。NET 开发人员是令人兴奋的。

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

  • 关于 web 应用的基本概念——http、客户端和服务器端
  • ASP.NET-ASP 的三种编程模型。 asp.net Web 窗体.NET 网页和 asp.net 网页.NET MVC
  • 哲学的 ASP.NET MVC
  • ASP 的特点。 asp.net Core 和 asp.net。 网 5

在讨论 ASP.NET Core 及其特性,让我们了解 web 应用开发的基本原理。 我坚信这样一个原则:如果你想成为某方面的专家,你就必须非常擅长基础知识。 这将有助于调试和修复问题。

话虽如此,我们还是要讨论以下几个关键的基本问题:

  • web 应用是如何工作的,以及一些关于 HTTP 的内容
  • 客户端和服务器端
  • HTTP 方法

只有三个关键概念。 没什么大不了的!

web 应用如何工作

所有的 web 应用,不管它们是否是用 ASP 构建的.NET MVC、Ruby on Rails 或任何其他新技术都是在 HTTP 协议上工作的。 有些应用使用 HTTPS (HTTP 的安全版本),其中数据在通过网络之前进行了加密。 但是 HTTPS 仍然使用 HTTP。

那么什么是 HTTP 协议呢?

HTTP代表超文本传输协议,是为分布式超媒体系统设计的应用协议。 超文本传输协议中的“超文本”是指使用超链接在文档之间进行遍历的结构化文本。 HTTP 的标准由Internet Engineering Task Force(IETF)和World Wide Web Consortium(W3C)开发。 HTTP 目前的版本是 HTTP/2,并在 2015 年标准化。 支持的浏览器有:Internet Explorer、Chrome、Firefox 等。

HTTP 协议(协议不过是一组管理通信的规则)是一种无状态协议,遵循请求-响应模式。

请求-响应模式

在讨论请求-响应模式之前,让我们先讨论几个术语:客户机和服务器。 服务器是一种计算资源,它接收来自客户机的请求并为其提供服务。 通常,服务器是一种具有处理许多请求的巨大内存的高性能机器。 客户机是发送请求和接收响应的计算资源。 通常,客户端可以是 web 服务器或发送请求的任何应用。

回到请求-响应模式,当您从服务器请求资源时,服务器将使用所请求的资源响应您。 资源可以是任何东西——网页、文本文件、图像或其他数据格式。

Request-response pattern

你发出一个请求。 服务器响应资源。 这称为请求-响应模式

HTTP 的无状态特性

当您再次请求相同的资源时,服务器将再次使用所请求的资源响应您,而不知道之前请求和服务的内容是相同的。 HTTP 协议本质上不知道以前接收和服务的任何请求的状态信息。 有几种可用的机制可以维护状态,但 HTTP 协议本身并不维护状态。 稍后我们将解释维护状态的机制。

让我用一个简单的实际示例向您解释无状态和请求-响应模式:

  1. 输入以下 URL:https://en.wikipedia.org/wiki/ASP.NET_MVC。 这是一个关于 ASP 的维基百科网页。 净 MVC。
  2. 从前面的 URL,浏览器触发一个对 Wikipedia 服务器的请求。
  3. 维基百科的网络服务器为你提供 ASP.NET MVC 网页。
  4. 你的浏览器接收这个网页并呈现它。
  5. 您可以通过再次输入相同的 URL(https://en.wikipedia.org/wiki/ASP.NET_MVC)再次请求相同的页面,然后按Enter
  6. 浏览器再次触发对 Wikipedia 服务器的请求。
  7. 维基百科为您提供相同的 ASP.NET MVC 网页,而没有意识到这一事实,相同的资源之前被请求从相同的资源。

注意事项

如前所述,有几种机制来维护状态。 让我们暂时假设,这里没有执行这种机制。 我知道我在这里过于简单化了,但这解释了这一点。

客户端和服务器端

有必要了解 web 应用的客户端和服务器端,以及双方可以做什么。 对于 web 应用,客户端是浏览器,服务器可以是 web 服务器/应用服务器。

浏览器端是浏览器中发生的任何事情。 它是 JavaScript 代码运行和 HTML 元素驻留的地方。

服务器端是在计算机的另一端的服务器上发生的任何事情。 从浏览器发出的请求必须通过线路(可能是通过网络)来执行一些服务器端代码并返回适当的响应。 您的浏览器不关心服务器端技术或服务器端代码所使用的语言。 服务器端也是 c#代码所在的地方。

让我们来讨论一些事实,使事情更清楚:

  • 事实 1:所有的浏览器只能理解 HTML, CSS 和 JavaScript,与浏览器厂商无关。

    • You might be using Internet Explorer, Firefox, Chrome, or any other browser. Still, the fact that your browser can understand only HTML, CSS, and JavaScript holds true. It cannot understand C#. It cannot understand Java. Nor Ruby. Only HTML, CSS, and JavaScript. This is the reason why you can access the web applications, built using any technology could be accessed by the same browser.

      Client-side and server-side

  • 事实 2:任何 web 开发框架的目的都是将服务器端代码转换为 HTML、CSS 和 JavaScript。

    • 这与前一点有关。 由于浏览器只能理解 HTML、CSS 和 JavaScript,所以所有的 web 开发技术都应该将服务器端代码转换为 HTML、CSS 和 JavaScript,以便浏览器能够理解。 这是任何 web 开发框架的主要目的。 这对于您是否使用 ASP 构建 web 应用是正确的。 净 MVC, ASP.NET Web 表单,Ruby on Rails,或者 J2EE。 对于如何生成 HTML、CSS 和 JavaScript,每个 web 开发框架可能都有一个独特的概念/实现,并且可能会以不同的方式处理安全性能等特性。 但是,每个框架都必须生成 HTML,因为这是您的浏览器所能理解的。

HTTP 方法

尽管 HTTP 协议的所有请求都遵循请求-响应模式,但发送请求的方式可能因时而异。 HTTP 方法定义如何将请求发送到服务器。

HTTP 中可用的方法有GETHEADPOSTPUTDELETETRACEOPTIONSCONNECTPATCH。 在大多数 web 应用中,GETPOST方法被广泛使用。 在本节中,我们将讨论这些方法。 稍后,我们将在需要知道的基础上讨论其他 HTTP 方法。

GET 方法

GET是 HTTP 协议的一种方法,用于从服务器获取资源。 使用GET方法的请求应该只检索数据,不应该有任何副作用。 这意味着,如果您一次又一次地触发相同的GET请求,您应该获得相同的数据,并且服务器的状态不应该因为这个 get 请求而发生任何变化。

GET方法中,参数作为请求 URL 的一部分发送,因此对最终用户可见。 这种方法的优点是,用户可以将 URL 标记为书签,并随时再次访问页面。 例如:www.yourwebsite.com?tech=mvc6&db=sql

我们在前面的GET请求中传递了两个参数。 tech是第一个值为mvc6的参数,db是第二个值为sql的参数。 假设您的网站使用前面的参数值,并在数据库中进行搜索,在将这些博客文章呈现给用户之前检索讨论mvc6sql的博客文章。

GET method

GET方法的缺点是,数据以明文形式作为参数在 URL 中传递,不能用于发送敏感信息。

而且,大多数浏览器对 URL 中的字符数量都有限制,所以在使用GET请求时,我们不能发送大量的数据。

POST 方法

请求通常用于更新或创建服务器上的资源。

数据在请求体中传递。 这有以下含义:

  • 您可以向服务器发送敏感信息,因为数据嵌入在请求体中,并且在 URL 中对最终用户不可见。

  • As the data is not sent through the request URL, it does not take up space in the URL and therefore it has no issues with the URL length limitations.

    POST method

既然我们已经介绍了基本原理,现在我们可以继续讨论 ASP.NET。

什么是 ASP.NET?

ASP.NET 是一个服务器端 web 应用开发框架,允许开发人员构建 web 应用、网站和 web 服务。 它最早是在 2002 年初由微软推出的,在这 14 年里,它经历了很多变化。

基本上,ASP.NET 有三种编程模型:

  • ASP。 净 Web 表单
  • ASP。 网网页
  • ASP.NET MVC

尽管上述所有编程模型的最终结果都是有效地生成动态网页,但它们遵循的方法各不相同。 让我们讨论每一个编程模型,以理解它们的原理。

净 Web 表单

从历史上看,当 ASP.NET 首先被引入,ASP.NET Web Forms 是程序员在 ASP.NET 中开发 Web 应用的唯一编程模型。

ASP.NET Web Forms 模型抽象了 Web,因此即使 Web 本身是无状态的,它也可以维护状态。

它还支持服务器端的事件驱动编程模型。 这帮助桌面应用开发人员平稳地过渡到 web 应用开发。

像 PHP 和其他一些 web 应用框架一样,ASP.NET Web Forms 是一个基于文件的框架,用户通过访问服务器上的文件来访问 Web 页面。 服务器将处理您的请求,将该文件中的所有服务器端组件转换为 HTML,并将其发送回请求客户机。

每个网页在 ASP.NET Web 表单由两个文件表示:.aspx.aspx.cs.aspx.vb。 这个.aspx文件包含你的前端组件——所有的 ASP 控件和 HTML 元素。 .aspx.cs(如果你使用 c#作为代码后置语言)或.aspx.vb(如果你使用 Visual Basic 作为代码后置编程语言)包含在网页上发生的事件的代码。

在 ASP 到来之前,这是主要的编程模型.NET MVC,并且这个编程模型仍然被用于维护使用这个模型编写的生产应用。

网网页

ASP.NET Web Pages 主要针对那些数据处理逻辑直接写在 Web 页面上的小型 Web 应用。

NET MVC

ASP.NET MVC 是 ASP.NET 中 MVC 模式的实现。 ASP 的缺点.NET Web 表单,例如对 HTML 生成的有限控制都在 ASP 中解决。 净 MVC。 大多数的现代应用控制的客户端 JavaScript 库/框架,如jQuery , KnockoutJS,AngularJS,有完全控制生成的 HTML 是至关重要的。

让我们来谈谈模型-视图-控制器模式,以及它对 web 应用开发的好处。

模型-视图-控制器(MVC)模式:这是一种软件架构模式,它帮助定义每个组件的职责,以及它们如何结合在一起以实现总体目标。 该模式主要用于构建用户界面,并适用于许多领域,包括开发桌面应用和 web 应用。 但我将从 web 开发的背景来解释 MVC 模式。

首先,MVC 模式有三个组件:

  • 模型:该组件表示您的领域数据。 请注意,这不是您的数据库。 这个模型组件可以与您的数据库对话,但是模型只代表您的领域数据。 例如,如果您正在构建一个电子商务 web 应用,模型组件可能包含诸如 Product、Supplier 和 Inventory 等类。

  • 视图:该组件负责向用户呈现什么内容。 通常,该组件将包含您的 HTML 和 CSS 文件。 这可能还包括控制 web 应用对最终用户的外观的布局信息。

  • Controller : As the name implies, the controller is responsible for interacting with different components. It receives the request (through the routing module), talks to the model, and sends the appropriate view to the user.

    ASP.NET MVC

这种职责的分离为 web 应用开发带来了很大的灵活性,允许对每个区域进行单独和独立的管理。

ASP. net 的特点 NET MVC

ASP.NET MVC 是一种固执己见的应用开发框架,它倾向于以某种独特的方式处理某些功能。 让我们来讨论一下 ASP 的每个特性.NET MVC,以及它们带来的好处。

约定优于配置

这是一种设计方法,在开发应用时大大减少了决策的数量,从而使其变得更简单。

如果您已经使用任何技术构建了任何应用,那么您可能会使用某种 XML 文件,其中必须配置其中的所有内容。 即使是最简单的东西,我们可能也要对那里的东西进行配置。

ASP.NET MVC 完全接受了约定而不是配置。 这是一种哲学,你可以在不配置相同的情况下确定它将如何工作。

让我给你举个简单的例子。 所有控制器代码都位于Controller文件夹中,视图为每个控制器都有一个单独的文件夹。 每当请求到来时,ASP.NET MVC 知道在哪里找到控制器和它关联的视图,而不需要任何配置。 这种方法导致更少的配置和更少的调试时间。

关注点分离

如前所述,ASP.NET MVC 有三个主要组件:模型、控制器和视图。 这清楚地分离了职责,以便 UI 设计人员或 UI 开发人员可以在 View 上工作,而后端开发人员可以在 Model 上工作,为应用构建数据域或与数据库对话。 由于每个组成部分的职责是明确定义和分开的,工作可以并行进行。

控制生成的 HTML

如果你有任何构建 ASP 的经验.NET Web 窗体应用,您可能已经使用了诸如asp:textbox之类的 ASP 控件。 尽管这些控制有很多好处,但它们也有缺点。 当使用这些控件时,开发人员不能完全控制生成的 HTML。 当然,您可以在 ASP 控件中设置一些属性,而 ASP 控件又可以在生成的 HTML 中设置一些属性。 但完全控制是不可能的。 ASP。 asp.net MVC HTML 助手和标签助手.NET Core 提供了对生成的 HTML 的更好的控制。

更好的支持单元测试

由于每个组件被分离和划分,创建单元测试用例变得更容易实现:

  • ASP 中统一的 MVC 和 Web API 控制器 NET Core:在早期版本的 asp.net.NET MVC, MVC(System.Web.MVC.Controller)和 Web API(System.Web.Http.ApiController)使用不同的控制器。 在 ASP.NET Core,只有一个基本控制器支持创建 MVC 控制器和 Web API 控制器。 关于路由,所有控制器使用相同的路由。 当然,您可以根据需要使用基于约定的路由或基于属性的路由。
  • 关于 Web API 的注意事项:Web API 是微软通过 HTTP 协议构建 Web 服务的技术。 HTTP 不仅仅局限于服务网页。 Web API 可以用于构建 API 服务和数据。 这种方法的优点是,使用 Web API 构建的服务可以被广泛的客户机使用,比如浏览器、移动应用和桌面应用。

ASP 的早期版本的代码。 asp.net MVC(直到 asp.net.NET MVC 5)如下:

publicclassValuesController : ApiController
{ 
  // GET api/values 
  publicIEnumerable<string>Get()
  { 
    returnnewstring[] { "value1","value2"}; 
  } 
} 
Code for ASP.NET Core: 
publicclassValuesController:Controller
{ 
  //GET api/values 
  [HttpGet] 
  publicIEnumerable<string>Get()
  { 
    returnnewstring[] { "value1","value2"}; 
  } 
} 

网 5

ASP.NET 5 是微软用于使用。NET 构建现代云应用的最新框架。 这是一个跨平台的框架,你可以运行你的应用建立在 ASP.NET 5 可以在任何平台上运行,比如 Linux 或 Mac OS X,当然也可以在 Microsoft Windows 上运行。 ASP.NET 5 是开源的,完整的源代码可以在 GitHub 上的https://github.com/aspnet/home上找到。

ASP 的最新版本。 净 MVC, ASP.NET core 运行在 asp.net。 净 5 平台。

ASP. net 的特点 网 5

  • 跨平台支持:应用是建立在 ASP 之上的.NET 5 可以在任何平台上运行。 已安装 net5。 这意味着您在 ASP 上构建的应用.NET 5 可以在苹果 OS X 和 Linux 机器上运行。 部署 ASP.NET 内核在 Linux 机器上将在后面的章节中解释。
  • 更好的支持客户端开发: NET 5 被设计为与一系列客户端框架无缝工作,如AngularJsKnockoutBootstrapReact.js

总结

在本章中,我们学习了 web 开发的基础知识,包括服务器端和客户端是由什么组成的。 我们甚至讨论了 ASP 的特性。 asp.net Core 和 asp.net。 净 5。

二、设置环境

在任何开发项目中,设置正确类型的开发环境都是至关重要的,这样您就可以集中精力开发解决方案,而不是解决环境问题或配置问题。 对于。net, Visual Studio 实际上是在。net 中构建 web 应用的标准IDE(集成开发环境)。

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

  • IDE 的目的
  • Visual Studio 的不同产品
  • 安装 Visual Studio Community 2015
  • 创建您的第一个 ASP.NET MVC 5 项目和项目结构

IDE 的目的

首先,让我们看看为什么我们需要一个 IDE,当您可以在记事本中输入代码,编译它,并执行它。

当你开发一个 web 应用时,你可能需要以下东西来提高效率:

  • 代码编辑器:这是输入代码的文本编辑器。 您的代码编辑器应该能够识别不同的结构,例如编程语言的if条件、for循环。 在 Visual Studio 中,所有关键字都将以蓝色突出显示。
  • 智能感知:智能感知是一种上下文感知的代码补全特性,在大多数现代 ide 中都有,包括 Visual Studio。 一个这样的例子是当你在一个对象后面键入一个点; 这个智能感知特性列出了对象上所有可用的方法。 这有助于开发人员更快更容易地编写代码。
  • 构建/发布:如果您可以使用单个单击或单个命令构建或发布应用,这将很有帮助。 Visual Studio 提供了几个开箱即用的选项,可以在一次单击中构建单独的项目或完整的解决方案。 这使得您的应用的构建和部署更加容易。
  • 模板:根据应用的类型,您可能需要创建不同的文件夹和文件以及样板代码。 因此,如果您的 IDE 支持创建不同类型的模板,这将是非常有用的。 Visual Studio 使用 asp.net 的代码生成不同类型的模板.NET Web 表单、MVC 和 Web API 来帮助您启动和运行。
  • 添加项目的易用性:你的 IDE 应该允许你轻松地添加不同种类的项目。 例如,您应该能够毫无问题地添加 XML 文件。 如果 XML 文件的结构有任何问题,它应该能够突出显示问题并提供信息来帮助您解决问题。

Visual Studio 产品

Visual Studio 2015 有不同的版本来满足开发人员/组织的不同需求。 Visual Studio 2015 主要有四个版本:

  • Visual Studio 社区
  • Visual Studio Professional
  • Visual Studio Enterprise
  • Visual Studio 测试专业

系统需求

Visual Studio 可以安装在运行 Windows 7 Service Pack 1 及以上操作系统的计算机上。 你可以从以下网址了解完整的需求列表:

https://www.visualstudio.com/en-us/downloads/visual-studio-2015-system-requirements-vs.aspx

Visual Studio 社区 2015

这是一个功能齐全的 IDE,可用于构建桌面、web 应用和云服务。 个人用户可以免费使用。

你可以从以下网址下载 Visual Studio Community:

https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx

在本书中,我们将使用 Visual Studio Community 版本进行开发,因为它对个人开发人员是免费的。

Visual Studio Professional

顾名思义,Visual Studio Professional 是面向专业开发人员的,它包含了诸如Code Lens等特性,可以提高团队的生产力。 它还具有促进团队内部协作的特性。

Visual Studio Enterprise

Visual Studio Enterprise 是 Visual Studio 的成熟版本,它具有一套完整的协作功能,包括团队基础服务器、建模和测试。

Visual Studio 测试专业

Visual Studio Test Professional 主要针对测试团队或参与测试的人员,其中可能包括开发人员。 在任何软件开发方法中,无论是瀑布模型还是敏捷开发人员都需要为他们正在开发的代码执行开发套件测试用例。

安装 Visual Studio 社区

按照给定的步骤安装 Visual Studio Community 2015:

  1. Visit the following link to download Visual Studio Community 2015:

    https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx

    Installing Visual Studio Community

  2. Click on the Download Community 2015 button. Save the file in a folder where you can retrieve it easily later:

    Installing Visual Studio Community

  3. Run the downloaded executable file:

    Installing Visual Studio Community

  4. Click on Run and the following screen will appear:

    Installing Visual Studio Community

有两种类型的安装—默认安装和自定义安装。 默认安装安装最常用的特性,这将涵盖开发人员的大多数开发人员用例。 自定义安装帮助您自定义组件,您想要安装:

  1. 选择安装类型后,点击安装按钮。

  2. Depending on your memory and processor speed, the installation will take 1 to 2 hours to install.

    Installing Visual Studio Community

  3. Once all the components are installed, you will see the following Setup Completed screen:

    Installing Visual Studio Community

安装 ASP 网 5

当我们安装 Visual Studio Community 2015 版时, NET 5 将默认安装。 ASP.NET Core 应用运行在 asp.net 的基础上.NET 5,我们需要安装 ASP。 净 5。 有几种方法可以安装 ASP。 净 5:

  • Get ASP.NET 5 from https://get.asp.net/

    Installing ASP.NET 5

  • 另一个选项是在 Visual Studio 中从新项目模板安装

这个选项比较简单,因为您不需要搜索和安装。

具体步骤如下:

  1. Create a new project by selecting File | New | Project or using the shortcut Ctrl + Shift + N :

    Installing ASP.NET 5

  2. Select ASP.NET Web Application and enter the project name and click on OK :

    Installing ASP.NET 5

  3. The following window will appear to select a template. Select the Get ASP.NET 5 RC option as shown in the following screenshot:

    Installing ASP.NET 5

  4. When you click on OK in the preceding screen, the following window will appear:

    Installing ASP.NET 5

  5. When you click on the Run or Save button in the preceding dialog, you will get the following screen asking for ASP.NET 5 Setup. Select the checkbox, I agree to the license terms and conditions and click on the Install button:

    Installing ASP.NET 5

  6. Installing of ASP.NET 5 might take a couple of hours. Once it is completed you'll get the following screen:

    Installing ASP.NET 5

在安装ASP 的过程中.NET 5 RC1 Update 1,它可能会要求你关闭 Visual Studio。 如果有人问,请这样做。

ASP 中的项目结构。 净 5 应用

一旦 ASP.NET 5 RC1 成功安装,打开 Visual Studio,创建一个新项目,并选择 asp.net 5 RC1.NET 5Web 应用如下截图所示:

Project structure in ASP.NET 5 application

一个新的项目将被创建,结构如下:

Project structure in ASP.NET 5 application

基于文件的项目

当您在文件系统中添加一个文件或文件夹时(在 ASP.NET 5 项目文件夹),这些更改将自动反映在您的应用中。

支持全。net 和。NET Core

你可能已经注意到前面的项目中有几个参考文献:DNX 4.5.1DNX Core 5.0DNX 4.5.1提供了功能全面的。net 而5.0【T7 DNX 核心】仅支持核心功能,这将使用如果你在跨平台部署应用,如苹果 OS X, Linux。 ASP 的开发和部署.NET Core 在 Linux 机器上的应用将在后面的章节中解释。

项目。 json 包

通常,在 ASP。 在 asp.net web 应用中,我们将把这些程序集作为引用,并将引用列表放在 c#项目文件中。 但是在 ASP 中.NET 5 应用,我们有一个名为Project.json的 JSON 文件,它将包含所有必要的配置及其所有。NET 依赖项以NuGet包的形式存在。 这使得依赖管理更加容易。 NuGet是一个由 Microsoft 提供的包管理器,它使得包的安装和卸载更加容易。 在NuGet之前,所有依赖项都必须手动安装。 依赖项部分标识了应用可用的依赖包列表。 框架部分告诉我们应用支持的框架。 脚本部分标识在应用的构建过程中要执行的脚本。 包含和排除属性可以用于任何节中包含或排除任何项。

控制器

此文件夹包含您的所有控制器文件。 控制器负责处理请求、通信模型和生成视图。

型号

所有表示域数据的类都将出现在这个文件夹中。

视图

视图是包含前端组件并呈现给应用最终用户的文件。 此文件夹包含您所有的Razor视图文件。

迁移

任何与数据库相关的迁移都可以在此文件夹中找到。 数据库迁移是 c#文件,其中包含通过实体框架(ORM 框架)完成的任何数据库更改的历史。 这将在后面的章节中详细说明。

wwwroot 文件夹

这个文件夹充当根文件夹,它是放置 CSS 和 JavaScript 文件等所有静态文件的理想容器。 所有放在wwwroot文件夹中的文件都可以直接从路径访问,而不需要通过控制器。

其他文件

appsettings.json文件是配置文件,您可以在其中配置应用级别的设置。 Bowernpm(Node Package Manager),gulpfile.js都是由 ASP 支持的客户端技术。 净 5 应用。

总结

在本章中,您了解了 Visual Studio 中的产品。 提供了安装 Visual Studio Community 版本的逐步说明,个人开发人员可以免费获得该版本。 我们还讨论了 ASP 的新项目结构.NET 5 应用及其与以前版本相比的变化。

在下一章中,我们将讨论控制器及其角色和功能。 我们还将构建一个控制器和相关的操作方法,并了解它们是如何工作的。

三、控制器

正如第一章所讨论的,所有 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 中预定义过滤器的使用。 净的核心。

四、视图

视图是交付给用户的应用的实际输出。 它是他们从屏幕上访问你的应用时实际看到的内容。 所有的组件,包括菜单、输入元素、对话框,以及用户看到的一切都只来自视图。 如果您在访问应用时没有提供良好的用户体验,用户将不会关心您的应用有多好。 因此,视图在构建 ASP 时起着至关重要的作用。 净 MVC 应用。

在本章中,我们将涵盖以下主题:

  • 视图引擎和剃刀视图引擎的目的
  • 剃刀视图引擎编程和不同的编程结构
  • 布局在 ASP.NET Core 及其特性
  • HTML 帮助
  • 局部视图
  • 标签助手

视图引擎和 Razor 视图引擎

第一章ASP 简介.NET Core,浏览器只能理解 HTML、CSS 和 JavaScript。 视图引擎的目的是从您的视图生成 HTML 代码,并将其发送到浏览器,以便它能够理解内容。 主要有两种不同类型的视图引擎——razor 视图引擎和 Webform 视图引擎。 尽管这两个视图引擎都是使用 ASP.NET MVC,你可以使用任何自定义视图引擎。

Razor 视图引擎

Razor 视图引擎是 ASP 中默认的和推荐的视图引擎.NET Core,以及以后,这可能是你安装 ASP 时唯一跳出来的视图引擎。 净 MVC。

你可以在 Razor View 中混合使用 c#代码和 HTML 代码,而且 Razor View 引擎足够智能,能够区分这两种代码,并生成预期的输出。 在某些情况下,我们可能需要提供额外的信息给 Razor View 以产生适当的结果。 剃刀代码块以@符号开始,不需要关闭@

在 Razor View 引擎中编程

在 Razor View 引擎中编程就像在 c#中编程一样。 不同之处在于,在 Razor View 引擎中,你的 c#代码会和 HTML 混合在一起,以产生想要的 HTML 输出。

Razor 视图中的变量

您可以在 razor 块中声明一个变量,并使用@符号来使用该变量。

注意事项

对于本章中的所有示例,我们只给出视图的代码示例。

让我们通过一个例子来讨论这个问题。

  1. 创建一个Controllers文件夹和一个名为HomeController的控制器。
  2. 创建一个名为Views的文件夹,子文件夹称为Home,和一个视图文件名为Index.cshtml通过右键单击上下文菜单并选择添加 | 新项目MVC Razor 视图从列表中【显示】。

HomeController.cs文件将有以下代码:

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

接下来是更新后的 Razor View,我们将在其中声明一个变量并使用它。 前五行和后两行是简单的 HTML 元素。

我们将集中在粗体的行上。 然后,我们将使用@创建一个 Razor 块,并在其中声明一个变量。 Razor 块以右花括号结束。 代码片段Value:被认为是简单的 HTML 文本。 由于我们想要使用 razor 变量值,我们将使用@i来指示 razor 视图引擎i不是一个正常的 HTML 文本; 这是一个剃刀结构,并将得到相应的对待。 完整的 HTML 代码如下:

<html>
  <head>
    <title> Views demo</title>
  </head>
  <body>

 @{

 int i = 5; 

 } 

     Value: @i 
  </body>
</html>

当你运行应用时,你会看到以下输出:

Variables in Razor View

请注意,当您访问 razor 变量时,需要使用@符号。 如果没有这个,Razor View 引擎将i视为文本而不是表达式。

下面的截图是你访问没有@符号的变量时得到的结果:

Variables in Razor View

for 循环

在 Razor View 中,你可以使用 c#中可用的大多数编程结构。 下面这段代码是for循环结构,我们循环五次并打印变量名:

@{
   for (int i = 0; i < 5; i++)
   {
     <li>@(i+1)</li>
   }
 }

以下是需要注意的几点:

  • 由于 for 循环是剃刀代码,我们应该用@符号将循环括起来,以表明下面的代码是剃刀代码,而不是普通的 HTML。
  • 每当我们使用 HTML 元素或标签时,Razor View 引擎就会回到 HTML 模式。 如果你想在 HTML 标签中使用任何 Razor 表达式,你需要再次包含@符号来告诉 Razor 视图引擎后面的代码是一个 Razor 代码,而不是一个 HTML 元素。 这就是我们在前面的表达式中再次使用@符号的原因,即使在父根级剃刀代码中也是如此。

视图的完整代码如下:

<html>
  <head>
    <title> Views demo</title>
  </head>
  <body>
    <ul>
    @{
       for (int i = 0; i < 5; i++)
       {
         <li>@(i+1)</li>
       }
     }
    </ul>
  </body>
</html>

while 循环

下面这段代码是相同循环的while循环实现。 请注意,大胆的表达式增加了变量 i。我们不会使用@符号,因为它不在 HTML 元素中:

@{
   int i = 0;
   while(i<5)
   {
     <li>@(i + 1)</li>

 i++; 

   }
 }

foreach 循环

Razor View 中的foreach循环和 c#中的foreach循环是一样的。 在下面的代码中,我们将初始化一个整数列表,遍历该列表并将其作为列表项打印:

<ul>
  @{
     List<int> integers = new List<int>
     {
       1,2,3,4,5
     };
     foreach (int i in integers)
     {
       <li>@i</li>
     }
   }
</ul>

if 条件

在下面的代码中,我们将检查变量的值是否小于 10。 如果它小于 10,我们将打印i is less than 10,否则,我们将显示i is greater than 10。 您可能想知道为什么我们必须包含text标签以及它的目的是什么。 由于我们在 Razor 视图代码块中,文本i is less than 10将被认为是 Razor 表达式,但它不是。

这个text标签是用来指示 Razor 视图引擎,不管text标签后面是什么,都被认为是文本,而不是 Razor 表达式:

@{
   int i = 5;
   if (i < 10)
   {
     <text>i is less than 10</text>
   }
   else
   {
     <text>i is greater than 10</text>
   }
 }

布局

在前面讨论的所有示例中,我们已经在单个文件中完成了完整的 View 编码。 这将导致灵活性的缺乏和可重用性的降低。

考虑以下网页结构,其中顶部部分包含公司标志或横幅,侧面部分包含网站各个部分的链接。 内容部分随页面的不同而变化。

Layout

如果我们在单个视图中编码完整的内容,我们可能必须在每个页面中复制Top SectionSide Section。 如果我们想要更改侧节中的任何内容,我们将必须更改所有文件。 这清楚地表明单个 View 文件并不是最好的解决方案。

在这种情况下,布局可以发挥作用。 布局定义了可以在所有网页中重用的网站结构。 布局甚至不需要有像顶部部分或侧面部分的东西; 它甚至可以包含一个简单的 HTML 结构,其中可以包含公共内容,而正文内容将从单个视图呈现。

让我们构建第一个布局。 为了使用布局,你需要具备以下三点:

  1. 通知布局文件的名称——该信息应该在_ViewStart.cshtml中可用。 按照惯例,所有共享文件的名称都以下划线开头,并且该文件直接位于Views文件夹下。

  2. 创建布局文件——按照惯例,文件的名称是_Layout.cshtml,它将位于Shared文件夹中。 所有共享内容,比如部分视图,也可以在这里找到。 分部视图将在本章后面讨论。

  3. Create the content View file—this View file is almost same as the earlier View files that we created so far with only one difference; only page-specific content will be available in this file, and this means that you'll not have any html , head , or title tags here.

    Layout

在创建_ViewStart.cshtml_Layout.cshtml和特定于页面的 View 文件之后,文件夹结构将与前面的快照类似。

创建 _ViewStart.cshtml

右键单击Views文件夹,在Context菜单中选择Add New Item。 然后在添加新项目对话框中选择MVC 视图起始页,如下图所示:

Creating _ViewStart.cshtml

当您点击Add按钮时,它将创建一个包含以下内容的文件:

@{
   Layout = "_Layout";
 }

创建 _Layout.cshtml

Views文件夹中创建一个名为Shared的文件夹。 然后,右键单击Shared文件夹,在Context菜单中选择Add New Item,如下截图所示:

Creating _Layout.cshtml

当您点击Add按钮时,它将创建_Layout。 cshtml包含以下内容:

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
</head>
<body>
<div>

@RenderBody() 

</div>
</body>
</html>

前面的布局文件是一个简单的 HTML 内容,包含两个 Razor 表达式。 @ViewBag 标题用于显示从控制器传递过来的标题信息,而@RenderBody是一个 Razor 表达式,它调用特定于页面的 View,并在那里合并该内容。

添加特定页面视图

在添加视图之前,我们需要在HomeController文件中添加一个动作方法,我们将从该方法中调用特定于页面的视图。

我们添加一个名为Index2的操作方法,如下所示:

public IActionResult Index2()
{
  ViewBag.Title = "This is Index2";
  return View();
}

ViewBag用于将信息从控制器传递到视图。 在这里,我们将Title信息从操作方法传递到视图。

现在,右键单击Views文件夹,选择Add|New Item,选择MVC View Page,将文件保存为Index2.cshtml

在生成的视图中,我添加了简单的Hello文本。 此文本将在布局页面的主体中呈现。 View 文件的完整代码如下:

@*
For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
*@
@{
 …
 }
 Hello. This text will be rendered in body of the layout page 

一切都准备好了。 运行应用并在浏览器中键入 URLhttp://localhost:50132/Home/Index2。 请注意,当您从 PC 上运行应用时,本地主机后的端口号可能会有所不同。

Adding a page-specific View

正如所料,您将看到前面图片中的文本。 然而,我们的重点不是文本。 它是关于生成的 HTML 内容的结构。

Ctrl+U查看源代码(适用于 Windows 下的 Chrome 浏览器)。 你会看到以下 HTML 内容:

Adding a page-specific View

顶部的内容(htmlheadbody,div开放标签)和底部的内容(html,head,body,div结束标记)来自布局文件和文本来自特定于页面视图。

生成 HTML

第一章ASP 简介.NET Core,浏览器只能理解 HTML、CSS 和 JavaScript,而不管你使用何种技术来构建 web 应用。 在用 ASP 构建应用时,这是正确的.NET MVC 也是如此。

大多数应用获得用户输入,处理输入,然后将所需信息存储在数据库中,以便以后检索它们。 在 web 应用的上下文中,Form HTML 元素用于获取用户输入。

下面是在 ASP 中生成 HTML 元素的几种方法.NET Core:

  • HTML 帮助
  • 标签助手

HTML helper 是帮助生成 HTML 元素的服务器端方法,这些元素可以被浏览器理解。 HTML 助手是生成 HTML 元素的主要方法,直到 ASP。 净 MVC 5。

标签助手,在 ASP 中引入.NET Core,也产生 HTML 元素。 我们将在本章后面的章节中讨论标记帮助程序,它看起来就像 HTML 元素,在其中添加属性以将它们标识为标记帮助程序。 使用 Tag 助手而不是 HTML 助手的优点是用户界面设计者/工程师不需要担心 Razor 代码。 它们只是使用 HTML 元素和其他属性进行编码。

在讨论 HTML 帮助程序和标签帮助程序之前,让我们先回过头来讨论一下为什么我们需要它们。

让我们考虑一个简单的表单,如下图所示,我们希望在其中获取用户的姓名和年龄。 如果用户输入她的年龄,我们将显示You are eligible to vote!。 如果没有,则显示You are not eligible to vote now:

Generating HTML

下面是显示上述简单表单的 HTML 代码:

<form>
  <table>
    <tr>
      <td>
        <label for="txtName">Name</label>
      </td>
      <td>
        <input type="text" id="txtName" />
      </td>
    </tr>
   <tr>
     <td>
       <label for="txtAge">Age</label>
     </td>
     <td>
      <input type="text" id="txtAge" />
     </td>
   </tr>
   <tr>
     <td colspan="2">
       <input type="submit"  />
     </td>
   </tr>
  </table>
</form>

这种直接编码 HTML 元素的方法既耗时又容易出错。 例如,在前面的表单中,标签和输入 HTML 元素引用相同的元素(第一组中的txtName和第二组中的txtAge)。 如果我们手工编写 HTML 元素,在构建 HTML 元素时可能会出现打字错误。

HTML 助手

HTML 帮助程序是为您生成 HTML 的服务器端方法。 我们可以使用 HTML 帮助程序生成相同的表单,如下所示(分别生成 HTMLform元素、@Html.Label@Html.TextBox元素):

@using (Html.BeginForm())
 {
   <table>        
     <tr> 
       <td>@Html.Label("Name")</td>
       <td>@Html.TextBox("txtName")</td>
     </tr>
     <tr>
       <td>@Html.Label("Age")</td>
       <td>@Html.TextBox("txtAge")</td>
     </tr>
     <tr>
       <td colspan="2"><input type="submit" value="Submit" /></td>
     </tr> 
   </table>
}

HTML Helpers

您可能想知道,既然可以手动编写 HTML 代码,为什么还需要使用 HTML 帮助程序。 当我们将模型从控制器传递到视图时,事情会变得更加复杂。 使用 HTML 帮助程序,我们可以直接从Models文件中构建form元素,这样它们就可以从您正在使用的Models文件中选取名称。

例如,让我们创建一个名为Models的文件夹和一个名为Person的类。 这个类将作为一个模型,如下面的截图所示:

HTML Helpers

Person类只是一个 POCO(普通的旧 c#对象)类,它将充当一个模型。 这个类的完整代码如下:

public class Person
{
  public int PersonId { get; set; }
  public string Name { get; set; }
  public int Age { get; set; }
}

让我们创建一个名为ValidateAge的新操作方法。 在这个方法中,我们将创建一个空的Person类,并将模型传递给视图。 我们还将在ViewBag中创建一个名为Title的动态属性,以便在 View 中显示此值:

[HttpGet]
public IActionResult ValidateAge()
{
  ViewBag.Title = "Validate Age for voting";
  Person person1 = new Person();
  return View(person1);
}

在视图中,使用以下 HTML 帮助程序创建form:

@model Chapter4.Models.Person
@using (Html.BeginForm("ValidateAge", "Home", FormMethod.Post))
 {
   <table>
     <tr>
       <td>@Html.LabelFor(Model => Model.Name) </td>
       <td>@Html.TextBoxFor(Model => Model.Name) </td>
     </tr>
     <tr>
       <td>@Html.LabelFor(Model => Model.Age)</td>
       <td>@Html.TextBoxFor(Model => Model.Age)</td>
     </tr>
     <tr>
       <td colspan="2"><input type="submit" value="Submit" /></td>
     </tr>
   </table>
}

在第一行中,我们告诉视图,我们正在传递类型为Person类的 Model。 这使您能够使用 Model 的强类型,也就是说,当您键入 Model 和一个点时,智能感知将为您提供Person类的所有属性

在第二行中,我们使用重载的BeginFormHTML 帮助程序,它接受三个参数——动作方法名称、控制器名称和Form方法。

简单地说,当用户提交表单时,信息应该传递给控制器所提到的操作。

LabelForTextBox中,对于 HTML 帮助程序,我们只是传递模型属性(名称和年龄); 它会自动查询和获取 Model 属性,并构建相应的 HTML 元素。 这是使用 HTML 帮助程序的主要优点之一。 如果不使用 HTML 帮助程序,这个过程可能会变得复杂。

现在,让我们用同样的方式写出各自的POST动作方法。 在下面的POST动作方法中,我们根据表单中输入的年龄,将动态属性设置为Message

[HttpPost]
public IActionResult ValidateAge(Person person1)
{
  if(person1.Age>=18)
  {
    ViewBag.Message = "You are eligible to Vote!";
  }
  else
  {
    ViewBag.Message = "Sorry.You are not old enough to vote!";
  }
  return View();
}

需要注意的是,GETPOST动作方法都是指同一个视图——ValidateAge.cshtml。 将以下内容添加到表单元素上方的 View 中:

@if(ViewBag.Message!=null)
 {
   <b>@ViewBag.Message</b>
 }

一旦用户提交表单,POST操作方法将在ViewBag中设置动态Message属性。 但是,当视图作为GET请求的一部分呈现时,此属性的值将为空。 如果值不为空,则在页面顶部插入消息。

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

HTML Helpers

局部视图

分部视图只是可以在应用中重用的视图。 分部视图可以被认为是可插入的可重用块,您可以在任何地方调用它,并显示分部视图的内容。

考虑下面的网页结构——它与我们之前使用的布局页面相同,但有一些变化。 Latest Newsblock 添加到Side Section中,Loginblock 添加到Top Section中。 这些区块不限于Top SectionSide Section,可以在您的应用的任何地方使用,包括您的Content Section,如下图所示:

Partial View

这些 Partial view 不限于静态内容,可以包含form元素。 在上面的截图中,Latest News分部视图包含文本内容,登录分部视图包含form元素,用于获取电子邮件 ID 和密码。

分部视图的位置-框架不限制分部视图的位置。 但是,按照惯例,如果您的分部视图将只被您的控制器使用,您可以在特定于控制器的视图文件夹中创建该分部视图。 例如,如果您的分部视图将只在HomeController文件中使用,您可以在Views\Home文件夹中创建该分部视图。

让我们看看如何创建并使用分部视图。

正如前面讨论的,分部视图就像普通视图一样。 我们创建分部视图的方式和创建普通视图的方式一样。

右键单击Shared文件夹,选择添加|新项目。 按照约定,像所有共享内容一样,分部视图的名称也将以“_”(下划线)开头,如下截图所示:

Partial View

提示

我们创建这个分部视图的假设是,它可以从应用的任何地方使用。

在生成的 Partial View 中,我添加了以下简单的静态内容—一个文本和一个简单的表:

<b>This content and below table is coming from partial view</b>
<table border="1"> 
  <tr>
    <th>Employee No</th>
    <th>Employee Name</th>
  </tr>
  <tr> 
    <td>10012</td>
    <td>Jon Skeet</td>
  </tr>
  <tr>
    <td>10013</td>
    <td>Scott Guthrie</td>
  </tr>
</table>

调用局部视图

可以使用@Html.PartialHTML helper 调用分部视图。

在本例中,我们将从Index2.cshtml文件调用分部视图。 传递的参数将是部分文件的名称。 它将通过该名称搜索分部视图,并将该完整内容作为Index2.cshtml文件的一部分呈现。

Index2.html文件内容如下:

Hello. This text will be rendered in body of the layout page<br/> <br/> <br/>

@Html.Partial("_PartialHelloWorld")

现在,运行应用并访问 URLhttp://localhost:50132/Home/Index2。 你会看到以下输出:

Calling the Partial View

查看组件

视图组件是在 ASP 中引入的一个新特性.NET Core,它们几乎类似于分部视图,但更强大。 当你使用分部视图时,你有对控制器的依赖。 然而,当您使用ViewComponent属性时,您不必依赖于 Controller,因此我们将建立关注点分离,并具有更好的可测试性。 尽管仍然支持现有的 Partial View HTML helper,但在使用。net Core 时,当您想要显示可重用的信息片段时,最好使用 View 组件。

创建 View 组件

您可以使用以下任何一种方法创建一个ViewComponent:

  • 通过派生ViewComponent属性来创建一个类
  • [ViewComponent]属性修饰一个类,或者从具有[ViewComponent]属性的类派生它
  • 您可以通过创建以后缀ViewComponent属性结尾的类来使用该约定

无论您选择什么选项,这个ViewComponent都应该是公共的、非嵌套的、非抽象的类。

与控制器一样,你也可以在ViewComponent属性中使用依赖注入(通过构造函数)。 由于ViewComponent属性与 Controller 生命周期是分开的,因此您可能无法使用ViewComponents中的动作过滤器。

有一个名为Invoke(或InvokeAync,即Invoke的异步对等体)的方法,它将返回IComponentViewResult接口。 这个方法类似于控制器返回视图的动作方法。

让我们创建一个ViewComponent属性。

在项目中创建一个名为ViewComponents的新文件夹和一个名为SimpleViewComponent的新类,如下截图所示:

Creating a View component

我们创建的SimpleViewComponent文件如下所示:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;

namespace Chapter4.ViewComponents
{
  public class SimpleViewComponent :ViewComponent
  {
    public IViewComponentResult Invoke()
    {
      var data = GetSampleData();
      return View(data);
    }
    /// <summary>
    /// This is a simple private method to return some dummy data
    /// </summary>
    /// <returns></returns>

    private List<string> GetSampleData()
    {
      List<string> data = new List<string>();
      data.Add("One");
      data.Add("Two");
      data.Add("Three");
      return data;
    }
  }
}

我们只有两个方法,一个用于填充数据,另一个是用于渲染视图的Invoke方法。

一旦您创建了ViewComponent属性,您将需要在Views\_ViewImports.cshtml文件中包含ViewComponent名称空间,以便ViewComponents属性可以用于所有视图。 下面高亮显示的代码片段被添加到视图:

@using Chapter4
@using Chapter4.Models

@using Chapter4.ViewComponents 

@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"

我们已经创建了ViewComponent,并让所有视图都可以使用它们。 HomeController文件中的一个简单操作方法只返回视图:

public ActionResult Sample()
{
return View();
}

在关联的视图中,我们可以调用如下代码片段所示的组件:

<p>
  This is a sample web page <br/>
  <div>
    @Component.Invoke("Simple")
  </div> 
</p>

当你调用组件时,它会在以下两个文件夹中搜索:

  • Views\<controller_name>\Components\<view component name>\<view name>文件夹
  • Views\Shared\Components\<view_component_name>/<view_name>文件夹

视图组件的默认视图名是Default,这使得视图的文件名为Default.cshtml。 所以我们需要在Views\Shared\Simple\Default.cshtml文件夹中创建Default.cshtml文件,如下截图所示:

Creating a View component

ViewComponent文件的 View(Default.cshtml文件)中,我们只是在模型中迭代项目,并将它们作为无序列表项显示,如下代码所示:

@model IEnumerable<string>

<h3> Sample list</h3>
<ul>
  @foreach(var item in Model)
  {
    <li>@item</li>
  }
</ul>

当您运行应用并访问 URL(http://localhost:50132/Home/Sample)时,您应该会看到以下输出:

Creating a View component

第一行这是一个示例 web 页面,来自父视图文件(sample.cshtml),而随后的列表来自ViewComponent属性。

ViewComponent属性通常在视图中引用。 但是,如果您想直接从控制器调用ViewComponent,您可以这样做。

我已经调用了Sample动作方法来直接调用 SimpleViewComponent,而不是通过其他视图调用它,如下所示:

public ActionResult Sample()
{
  return ViewComponent("Simple");
  //return View();
}

Creating a View component

因此,与旧的 HTML 部分视图相比,这些ViewComponents具有更多的灵活性和特性,比如依赖注入。 这确保了ViewComponents是单独测试的。

标签助手

标签助手是 ASP 的一个新特性。 核心网; 它们帮助生成 HTML 元素。 在 HTML 助手中,我们将编写一个 c# /Razor 代码来生成 HTML。 这种方法的缺点是许多前端工程师不知道 c# /Razor 代码。 它们使用纯 HTML、CSS 和 JavaScript。 标记助手看起来就像 HTML 代码,但具有服务器端呈现的所有特性。 您甚至可以根据需要构建自定义的 Tag Helper。

让我们看看如何使用标签助手。 为了使用 Tag helper,您需要安装Microsoft.AspNet.Mvc.TagHelpersNuGet 包。

选择View|Other Windows|Package Manager Console打开Package Manager Console窗口,如下图所示:

Tag Helpers

您可以在包管理器控制台窗口中输入以下命令安装TagHelpers方法:

Install-Package Microsoft.AspNet.Mvc.TagHelpers -Pre

当您输入该命令时,将出现以下响应:

Tag Helpers

一旦安装了TagHelpers包,我们将需要调用ViewImports文件,我们将在其中添加TagHelpers指令,以便 Tag Helpers 对我们的视图可用。

右键单击Views文件夹,在上下文菜单中选择添加新项目选项; 您将看到以下屏幕:

Tag Helpers

_ViewImports.cs文件中添加以下内容。 前两行告诉 ASP.NET MVC 中包含必要的名称空间。 最后一行告诉 ASP.NET MVC 包括Microsoft.AspNet.Mvc.TagHelpers中所有可用的TagHelpers。 第一个参数表示 TagHelper 的名称。 我们已经使用了*,这意味着我们可能想要使用所有的 Tag Helpers。 第二个参数表示TagHelpers将可用的程序集:

@using Chapter4
@using Chapter4.Models
@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"

当我们直接在Views文件夹下创建_ViewImports.cshtml文件时,如下截图所示,所有视图都可以使用标签助手:

Tag Helpers

如果我们将_ViewImports.cshtml文件包含在Home文件夹下,那么 Tag Helpers 将只对Home文件夹下的 Views 可用。

让我们在HomeController文件中添加一个简单的操作方法Index3,在关联的视图中,我们将使用 Tag Helpers,如下代码所示:

public IActionResult Index3()
{
  ViewBag.Title = "This is Index3";
  Person person = new Person();
  return View(person);
}

用下面的代码为Index3操作方法添加相应的 View(Index3.cshtml文件):

@model Chapter4.Models.Person
<form asp-controller="Home" asp-action="Index3"> 
  <table>
    <tr> 
      <td><
labelasp-for

="Name"></
label

></td>
      <td><input asp-for="Name" /></td> 
    </tr>
    <tr>
      <td><
labelasp-for

="Age"></
label

></td>
      <td><
inputasp-for

="Age" /></td>
    </tr>
    <tr>
      <td colspan="2"><input type="submit" value="Submit" /></td>
    </tr> 
  </table>
</
form

>

以下是在前面的代码中使用标签助手时需要注意的一些事情:

  • 所有表单元素看起来就像标准的 HTML 元素,只是在属性上做了一些更改。 这使得前端开发人员可以独立工作,无需学习 HTML/Razor 代码,从而更容易实现所关注的分离。
  • 前一个视图的第一行指示了从控制器传递给视图的模型数据的类型。
  • Form 元素有两个属性asp-controllerasp-action,分别表示控制器名称和动作方法名称。
  • Label 和输入标记帮助程序就像 HTML 元素一样,只是有一个额外的asp-for属性。 这些属性的值表示模型属性。 在为这些属性输入值时,你可以利用智能感知。

创建自定义标签助手

ASP.NET Core 提供了许多内置的 Tag Helpers 来帮助您为许多场景创建必要的 HTML 元素。 然而,这个过程不是全面的,是彻底的。 有时,您可能希望对生成的 HTML 元素进行一些更改,或者您可能希望创建一个具有新属性的 HTML 元素,或者完全创建一个新的 HTML 元素。 您不受限于只使用 ASP 中现有的标记帮助程序.NET Core 应用。 如果现有的标签助手不适合您的需要,您可以创建自己的标签助手。 让我们创建一个简单的 Tag Helper 来创建一个电子邮件链接:

<a href="mailto:mugil@dotnetodyssey.com"> 

有两种方法可以创建 Tag Helpers 来实现ITagHelper接口或继承TagHelper类。 TagHelper类有一个Process方法,您可以重写该方法来编写自定义的标记助手。 类还具有TagHelperOutput参数,您可以使用该参数编写和生成所需的输出 HTML。 因此,最好通过继承TagHelper类来创建 Tag Helpers。

我们的目标是编写一个自定义的电子邮件标签助手,以便当有人使用这个标签助手(即<email mailTo="mugil@greatestretailstore.com"></email>)时,它应该转换为以下代码行:

<a href="mailto:mugil@greatestretailstore.com">Drop us a mail</a> 

下面是在 ASP 中创建自定义标记 Helper 时需要执行的步骤.NET Core 应用。

创建一个名为TagHelper的文件夹,并添加一个名为EmailTagHelper.cs的新项。 按照惯例,所有的 Tag Helpers 类都应该以TagHelper结尾,尽管我们可以重写该惯例。

Creating custom Tag Helpers

一旦你创建了文件,你将需要覆盖Process方法来生成所需的 HTML 输出:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Razor.TagHelpers;

namespace Chapter4.TagHelpers
{
  public class EmailTagHelper : TagHelper
  {
    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
      string emailTo= context.AllAttributes["mailTo"].Value.ToString();
      output.TagName = "a";
      output.Attributes["href"] = "mailto:" + emailTo;
      output.Content.SetContent("Drop us a mail");
    }
  }
}

上述代码中使用的参数解释如下:

  • 参数将为您提供在 Tag Helper 中提供的所有信息。 例如,在<emailmailTo="mugil@greatestretailstore.com"></email>标签 Helper 中,您可以从context参数中获得mailTo属性及其关联值。 在前面的Process方法的第一行中,我们将获得mailTo属性值,并使用该值在生成的 HTML(锚标记)中创建一个属性。
  • 参数output的类型是TagHelperOutput,它用于生成所需的 HTML 输出。
  • 参数output.Content.SetContent将设置锚标记显示的文本。

我们已经创建了电子邮件标签助手。 现在,我们必须让它对视图可用,这样我们才能在视图中使用标签 Helper。 编辑Views\_ViewImports.cshtml以包含TagHelpers的名称空间,并添加相关的TagHelpers。 在下面的_ViewImports.cshtml文件中,我们添加了粗体显示的内容:

@using Chapter4
@using Chapter4.Models

@using Chapter4.TagHelpers 

@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers" 

@addTagHelper "*, Chapter4"

下面一行中的“*”符号告诉视图引擎在Chapter4命名空间中包含所有的 TagHelpers:

@addTagHelper "*, Chapter4"

例如,以下一行将只包含EmailTagHelper,因此它对我们的视图可用:

@addTagHelper "Chapter4.TagHelpers.EmailTagHelper, Chapter4" 

让我们在主控制器中创建一个简单的操作方法。 在关联操作方法的视图中,我们将使用电子邮件标签助手:

public IActionResult AboutUs()
{
  return View();
}

以下是前文AboutUs动作方法的观点:

<h3>About Us</h3>
We are one of the biggest electronics retail store serving millions of people across the nation. blah.blah. blah <br/>

If you want to hear great offers from us 
<email mailTo="mugil@greatestretailstore.com"></email>

当您运行应用并访问http://localhost:50132/Home/AboutUsURL 时,您将看到以下输出:

Creating custom Tag Helpers

在这里,我们创建了一个锚标记,其属性为mailto,而 email 值为href属性值。

我已经打开了Developer Tools窗口(按F12来做这个,然后选择DOM Explorer选项卡)来查看生成的 HTML。

总结

在本章中,您学习了什么是视图引擎,以及如何使用 Razor 视图引擎构建视图。 我们还讨论了在 Razor 中可以用来生成所需 HTML 输出的不同编程结构。 然后,您学习了布局以及如何在 ASP 的所有页面上提供一致的站点结构。 净 MVC 应用。 稍后,我们通过一个例子讨论了如何使用分部视图促进可重用性。 最后,您学习了如何使用 Tag Helpers 来生成干净的 HTML。

五、模型

数据是每个应用的核心。 用户在应用中输入数据,编辑输入的数据,并搜索数据。 我们甚至可以说,我们构建的应用只是我们对应用数据执行操作的接口。 因此,任何框架都必须提供一种机制来更容易和更易于管理地处理数据操作。 模型在 ASP.NET MVC 用于表示业务域数据。

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

  • 模型及其目的
  • 创建一个简单的模型并在控制器和视图中使用它。 净 MVC 应用
  • 创建一个特定于 View 模型的模型
  • ASP 中的数据流.NET MVC 应用中的模型与ViewModels
  • 实体框架的目的及其特性和好处
  • 使用实体框架添加、更新和删除数据
  • 在 ASP 中使用实体框架。 净 MVC 应用

型号

模型是简单的POCO(普通的旧 c#对象)类,代表您的业务领域数据。 对于电子商务企业,模型类将是ProductOrderInventory。 如果你正在创建一个大学申请,典型课程将是StudentTeacher,Subject。 模型表示应用中的业务领域数据,它们不知道应用中使用的底层数据库。 事实上,您甚至不需要数据库来处理模型。

它们可以表示存储在 XML 文件或 CSV 文件或应用中的任何其他数据中的数据。 话虽如此,这些模型可以用于与数据库交互(在大多数情况下),但它们与数据库没有任何依赖关系。

下面的步骤描述了如何创建一个 ASP.NET Core 应用,使用模型:

  1. 确保创建一个 ASP.NET 5 应用与一个空模板。 安装 ASP.NET CoreNuGet包并配置它,就像前面一章中讨论的那样。
  2. 创建一个Controllers文件夹,并使用单个Index操作方法创建一个HomeController
  3. Views模型创建以下文件夹/文件:
    • Views:此文件夹在您的项目中。
    • Views\_ViewStart.cshtml:标识Layout文件的名称。
    • Views\Shared文件夹:此文件夹保存了应用的所有共享视图组件。
    • Shared\_Layout.cshtml:该文件确定了您的 web 应用结构应该是什么样子。
    • Views\Home文件夹:此文件夹包含您HomeController的所有视图。
    • Views\Home\Index.cshtml:此视图对应HomeControllerIndex动作方法。

现在,我们已经创建了一个 ASP。 带有控制器和视图的 NET Core 应用。

让我们在应用中创建一个Models文件夹; 这个文件夹将包含所有的模型文件。 在真实的应用中,这个文件夹和各自的模型文件将驻留在不同的项目中。 为了简单起见,我们将Models文件夹及其文件保存在同一个项目中。

Models

让我们在Models文件夹中创建一个简单的模型类Product模型:

public class Product { 
  public int ProductId { get; set; } 
  public string Name { get; set; } 
  public decimal Price { get; set; } 
} 

这个Product模型类与其他 c#类没有什么不同,它包含了一些关于产品的属性。

更新HomeController中的Index操作方法以使用Product模型,如下面的代码片段所示。 我们正在构建模型数据并将模型数据传递给视图,以便它可以显示给用户。 然而,而不是建议在控制器的操作方法中构建 Model 数据,因为它违反了关注点分离。 为了简单起见,我们在操作方法中构建模型数据。

public IActionResult Index() { 
  /* Build the products model. It is NOT RECOMMENDED to build    models in Controller action methods  like this. In real world appication, these models and the    respective Data Access Layer(DAL) would  be in separate projects. We are creating it here to make things    simpler to explain */ 
  List<Product> Products = new List<Product> { 
    new Product { 
      Name = "Mobile Phone", 
      Price = 300 
    }, 
    new Product { 
      Name = "Laptop", 
      Price = 1000 
    }, 
    new Product { 
      Name = "Tablet", 
      Price = 600 
    } 
  }; 
  return View(Products); 
} 

更新相应的IndexView 方法,使用 Model 数据循环遍历每个产品,并将其显示为无序列表项。 第一行中的@model表示 Model 元数据; 传递给视图的数据类型。 for…each循环中的 Model 表示实际的数据本身,在我们的例子中是一个产品列表:

@model List<Chapter5.Models.Product> 

<ul> 
  @foreach (var Product in Model) { 
    <li>@Product.Name</li> 
  } 
</ul> 

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

Models

我们已经成功地创建了一个模型,并在控制器和视图中使用了它。

让我们创建一个比较复杂的 Model 类Order(Models文件夹中的Order.cs),它包含产品列表及其总量:

public class Order { 
  public int OrderId { get; set; } 
  public List<Product> Products { get; set; } 
  public decimal Total { get; set; } 
} 

Models

现在,我们必须更新Index动作方法来使用Order模型。 一旦我们构建了产品列表,我们就将产品列表分配给Order属性并计算订单的总成本。 这些计算通常作为业务层的一部分进行。 同样,为了简单起见,我们在操作中构建数据模型和计算; 在实际应用中不应该出现这种情况。

粗体突出显示的代码是我们在 action 方法中所做的更改:

public IActionResult Index() { 
  /* Build the products model. It is NOT RECOMMENDED to build    models in Controller action methods  like this. In real world appication, these models and the    respective Data Access Layer(DAL) would  be in separate projects. We are creating it here to make things    simpler to explain   */ 
  List<Product> Products = new List<Product> { 
    new Product { 
      Name = "Mobile Phone", 
      Price = 300 
    }, 
    new Product { 
      Name = "Laptop", 
      Price = 1000 
    }, 
    new Product { 
      Name = "Tablet", 
      Price = 600 
    } 
  }; 

 Order order = new Order(); 
  order.Products = Products; 
  order.Total = Products.Sum(product => product.Price); 

  return View(order);

} 

视图被更新以适应模型的更改。 模型元数据(@model)被更改,以表明 Order 信息被传递给 View,而不是产品列表。

然后,我们以表格格式显示产品列表。 请注意,所有的模型数据(在本例中是Order对象及其属性)都可以通过模型访问。 例如,Products类可以通过Model.Products访问,Total的值可以通过Model.Total获得:

@model Chapter5.Models.Order 

<table border="1"> 

  <tr> 
    <th>Product Name</th> 
    <th>Price</th> 
  </tr> 

  @foreach (var Product in Model.Products){ 
    <tr> 
      <td>@Product.Name</td> 
      <td>@Product.Price</td> 
    </tr> 
  } 
  <tr> 
    <td><b>Total</b></td> 
    <td><b>@Model.Total</b></td> 
  </tr> 
</table> 

当你运行应用时,你会看到以下输出:

Models

特定于 View 组件的模型

在某些情况下,您可能希望只更新大型模型中的一些属性,或者您可能希望基于一些模型创建一个新模型。 在这种情况下,最好创建一个特定于视图的新模型。

例如,让我们假设我们正在构建一个屏幕,您可以在其中更新产品的价格。 这个简单的屏幕可能只包含三个属性——产品 ID、产品名称和产品价格。 但是产品的 Model 可能包含 30 多个属性来保存产品的所有细节,比如制造商、颜色、大小和其他属性。 我们不需要发送包含所有属性的完整模型,而是可以创建一个新的模型,这个模型只包含几个属性——id、Name 和 Price。

ViewModels 的注意事项

ViewModels 是当你更新模型时,你的视图会自动更新的实体,反之亦然。 在许多在线文章中,甚至在一些书中,他们提到了ViewModels,而实际上他们试图指的是特定于 View的模型。

在 ViewModels 中,绑定有两种方式—当您更新模型或视图时,另一个将自动更新。 让我们考虑一个简单的例子; 表单的左侧有多个字段,右侧有打印预览。 在这种情况下,您在表单中实时输入的任何内容都将立即反映在右侧。 在这种情况下,您可以在键入时使用纯视图模型,您的 ViewModel 将被更新,而ViewModel将在右侧打印预览中被使用。 这些纯 viewmodel 被用于高级 JavaScript 框架中,如KnockoutAngularJS

特定于视图的模型中,我们只以一种方式从模型绑定到视图。 这里,我们将特定于模型的模型发送到视图,而不是通用的模型(它表示一个业务领域类)。

然而,在本书中,为了简洁起见,我们将把针对视图的模型称为ViewModel。 除非另有说明,否则应将所有 ViewModels 读为视图特定的模型。 所以,我犯了和其他作者一样的错误Note on ViewModels

与模型相关的数据流

下面的框图显示了 ASP 中的数据流.NET MVC 应用:

Data flow with respect to a Model

数据源表示应用数据。 应用数据可以驻留在任何地方——从成熟的 RDBMS(如 SQL 服务器)到简单的 Excel 电子表格,或者介于两者之间的任何东西。

模型模型表示应用的业务领域数据,且与所使用的数据源无关。 相同的模型可以用于不同的数据源。

我们可以使用视图中的模型来获取数据或呈现数据。 但是在某些视图中,您可能不需要模型的所有属性。 因此,我们不是将整个模型发送到视图,而是创建特定于视图的模型,并在视图中使用它们。 这样事情就简单多了。

下面是在 ASP 中存储或检索记录时发生的事件的高级序列.NET Core 使用模型:

  1. 用户在应用中的表单(使用视图创建)中输入数据。 表单中的字段不需要表示完整的模型,因为我们只需要在 model 中使用一些属性。
  2. 输入的数据被传递到模型绑定发生的控制器。 模型绑定是将输入到视图中的数据映射到模型或 ViewModel 的过程。
  3. 如果数据是在 ViewModel 中接收到的,那么我们将把 ViewModel 转换为 Model。
  4. 最后,将 Model 数据存储在数据源中。

到目前为止,我们在应用中一直只处理内存中的数据。 在几乎所有的实际应用中,某种形式的数据库将用于数据存储、访问和检索。 在下一节中,我们将讨论实体框架(Entity Framework, ORM 框架),它使。net 应用中的数据访问变得更简单。

模型绑定

模型绑定是将来自视图的模型数据映射到控制器中动作方法的 ViewModel 参数的过程。

让我们考虑一个简单的表单,它有两个表单字段:NameEmailID。 在表单提交时,这些值将映射到控制器的动作方法的ViewModel对象。 模型绑定负责这种映射。 Model 绑定器在表单字段、查询字符串和请求参数中查找匹配。

在前面的示例中,ModelBinder 可以毫无问题地提取具有这些属性的任何类。

由于下面的Person类包含NameEmailID属性,模型绑定器不会抱怨使用该模型映射表单中输入的值:

public class Person { 
  public string Name { get; set; } 
  public string EmailID { get; set; } 
} 

下面的代码片段展示了如何在 action 方法中使用Person类:

public ActionResult Add(Person p) { 
  return View(); 
} 

实体框架

实体框架是对象关系映射(ORM)框架,它允许开发人员直接处理特定于领域的对象以进行数据访问,而不是处理数据库查询。 这大大降低了应用中数据访问层的代码复杂度。

在讨论实体框架及其特性之前,让我们暂停一下,考虑一下在使用 ADO 时,当我们试图将一些信息保存到数据库时所遵循的步骤.NET:

  1. 构造业务域对象。
  2. 创建到数据库的连接。
  3. 打开连接。
  4. 创建命令对象和命令类型。
  5. 将业务域对象的属性添加到命令对象的参数中。
  6. 执行将数据保存到数据库中的命令。

我们必须按照前面提到的 6 个步骤来执行常见操作,比如将数据保存到数据库中。

如果你正在使用一个 ORM 框架,比如实体框架,你只需要三个步骤:

  1. 构造业务域对象。
  2. 为您的业务域对象创建DbContext类。 类的实例表示与数据库的会话。
  3. 使用DBContext类的实例将其保存到数据库中。

你可能想知道这怎么可能。

事实上,在后台,实体框架创建到数据库的连接,并执行查询以将业务域对象保存到数据库中。 为了简单起见,实体框架为您编写所有数据访问代码,以便您可以集中精力实现应用的业务功能,而不是编写数据库层代码。

实体框架是独立于 ASP 的.NET MVC

正如前面所讨论的,实体框架是一个用于访问数据的 ORM 框架,它独立于 ASP。 净 MVC。 实体框架可以在Windows Communication Foundation(WCF)服务、Web API 服务,甚至控制台应用中使用。 您可以在任何类型的应用中使用实体框架,并利用它来使用对象访问数据。 实体框架的概念和功能保持不变,与使用它的应用类型无关。

现在,我们将在控制台应用中使用实体框架。 这使我们能够专注于手头的任务,并演示实体框架的功能,而不是在 ASP 的样板代码上工作.NET Core 应用。 在本章后面的部分中,我们将把实体框架与 ASP 集成在一起.NET Core 应用。

针对 SQL server 的实体框架的最新版本是 7.0.0,在撰写本书时仍处于测试阶段。 EF7 (Entity Framework 7)与之前的版本(Entity Framework 6)相比,带来了显著的变化.NET 5 应用,因此我们将在本书中使用这个版本。

注意事项

我们需要一个数据库来解释实体框架的许多特性。 在继续之前,请在您的 PC 上安装 SQL Server 2014 Express。 安装 SQL Server 2014 Express 和 SQL Server Management Studio 的步骤说明在附录 A 中给出。

使用实体框架创建控制台应用

按照以下步骤创建一个简单的控制台应用:

  1. 选择文件|新项目,选择控制台应用

  2. Name the project and click on OK .

    Creating console applications with the Entity Framework

安装实体框架 7 NuGet 包

在您的应用中安装任何NuGet包有两种方法:

  • 使用NuGet包管理器
  • 使用包管理器控制台

使用 NuGet 包管理器

喜欢图形界面的人可以使用这个选项:

  1. Right-click on the console project and select Manage NuGet Packages from the context menu:

    Using the NuGet Package Manager

  2. Search for EntityFramework.MicrosoftSqlServer in the NuGet package and make sure the Include prerelease checkbox is checked. Click on Install once you select EntityFramework.MicrosoftSqlServer and select Latest pre-release 7.0.0-rc1-final (at the time of writing this book). You can select any latest version of Entity Framework 7:

    Using the NuGet Package Manager

  3. Once you click on Install , the NuGet package manager will ask you to review the changes. Click on OK :

    Using the NuGet Package Manager

  4. Click on I Accept in the License Acceptance window:

    Using the NuGet Package Manager

  5. Once you click on I Accept , it will install the Entity Framework with all its dependencies. In the Output window, you will get the Finished message once the installation is complete:

    Using the NuGet Package Manager

使用包管理控制台

要使用包管理器控制台安装NuGet包,请遵循以下步骤:

  1. Open the Package Manager Console window by selecting the menu option View | Other Windows | Package Manager Console .

    Using the Package Manager Console

  2. Type Install-Package EntityFramework.MicrosoftSqlServer - Pre in the Package Manager Console window as shown in the following screenshot:

    Using the Package Manager Console

  3. Once the installation is complete, a message, Successfully installed 'EntityFramework.MicrosoftSqlServer 7.0.0-rc1-final' , will be shown:

    Using the Package Manager Console

安装实体框架命令

为了执行迁移活动,我们需要安装实体框架命令包。 迁移包括数据库及其相关表的创建。 模式中的任何变化也将通过迁移来处理:

Installing Entity Framework commands

正如前面所讨论的,当我们使用实体框架时,我们需要遵循三个步骤来与数据库交互:

  1. 创建Model类。
  2. 为您的业务域对象创建DbContext类。 类的实例表示与数据库的会话。
  3. 构造业务领域对象并使用DBContext类的实例将其保存到数据库中。

让我们详细讨论前面的每个步骤,并尝试将对象保存到数据库中。

创建模型类

Model类是简单的 POCO 对象,可以与实体框架一起使用。

让我们为我们的业务领域对象创建一个 POCO 类,即本例中的Employee类。 我在控制台应用中创建了一个名为Employee.cs的新文件,其中包含以下内容。 这个Employee类包含雇员的一些属性,并且没有特殊的属性或字段来与实体框架一起工作。

让我们看看下面的代码片段:

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

按照惯例,如果属性名是IdClassName+Id,实体框架在创建数据库表时将其视为主键。

具有字符串数据类型的属性将被创建为类型为nvarchar(max)的字段。 但是,我们可以通过使用注释来覆盖这种行为,我们将在后面讨论。

创建 DbContext 类

DbContext类的实例表示到数据库的会话,而DbContext类为应用完成大部分繁重的数据访问。

通过命名的EmployeeDbContext创建一个新类,内容如下:

using Microsoft.Data.Entity; 
using System.Configuration; 

namespace ConsoleEF7 { 
  public class EmployeeDbContext : DbContext{ 
    public DbSet<Employee> Employees {get; set;} 

    protected override void OnConfiguring(DbContextOptionsBuilder      optionsBuilder) {string connectionString =        ConfigurationManager.ConnectionStrings       ["SqlServerExpress"].ConnectionString; 
      optionsBuilder.UseSqlServer(connectionString); 
      base.OnConfiguring(optionsBuilder); 
    } 
  } 
} 

使用App.Config配置它:

<?xml version="1.0" encoding="utf-8" ?> 
<configuration> 
  <startup> 
    <supportedRuntime version="v4.0"  
    sku=".NETFramework,Version=v4.6.1" /> 
  </startup> 
  <connectionStrings> 
    <add name="SqlServerExpress" connectionString="Data Source=     MUGIL-PC\SQLEXPRESS;Initial Catalog=EF7Console;Integrated      Security=True"/> 
  </connectionStrings> 
</configuration> 

在前面的代码片段中,有一些事情需要注意:

  • Microsoft.Data.Entity命名空间作为这个命名空间中可用的DbContext类。 我们的连接字符串可以在App.Config文件中找到。 为了阅读App.Config文件的内容,我们将ConfigurationManager类包含在System.Configuration中。
  • 为了使用DbContextAPI,必须创建一个继承自DbContext类的类,这样我们才能访问DbContextAPI 的方法。 我们创建了从DbContext类继承而来的EmployeeDbContext类。
  • DbSet是一个类,它允许对给定的实体类型执行实体框架操作。 我们需要为应用中使用的每个 Entity 类型创建DbSet对象。 在本例中,我们只使用了一个DbSet对象,因为我们正在处理Employee类。

创建迁移

迁移是记录数据库所有变更的过程。 Add-Migration是用于添加迁移的实体框架命令:

Create a migration

  1. Once you add the migration, you can revoke the changes by executing the  Remove-Migration Entity Framework command.

    Create a migration

    • This is what the migrations directory looks like:

      Create a migration

  2. Update the database by issuing the Entity Framework command Update-Database  , which updates the database tables as per the information available in the migration. As we have installed the EntityFramework.Commands package earlier, these commands will be available for the application:

    Create a migration

  3. Once you update the database, you can see the changes in the database by connecting to SQL Server Management Studio:

    Create a migration

  4. 执行数据库操作,将业务域对象保存在数据库中。 您可以手动创建数据库,或者,如果数据库不可用,它将为您创建一个。

方法更新为以下代码:

class Program { 
  static void Main(string[] args) { 
    AddEmployee(); 
  } 

  static void AddEmployee() { 
    using (var db = new EmployeeDbContext()) { 
      Employee employee= new Employee { 
        Designation = "Software Engineer", 
        Name = "Scott", 
        Salary = 5600 
      }; 

      db.Employees.Add(employee); 
      int recordsInserted = db.SaveChanges(); 
      Console.WriteLine("Number of records inserted:" +        recordsInserted); 
      Console.ReadLine();
    } 
  } 
} 

首先,我们构造业务领域对象。 然后,我们将构造的Employee对象添加到DbContext类的员工的DbSet。 最后,我们调用SaveChanges方法DbContextAPI,它将把所有挂起的更改保存到数据库中。

您可能想知道,当我们甚至没有给出连接字符串时,它如何将其保存到数据库。

让我们来讨论一下当我们运行程序时,在幕后发生了什么:

  • 当您更改任何DbSet集合时,实体框架将检查数据库是否存在。 如果不存在,则使用模式<Namespace of DbContextName>创建一个新的。 在本例中,将创建一个由EF6.EmployeeDbContext调用的数据库。
  • 然后,它为DbSet中声明的实体创建数据库表。 根据约定,实体框架使用 Entity 的复数形式作为表名。 由于我们为Employee实体声明了DbSet,实体框架创建了Employee的复数形式,并创建了名为Employees的表。

数据库和表的创建发生在执行以下代码时:

db.Employees.Add(employee); 

当执行SaveChanges方法时,Employee对象中的数据将被保存到数据库中,并返回受影响的记录数量。 在前面的例子中,它返回1

当再次运行应用时,前面提到的前两个步骤将被跳过,因为已经创建了数据库和表。

查询数据库时,可以看到新插入的记录:

Create a migration

savchanges 方法的工作原理

当我们进行更改时,实体框架跟踪每个对象的状态,并在调用SaveChanges方法时执行适当的查询。

例如,当我们向雇员集合(DbSet)添加一个Employee对象时,该对象将被跟踪为处于Added状态的Entity。 当调用SaveChanges时,实体框架为其创建一个insert查询并执行该查询。 更新和删除对象的情况也是如此。 实体框架将各自对象的Entity状态设置为ModifiedDeleted。 当调用SaveChanges时,它创建并执行UpdateDelete查询。

How the SaveChanges method works

上图说明了SaveChanges方法在不同类型的变更的高层中是如何工作的。 我们有两个 POCO 对象(对象 1对象 2),它们已经被添加到员工的DbSet对象中。 让我们假设对象 3对象 4已经被修改,而对象对象 5对象 6处于Deleted状态。 当调用SaveChanges方法时,它将创建三组查询。 第一组查询用于添加对象,结果是对数据库执行insert查询。 在第二组查询中,将为其状态被修改的对象创建并执行Update查询。 最后,对所有Deleted状态对象的对象执行Delete查询。

更新记录

让我们尝试使用实体框架更新插入的员工记录的工资:

static void UpdateSalary() { 
  using (var db = new EmployeeDbContext()){ 
    Employee employee = db.Employees.Where(emp => emp.EmployeeId      == 1).FirstOrDefault(); 
    if(employee!=null){
      employee.Salary = 6500; 
      int recordsUpdated = db.SaveChanges(); 
      Console.WriteLine("Records updated:" + recordsUpdated); 
      Console.ReadLine(); 
    }
  } 
} 

在前面的方法中,我们找到了具有EmployeeId = 1的员工。 然后,我们将员工的工资更新为6500,并将employee对象保存到数据库中。 请注意,在前面的方法中,我们与数据库进行了多次交互——一次是查找正确的员工记录(read操作),另一次是更新记录(update操作)。

static void Main(string[] args){
  UpdateSalary(); 
} 

更新Main方法以调用UpdateSalary方法。 当你查询数据库时,你应该看到有更新信息的记录:

Updating the record

删除记录

删除记录有点棘手,因为它涉及直接设置状态。 在下面的方法中,首先我们获取对象,并将对象的状态设置为Deleted。 然后调用SaveChanges方法生成对象的delete查询,并执行该查询,从而最终删除数据库中的记录:

static void DeleteEmployee() { 
  using (var db = new EmployeeDbContext()) { 
    Employee employeeToBeDeleted = db.Employees.Where(emp =>      emp.EmployeeId == 1).FirstOrDefault(); 
    if (employeeToBeDeleted != null) { 
      db.Entry(employeeToBeDeleted).State =        Microsoft.Data.Entity.EntityState.Deleted; 
      int recordsDeleted = db.SaveChanges(); 
      Console.WriteLine("Number of records deleted:" +        recordsDeleted); 
      Console.ReadLine(); 
    } 
  } 
} 

static void Main(string[] args) { 
  DeleteEmployee(); 
} 

在 ASP 中使用实体框架。 净 MVC 应用

在控制台应用中使用实体框架和在 asp.net 中使用实体框架之间没有太大的区别。 净 MVC 应用。 现在,我们将构建一个简单的应用,它只有一个屏幕,如下图所示。 在这个屏幕中,我们将有一个表单,用户将在其中输入关于员工的信息; 一旦用户提交表单,信息将被保存到数据库中,并反映在以下截图中:

Using the Entity Framework in ASP.NET MVC applications

我们可以为员工创建一个简单的 Model。 我们需要为这个视图构建一个 ViewModel,因为我们需要从用户那里获得员工信息,我们还需要在同一个屏幕上显示员工列表。

让我们创建一个 ASP.NET Core 应用,添加员工并显示员工列表。 以下是为上述目标创建应用的逐步说明:

  1. 创建一个 ASP。 在 Visual Studio 中选择一个空的 asp.NET Core 项目。 净 5 应用。

  2. 安装 ASP.NET CoreNuGet包。

  3. 安装实体框架 7NuGet包和 **ef** EntityFramework 命令(用于数据库迁移),如本章前面所述。

  4. 添加config.json来声明数据库的连接字符串:

    { 
      "Data": { 
        "DefaultConnection": { 
          "ConnectionString": "Data Source=MUGIL-PC\\SQLEXPRESS;Initial Catalog=Validation;Integrated Security=True" 
        } 
      } 
    } 
    
    
  5. 更新project.json以包含 EntityFramework 7 和 EntityFramework 命令。 更改以粗体显示:

    { 
      "version": "1.0.0-*", 
      "compilationOptions":{ 
        "emitEntryPoint": true 
      }, 
    
      "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", 
    
    "EntityFramework.MicrosoftSqlServer":      "7.0.0-rc1-final", 
        "EntityFramework.Commands": "7.0.0-rc1-final"
    
      }, 
    
      "commands": { 
        "web": "Microsoft.AspNet.Server.Kestrel", 
    
    "ef": "EntityFramework.Commands"
    
      }, 
    
      "frameworks": { 
        "dnx451": { }, 
        "dnxcore50": { } 
      }, 
    
      "exclude": [ 
        "wwwroot", 
        "node_modules" 
      ], 
      "publishExclude": [ 
        "**.user", 
        "**.vspscc" 
      ] 
    } 
    
    
  6. Startup类(Startup.cs)中配置 MVC:

    • 在构造函数中,我们通过读取config.json文件来构建配置

    • 将 MVC 服务和实体框架服务添加到ConfigureServices方法中的服务中

    • Configure方式配置 MVC 路由:

      using Microsoft.AspNet.Builder;
      using Microsoft.AspNet.Hosting;
      using Microsoft.AspNet.Http;
      using Microsoft.Extensions.DependencyInjection;
      using Microsoft.Extensions.Configuration;
      using Validation.Models;
      using Microsoft.Data.Entity;
      using Microsoft.Extensions.PlatformAbstractions;
      
      namespace Validation {
       public class Startup {
       public IConfigurationRoot Configuration { get; set; }
      
      public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv) {
       var builder = new ConfigurationBuilder()
       .AddJsonFile("config.json")
       .AddEnvironmentVariables();
       Configuration = builder.Build();
       }
      
      // This method gets called by the runtime. Use this method to add services to the container. 
      // For more information on how to configure your application, visit http
      ://go.microsoft.com/fwlink/?LinkID=398940 
      
      public void ConfigureServices(IServiceCollection services) {
      services.AddEntityFramework()
       .AddSqlServer()
       .AddDbContext<EmployeeDbContext>(options => {
       options.UseSqlServer(Configuration.Get<string> ("Data:DefaultConnection:ConnectionString"));
       });
       services.AddMvc();
       }
      // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
      
      public void Configure(IApplicationBuilder app) {
       app.UseIISPlatformHandler();
       app.UseMvc(routes => {
       routes.MapRoute(
       name: "default",
       template: "{controller=Employee}/ {action=Index}/{id?}");
       });
       }
      // Entry point for the application.
       public static void Main(string[] args) => WebApplication.Run<Startup>(args);
       }
       }
      
  7. 创建ModelsDbContext类。

  8. 创建Models文件夹,并添加Employee模型类和EmployeeDbContext类。

  9. 创建Employee模型类(Employee.csModels文件夹中):

    public class Employee { 
      public int EmployeeId { get; set; } 
      public string Name { get; set; } 
      public string Designation { get; set; } 
      public decimal Salary { get; set; } 
    } 
    
    
  10. 创建EmployeeDbContext(EmployeeDbContext.csModels文件夹中):

```
using Microsoft.Data.Entity; 
using Microsoft.Extensions.Configuration; 

namespace Validation.Models { 
  public class EmployeeDbContext : DbContext { 

    public IConfigurationRoot Configuration { get; set; } 

    public DbSet<Employee> Employees { get; set; } 

    public EmployeeDbContext() { 
      var builder = new ConfigurationBuilder() 
      .AddJsonFile("config.json") 
      .AddEnvironmentVariables(); 
      Configuration = builder.Build(); 
    } 

    protected override void OnConfiguring     (DbContextOptionsBuilder optionsBuilder) {      optionsBuilder.UseSqlServer       (Configuration.Get<string>       ("Data:DefaultConnection:ConnectionString")); 
      base.OnConfiguring(optionsBuilder); 
    } 
  } 
} 

```
  1. 创建 T0:
    * 由于我们将显示员工列表和在同一个屏幕中添加员工的表单,所以我们将构建一个特定于此视图的模型。 该模型将包含关于雇员列表和要添加的雇员的信息。
  2. 创建ViewModels文件夹并添加EmployeeAddViewModel:
```
using MVCEF7.Models; 

namespace MVCEF7.ViewModels { 
  public class EmployeeAddViewModel { 
    public List<Employee> EmployeesList { get; set; } 
    public Employee NewEmployee { get; set; } 
  } 
} 

```

*   这个`ViewModel`有几个属性。 `EmployeesList`和`NewEmployee`。 `EmployeesList`将包含员工列表。 这个列表将从数据库中获取。 `NewEmployee`将保存用户输入的员工信息。
  1. 创建Controllers来处理传入的请求:
    * 创建一个Controllers文件夹,并添加EmployeeController类和两个操作方法——一个用于GET,另一个用于POST。 当您访问 URL(http://localhost/Employee/Index)或运行应用时,将调用与GET操作方法对应的Index操作方法。 当您按以下方式提交表单时,将调用POST Index动作方法:
    * 在前面的GET Index操作方法中,我们创建了ViewModel对象并将其传递给 View。
    * 下面的代码使用了POST Index动作方法:

    [HttpPost] 
    public IActionResult Index(EmployeeAddViewModel  employeeAddViewModel) { 
    
      using (var db = new EmployeeDbContext()) { 
        db.Employees.Add(employeeAddViewModel.NewEmployee); 
        db.SaveChanges(); 
        //Redirect to get Index GET method 
        return RedirectToAction("Index"); 
      } 
    
    } 
    
    
*   我们在 ViewModel 中获得了`NewEmployee`属性,它包含关于用户的信息。 保存到数据库中。 一旦我们将员工信息保存到数据库,并将控件重定向到`GET Index`操作方法,`GET Index`操作方法将再次显示表单,以表格格式输入员工信息和员工列表。
  1. 添加Views文件夹:
    1. 创建包含以下内容的Views\_ViewStart.cshtml
    2. 创建包含以下内容的Views\Shared\_Layout.cshtml
    3. 创建包含以下内容的Views\Employee\Index.cshtml

在前面的Index视图中,我们创建了一个表单,从最上面的div中获取用户的员工信息。 在下一个div中,我们以表格格式显示员工列表。

一旦我们创建了所有的文件夹和文件,项目结构应该如下所示:

Using the Entity Framework in ASP.NET MVC applications

数据库迁移

我们已经创建了业务实体——Employee类。 现在,我们可以继续迁移。 迁移过程分为两步:在第一步中,我们创建迁移文件。 这可以通过从项目上下文的命令提示符中执行以下命令来实现:

dnx ef migrations add InitialMigration

Database migration

这个命令将在你的项目中创建迁移文件,如下图所示:

Database migration

然后执行如下命令创建数据库:

Database migration

这个命令将读取在上一步中创建的迁移文件,并创建数据库和相关的表:

Database migration

运行应用。 您将看到以下屏幕,其中用户可以在表单中输入员工信息。 因为我们在视图中使用强类型模型,所以所有属性都采用默认值。 NameDesignation``string类型的属性,这些字段的默认值是空字符串,Salary``decimal类型,默认值为decimal0因此0所示的形式加载时【显示】字段。

由于没有记录,我们在List of employees表中显示0记录:

Database migration

当您在表单中输入信息并提交时,将信息保存到数据库中,Employees表中的所有数据库记录将显示如下:

Database migration

总结

在本章中,我们学习了什么是模型,以及它如何适应 ASP。 净 MVC 应用。 然后,我们创建一个简单的模型,在控制器中构建模型数据,将模型传递给视图,并使用视图显示数据。 我们已经了解了特定于视图的模型,并讨论了与模型相关的数据流。 我们学习了实体框架,这是微软的一个 ORM 框架,以及它如何简化。net 应用的数据库访问。 我们创建了简单的控制台应用,在其中插入、更新和删除了记录。 最后,我们建立了一个 ASP.NET Core 应用,使用模型、ViewModel 和实体框架。

六、验证

我们永远不能依赖用户输入的数据。 有时他们可能不知道应用,因此他们可能在不知情的情况下输入了不正确的数据。 在其他时候,一些恶意用户可能希望通过向应用中输入不适当的数据来破坏应用。 在这两种情况下,我们都需要在存储数据以进行进一步处理之前验证输入数据。

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

  • 不同类型的验证
  • 服务器端验证的示例
  • 一个示例的客户端验证
  • 不显眼的 JavaScript 验证使用 jQuery 不显眼的库,在这里我们不需要为验证编写单独的代码

在理想的情况下,用户将在应用中以适当的格式输入有效的数据。 但是,正如你可能意识到的,现实世界并不是那么理想。 用户将在应用中输入不正确的数据。 作为开发人员,验证应用中的用户输入是我们的责任。 如果输入的输入无效,您需要通知用户,说明哪里出错了,以便用户可以更正输入数据并再次提交表单。

验证可以在客户端、服务器端或两端进行。 如果在将数据发送到服务器之前进行了验证,则称为客户端验证。 例如,如果用户没有在强制字段中输入任何数据,我们可以在客户端验证表单(通过查找未输入的数据)。 不需要将表单数据发送到服务器。 JavaScript 是客户端验证最常用的语言。

Validation

如果在服务器端进行验证(将表单数据发送给服务器),则称为服务器端验证。 例如,您可能希望根据数据库中的数据验证用户输入的数据。 在这种情况下,最好是进行服务器端验证,因为我们不能在客户端拥有数据库中的所有数据。

Validation

客户端和服务器端验证

在现实世界中,这不是服务器端或客户端验证的问题。 我们可以同时拥有这两种类型的验证。 实际上,建议对两端的数据进行验证,以避免不必要的处理。

Client-side and server-side validation

上图显示验证在客户端和服务器端同时执行。 如果数据没有输入到必需的字段中,我们可以在客户端本身捕获该问题。 不需要将数据发送到服务器以最终发现没有输入数据。 一旦输入了所有必需的数据,数据就会被发送回服务器,以根据某些业务逻辑验证输入的数据。 如果验证失败,表单数据将与错误消息一起再次发送到浏览器,以便用户可以再次发送数据。

我们已经讨论了关于验证需求和应用中通常使用的验证类型的足够理论。 让我们亲自动手,为我们在前一章中构建的应用添加验证。

下面的截图是我们在上一章中建立的表单。 这个表单中没有什么奇特的东西——只有三个字段。 当用户在表单中输入数据时,数据被存储在数据库中,整个员工信息被提取回来并以表格格式显示。

Client-side and server-side validation

在我们构建的现有应用中,即使用户没有在任何字段中输入任何信息并提交信息,我们也不会向用户显示任何消息。 相反,我们默默地存储字段的默认值(字符串类型为空值,十进制类型为 0.00),如下面的截图所示:

Client-side and server-side validation

但情况不应该是这样。 我们应该通知用户输入的数据是无效的,并要求用户更正输入的数据。

服务器端验证

让我们继续上一章中构建的应用。 要进行服务器端验证,我们需要做以下工作:

  1. ViewModels模型类添加 Data Annotation 属性。 根据该元数据验证输入数据,并自动更新模型状态。
  2. 更新view方法以显示每个字段的验证消息。 带有asp-validation-for属性的span标记 helper 将用于显示验证错误消息。
  3. 更新控制器动作方法以验证模型状态。 如果模型状态是有效的,我们将数据插入数据库。 否则,将更新 View 模型,并使用验证错误消息再次呈现view方法,以便用户可以更新有效的输入数据并再次提交表单。

使用 Data Annotation 属性更新视图模型

Data Annotation 属性定义了Model/ViewModel属性的验证规则。 如果输入数据与模型中的属性定义不匹配,则验证将失败,从而使关联的模型状态无效。

有几个 Data Annotation 属性可用来验证数据。 以下是最常用的数据注释属性:

  • Required:该属性表示该属性是必需的。
  • Range:该属性定义最小和最大约束。
  • MinLength:这定义了一个属性成功验证所必须具有的最小长度。
  • MaxLength:顾名思义,该属性定义了属性的最大长度。 如果属性值的长度超过最大长度,则验证将失败。
  • regulareexpression:如果我们使用这个属性,我们可以使用一个正则表达式来验证数据。

由于 Data Annotation 属性在System.ComponentModel.DataAnnotations名称空间中可用,所以我们需要包含这个名称空间。 以下是更新后的视图模型代码:

using System; 
using System.Collections.Generic; 
using System.ComponentModel.DataAnnotations; 
using System.Linq; 
using System.Threading.Tasks; 
using Validation.Models;

namespace Validation.ViewModels 
{ 
    public class EmployeeAddViewModel 
    { 
        public List<Employee> EmployeesList { get; set; } 
        [Required(ErrorMessage ="Employee Name is required")] 
        public string Name { get; set; } 

        [Required(ErrorMessage ="Employee Designation is required")] 
        [MinLength(5, ErrorMessage = "Minimum length of designation should be 5 characters")] 
        public string Designation { get; set; } 

        [Required] 
        [Range(1000,9999.99)] 
        public decimal Salary { get; set; } 
    } 
} 

我们为所有三个属性——NameDesignationSalary添加了 Data Annotation 属性。

ErrorMessage属性在验证失败时显示一条消息。 如果验证失败,并且没有提到ErrorMessage,则会显示默认的错误消息。

更新 View 模型以显示验证错误消息

对于每个字段,我们都添加了一个span标记,当验证失败时,错误消息将以红色显示。 当验证成功时,将不会显示错误消息。 asp-validation-for的属性值表示必须为其显示验证错误消息的字段名。 例如,我们使用带有asp-validation-for属性和Name值的span标记,它告诉 ASP.NET MVC 显示Name字段的验证错误消息:

<form asp-controller="Employee" asp-action="Index"> 
        <table> 
            <tr> 
                <td><label asp-for="Name"></label></td> 
                <td><input asp-for="Name" /></td> 
                <td><span asp-validation-for="Name" style="color:red"></span></td> 
            </tr> 
            <tr> 
                <td><label asp-for="Designation"></label> </td> 
                <td><input asp-for="Designation" /></td> 
                <td><span asp-validation-for="Designation" style="color:red"></span> </td> 
            </tr> 
            <tr> 
                <td><label asp-for="Salary"></label></td> 
                <td><input asp-for="Salary"></td> 
                <td> <span asp-validation-for="Salary" style="color:red"></span> </td> 
            </tr> 
            <tr> 
                <td colspan="2"><input type="submit" id="submitbutton" value="Submit" /></td> 
            </tr> 
        </table> 
    </form> 

更新控制器动作方法,验证模型状态

模型状态会根据在 View 模型和输入数据上指定的 Data Annotation 属性自动更新。 我们在下面的Index方法中验证模型状态是否有效,这是一个POST动作方法。 如果模型状态是有效的(验证成功时),我们将输入的数据保存到数据库中。 如果验证失败,则将ModelState自动设置为invalid。 然后,我们将用输入的数据填充ViewModel并再次呈现View方法,以便用户可以更正输入数据并重新提交数据:

[HttpPost] 
    public IActionResult Index(EmployeeAddViewModel employeeAddViewModel) 
{ 
        if (ModelState.IsValid) 
        { 
            using (var db = new EmployeeDbContext()) 
            { 
                Employee newEmployee = new Employee 
                { 
                    Name = employeeAddViewModel.Name, 
                    Designation = employeeAddViewModel.Designation, 
                    Salary = employeeAddViewModel.Salary 
                }; 
                db.Employees.Add(newEmployee); 
                db.SaveChanges(); 
                //Redirect to get Index GET method 
                return RedirectToAction("Index"); 
            } 
        } 
        using (var db = new EmployeeDbContext()) 
        { 
            employeeAddViewModel.EmployeesList = db.Employees.ToList(); 
        } 
        return View(employeeAddViewModel); 
} 

当您在进行上述更改后运行应用并提交表单而不输入值时,错误消息将显示在如下截图所示的字段旁边。 请注意,即使在验证错误的情况下,我们也会在下表中显示员工的数据,这是通过使用前面代码片段中的代码块实现的。

Updating the controller action method to verify the model state

在之前的验证和它的错误消息中有一些事情需要注意:

  • 如果验证失败,将按预期显示错误消息。

  • 如果一个字段有多个验证,它将一次显示一条错误消息。 例如,我们对名称字段进行了两个验证——RequiredMinLength属性。 如果没有为该字段输入数据,则只显示必需的字段错误消息。 只有当必需的字段错误得到解决时(通过在字段中输入一些字符),才会显示第二个验证错误消息。

  • If no error message is available and if the validation fails, the default error message is displayed. We have not given any error message for the Salary field. So, when the validation fails for that field, ASP.NET MVC displays the default error message based on the field name and the type of validation failure.

    Updating the controller action method to verify the model state

上图描述了服务器端验证中事件的高级序列,描述如下:

  1. 用户输入无效数据。
  2. 基于 View 模型中的 Data Annotations 属性,模型状态会自动更新。 这发生在模型绑定过程中,其中view方法中的数据被映射到模型或 View 模型中的数据。
  3. 在控制器的动作方法中,我们验证了模型的状态。
  4. 如果模型状态是有效的,我们将输入的数据保存到数据库中。
  5. 如果模型状态无效,我们将使用验证错误消息再次分解 View 模型,以便用户可以更正输入数据并再次提交表单,并使用有效的输入数据。

客户端验证

在有些情况下,我们不需要访问服务器来验证输入数据。 在前面的服务器端验证示例中,我们不需要访问服务器来验证用户是否为Name字段输入了数据。 我们可以在客户端进行验证。 这可以防止往返于服务器,并减少服务器负载。

我们将使用 JavaScript 验证来自客户端的数据。 JavaScript 是一种高级的解释性语言,主要用于客户端编程。

注意事项

现在,JavaScript 也作为 Node.js 的一部分在服务器端使用。

我们将在 View 模型(Index.cshtml文件)中做一些更改,以在客户端验证表单:

  1. 表单的改变:将id属性添加到所有的span标签中,这样我们就可以访问这个 HTML 元素来显示 HTML 错误消息。 提交表单时,调用一个 JavaScript 函数来验证输入数据。
  2. 添加脚本 HTML 元素并创建一个 JavaScript 函数来验证输入数据。

在下面的代码中,我们将在表单提交时调用validateFormJavaScript 函数。 如果validateForm函数返回true,则数据将被发送到服务器。 否则,将不会发送数据。 我们为所有的span标签添加了id属性,这样我们就可以识别span标签并在那里显示验证错误消息:

<form asp-controller="Employee" asp-action="Index" onsubmit="return validateForm()"> 
        <table> 
            <tr> 
                <td><label asp-for="Name"></label></td> 
                <td><input asp-for="Name" /></td> 
                <td><span id="validationName" asp-validation-for="Name" style="color:red"></span></td> 
            </tr> 
            <tr> 
                <td><label asp-for="Designation"></label> </td> 
                <td><input asp-for="Designation" /></td> 
                <td><span id="validationDesignation" asp-validation-for="Designation" style="color:red"></span> </td> 
            </tr> 
            <tr> 
                <td><label asp-for="Salary"></label></td> 
                <td><input asp-for="Salary"></td> 
                <td> <span id="validationSalary" asp-validation-for="Salary" style="color:red"></span> </td> 
            </tr> 
            <tr> 
                <td colspan="2"><input type="submit" id="submitbutton" value="Submit" /></td> 
            </tr> 
        </table> 
</form> 

我们已经添加了 JavaScript 函数来验证所有三个字段。 我们得到所有三个字段的值,并将它们存储在单独的变量中。 然后验证每个变量的值是空还是空。 如果该值为空,则获取相应字段的span元素,并使用验证错误消息设置文本上下文:

<script type="text/javascript"> 
        function validateForm() { 
            var isValidForm = true; 
            var nameValue = document.getElementById("Name").value; 
            var designationValue = document.getElementById("Designation").value; 
            var salaryValue = document.getElementById("Salary").value; 

            //Validate the name field 
            if (nameValue == null || nameValue == "") { 
                document.getElementById("validationName").textContent = "Employee Name is required - from client side"; 
                isValidForm = false; 
            } 

            //validate the designation field 
            if (designationValue == null || designationValue == "") { 
                document.getElementById("validationDesignation").textContent = "Employee Designation is required - from client side"; 
                isValidForm = false; 
            } 

            //validate the salary field - if it is empty 
            if (salaryValue == null || salaryValue == "") { 
                document.getElementById("validationSalary").textContent = "Employee Salary is required - from client side"; 
                isValidForm = false; 
            }else if (Number(salaryValue) == NaN || Number(salaryValue)<=0.0) { 
                document.getElementById("validationSalary").textContent = "Please enter valid number for salary field - from client side"; 
                isValidForm = false; 
            } 

            return isValidForm; 

        } 

</script> 

当您运行应用并提交表单而不输入数据时,您将获得从客户端本身生成的错误消息,而无需访问服务器。

Client-side validation

在真实的应用中,我们不会在 JavaScript 中手工编写验证代码。 相反,大多数应用使用不引人注目的验证,其中我们不编写 JavaScript 代码来验证每个字段。 只需添加相应的 JavaScript 库就可以了。

您可能想知道在不编写代码的情况下如何验证字段。 其神奇之处在于基于 Data Annotation 属性添加到输入 HTML 元素的data-属性。 这个 jQuery 不显眼的库获得一个字段列表,为其添加了data-属性,并对其进行验证。

运行应用并按Ctrl+U查看源代码。 源代码看起来如下所示:

Client-side validation

不同的属性将被添加到不同类型的 Data Annotation 属性中。 对于要验证的字段,将data-val属性设置为true。 对于 View 模型中标记为必需的属性,data-val-required属性将具有相关属性的错误消息的值。

实现

会有一个布局文件(_Layout.cshtml)来定义您的 web 应用的布局结构。 由于 JavaScript 库将在所有页面中使用,所以这里是添加普通功能(如不引人注目的验证)的合适位置。 只需将 JavaScript 库(以粗体突出显示)添加到布局文件(_Layout.cshtml)中,这样它们就可以用于所有View文件:

<!DOCTYPE html> 
<html> 
<head> 
    <meta name="viewport" content="width=device-width" /> 
    <title>@ViewBag.Title</title> 
</head> 
<body> 
    <div> 
        @RenderBody() 
    </div> 

<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.2.3.js"></script> 
    <script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/jquery.validate.min.js"></script> 
    <script src="https://ajax.aspnetcdn.com/ajax/mvc/5.2.3/jquery.validate.unobtrusive.min.js"></script>

</body> 
</html> 

除了删除了我们之前为验证字段而编写的 JavaScript 函数外,对 View 模型没有任何更改。 视图的完整代码如下所示:

@model Validation.ViewModels.EmployeeAddViewModel 

<div> 

    <form asp-controller="Employee" asp-action="Index" method="post" role="form"> 
        <table> 
            <tr> 
                <td><label asp-for="Name"></label></td> 
                <td><input asp-for="Name" /></td> 
                <td><span id="validationName" asp-validation-for="Name" style="color:red"></span></td> 
            </tr> 
            <tr> 
                <td><label asp-for="Designation"></label> </td> 
                <td><input asp-for="Designation" /></td> 
                <td><span id="validationDesignation" asp-validation-for="Designation" style="color:red"></span> </td> 
            </tr> 
            <tr> 
                <td><label asp-for="Salary"></label></td> 
                <td><input asp-for="Salary"></td> 
                <td> <span id="validationSalary" asp-validation-for="Salary" style="color:red"></span> </td> 
            </tr> 
            <tr> 
                <td colspan="2"><input type="submit" id="submitbutton" value="Submit" /></td> 
            </tr> 

        </table> 
    </form> 

</div> 

<br /><br /> <br /> 

<b> List of employees:</b> <br /> 
<div> 
    <table border="1"> 
        <tr> 
            <th> ID </th> 
            <th> Name </th> 
            <th> Designation </th> 
            <th> Salary </th> 
        </tr> 
        @foreach (var employee in Model.EmployeesList) 
        { 
            <tr> 
                <td>@employee.EmployeeId</td> 
                <td>@employee.Name</td> 
                <td>@employee.Designation</td> 
                <td>@employee.Salary</td> 
            </tr> 
        } 
    </table> 

</div> 

Implementation

上图描述了不显眼的客户端验证过程:

  1. Model/ViewModels添加数据注解。
  2. 视图取Model/ViewModels并生成 HTML。
  3. 从 View 模型中生成的 HTML 包含data-*属性:
    • 对于设置了Required属性的字段,将以错误消息作为其值创建data-val-required属性。
    • 对于带有MinLengthData Annotation 属性的字段,将data-val-minlength属性设置为错误消息值。
    • 对于范围 Data Annotation,将data-val-range属性设置为错误消息值。 data-val-range-max表示范围中的最大值,data-val-range-min属性表示范围中的最小值。
  4. jQuery 不引人注目的验证库使用data-*属性读取这些元素,并执行客户端验证。 这允许开发人员不用使用 JavaScript 编写分离验证代码,因为一切都是由配置本身解决的。

总结

在本章中,我们了解了验证的需求以及可用的不同类型的验证。 我们甚至讨论了客户端和服务器端验证的工作方式,以及每种验证类型的优缺点。 稍后,我们对代码进行了更改,以在服务器端验证输入数据。 然后,我们使用 JavaScript 在客户端本身验证输入数据。 最后,我们使用 jQuery 不显眼的库进行客户端验证,而无需编写 JavaScript 代码在客户端验证输入数据。

在下一章中,我们将讨论路由原理以及如何定制它。 在前面的一章中,我们看到了在 ASP 中路由的基础知识。 净 5 应用。 现在我们要深入探讨这个问题。

七、路由

路由是 ASP 中重要的概念之一.NET MVC 应用,因为它负责处理传入的请求并将它们映射到适当的控制器操作。

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

  • 使用MapRoute方式配置路由
  • 带有示例的不同类型的路由-约定的和基于属性的
  • 在基于属性的路由中使用 HTTP 动词

我们在第 3 章、控制器中简要讨论了路由。 在本章中,我们将讨论路由以及在 ASP 中定制路由的几种可用选项。 净的核心。

基于约定的路由

路由引擎负责将传入请求映射到控制器的适当操作方法。

Startup类的Configure方法中,我们映射了以下路径:

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

MapRoute方法有两个参数:

  • name:表示路由的名称,因为我们可以为同一个应用配置多条路由。
  • template:表示路由的实际配置。 这个配置值有三个部分。 由于我们提供了默认参数,如果没有传递值,它将接受默认参数值。
  • {controller=Employee}:第一个值作为控制器的名称,当控制器的值在 URL 中不可用时,我们使用Employee控制器作为默认控制器。
  • {action=Index}:Index动作方法将作为默认的动作方法,URL 中的第二个参数将作为动作方法的名称。
  • {id?``}:通过在id参数后指定“?”,我们表示id是可选参数。 如果该值作为第三个参数传递,则将采用id值。 否则,它将不被考虑。

还有另一种方法具有相同的功能。 app.UseMvcWithDefaultRoute()方法配置路由"{controller=Employee}/{action=Index}/{id?}"。 但是我们已经使用了前面的方法来说明我们可以根据自己的需要定制路由。

让我们看几个例子并观察路由引擎是如何工作的。 对于前面的例子,我们假设下面的路由:

"{controller=Employee}/{action=Index}/{id?}" 

例 1

网址-http://localhost:49831/

在此 URL 中,我们没有为controlleractionid传递值。 因为我们没有传递任何东西,所以它将采用控制器和动作的默认值。 因此,路由引擎将 URL 转换为以下 URL:

http://localhost:49831/Employee/Index

示例 2

网址-http://localhost:49831/Employee/

在此 URL 中,我们传递了controller(第一个参数)的值,即Employee,而没有传递action方法(第二个参数)或id方法(第三个参数)的任何内容。 因此,通过取action方法的默认值,将 URL 转换为以下 URL:

http://localhost:49831/Employee/Index

示例 3

网址-http://localhost:49831/Manager/List

路由引擎将第一个参数Manager作为controller方法名,第二个参数List作为action方法名。

示例 4

网址-http://localhost:49831/Manager/Details/2

我们已经在这个 URL 中传递了所有三个参数。 因此,第一个参数值Manager将被视为controller方法名,第二个参数值将被视为action方法名,第三个参数值将被视为id方法名。

在定义地图路径时,我们使用了带有几个参数的MapRoute方法。 第一个参数name表示路由的名称,第二个参数template表示要匹配的 URL 模式和默认值:

routes.MapRoute(name: "default",
               template: "{controller=Employee}/{action=Index}/{id?}");

这个MapRoute方法还有其他重载的变体。 下面是另一个常用的重载MapRoute方法,其中传入的 URL 模式和默认值将用于不同的参数。 路由的名称为FirstRoute,该路由将应用于所有以Home开头的 url。 控制器和动作的默认值分别为HomeIndex2:

routes.MapRoute(name:"FirstRoute",
               template:"Home",
               defaults:new {controller ="Home", action="Index2"});

您可以为您的 ASP 定义任意数量的路由映射。 净 MVC 应用。 对路由图没有限制或限制。 让我们向应用添加另一个路由映射。 我们在应用中添加了另一个名为FirstRoute的路线图(用粗体突出显示):

public void Configure(IApplicationBuilder app) 
    { 
        app.UseIISPlatformHandler(); 
        app.UseMvc(routes => 
        { 

routes.MapRoute(name:"FirstRoute",
            template:"Home", defaults:new {controller ="Home", action="Index2"}); 

            routes.MapRoute(name: "default",
            template: "{controller=Employee}/{action=Index}/{id?}");
        }); 
    } 

我们还添加了另一个名为HomeControllercontroller方法,其中有两个简单的action方法返回不同的字符串:

public class HomeController : Controller 
    { 
        // GET: /<controller>/ 
        public IActionResult Index() 
        { 
            return Content("Index action method"); 
        } 

        public IActionResult Index2() 
        { 
            return Content("Index2 action method"); 
        } 
    } 

当您试图通过 URLhttp://localhost:49831/Hello访问应用时,路由映射FirstRoutedefault都与 URL 模式匹配。

您认为在这个场景中会应用哪种映射路由?

路由引擎根据以下因素映射传入的 URL:

  1. 匹配模式。
  2. 按照路由引擎中定义的顺序。

第一个因素很明显。 为了让路由引擎获取路由映射,传入 URL 的模式应该与路由映射中定义的模板匹配。

第二个因素很微妙但很重要。 如果有多个路由映射与传入的 URL 匹配,路由引擎将选择配置中定义的第一个 URL。 例如,如果传入的 URL 同时匹配FirstRoutedefault映射,路由引擎将选择在配置中首先定义的FirstRoute映射。

Example 4

如果路由引擎不能将传入的 URL 映射到任何映射路由,我们将得到一个HTTP 404 error,这意味着无法找到资源。 通过查看开发工具中的Network选项卡,可以看到状态(200 表示OK,404 表示未找到资源),如下图所示:

Example 4

基于属性的路由

到目前为止,我们一直使用基于约定的路由。 在基于约定的路由中,我们将路由模板(只是参数化的字符串)定义在一个集中的位置,这些模板适用于所有可用的控制器。 基于约定的路由的问题在于,如果我们想为不同的控制器定义不同的 URL 模式,我们需要定义一个对所有控制器都通用的自定义 URL 模式。 这使得事情变得困难。

还有另一个选项可以配置基于属性的路由引擎。 在基于属性的路由中,不是在一个集中的位置配置所有路由,而是在控制器级别进行配置。

让我们看一个基于属性路由的示例。

首先,让我们删除之前在startup.cs类文件中的Configure方法中创建的基于约定的路由:

public void Configure(IApplicationBuilder app) 
    { 
        app.UseIISPlatformHandler(); 
        app.UseMvc(); 
        //app.UseMvc(routes => 
        //{ 
        //    routes.MapRoute(name: "FirstRoute",
        //                    template: "Hello", 
        //                    defaults: new { controller = "Home",  
        //                    action = "Index2" }); 

        //    routes.MapRoute(name: "default",
        //                 template:"
        //                {controller=Employee}/{action=Index}/{id?}"); 
        //}); 
    } 

然后,我们可以在控制器本身配置路由。 在下面的代码中,我们为之前创建的home控制器添加了路由配置:

namespace Validation.Controllers 
{ 
    public class HomeController : Controller 
    { 
        // GET: /<controller>/ 
        [Route("Home")] 
        public IActionResult Index() 
        { 
            return Content("Index action method"); 
        } 
        [Route("Home/Index3")] 
        public IActionResult Index2() 
        { 
            return Content("Index2 action method"); 
        } 
    } 
} 

我们在控制器的action方法中使用了Route属性。 在Route属性中传递的值将充当 URL 模式。 例如,当我们访问 URLhttp://localhost:49831/Home/时,将调用HomeControllerIndex方法。 当我们访问 URLhttp://localhost:49831/Home/Index3时,将调用HomeControllerIndex2方法。 请注意,URL 模式和action方法名不需要匹配。 在前面的示例中,我们调用了Index2操作方法,但 URL 模式使用了Index3http://localhost:49831/Home/Index3

同时使用基于属性的路由和基于约定的路由时,优先使用基于属性的路由。

控制器级别的路由属性

您将注意到,对于action方法IndexIndex2的 URL 模式,我们在HomeHome/Index3两个 URL 模式中重复了控制器名称Home。 我们可以在controller级别定义它,而不是在action方法级别重复controller方法名称(或 URL 中的任何公共部分)。

在下面的代码中,URL 的公共部分(Home)在controller级别上定义,惟一部分在action方法级别上定义。 当 URL 模式被映射到控制器的action方法时,两个路由部分(在controller级别和action方法级别)被合并和匹配。 所以前面定义的路线和后面的路线没有区别。

如果你想在基于属性的路由中使用两个参数,你可以在花括号中传递它们。 在下面的例子中,我们对SayHello操作方法进行了这样的操作。

例如,URL 模式http://localhost:49831/Home/Index3仍然会映射到HomecontrollerIndex2方法:

namespace Validation.Controllers 
{     
    [Route("Home")] 
    public class HomeController : Controller 
    { 
        // GET: /<controller>/ 
        [Route("")] 
        public IActionResult Index() 
        { 
            return Content("Index action method"); 
        } 

        [Route("Index3")] 
        public IActionResult Index2() 
        { 
            return Content("Index2 action method"); 
        } 

 [Route("SayHello/{id}")] 
        public IActionResult SayHello(int id) 
        { 
            return Content("Say Hello action method"+id); 
        } 
    } 
} 

在控制器中以 HTTP 动作动词传递路由值

我们甚至可以通过 HTTP 动作动词(如HTTPGetHTTPPost)传递路由值,而不是通过Route属性传递路由值。

在下面的代码中,我们使用了HTTPGet属性来传递路由值。 对于Index方法,我们没有传递任何值,因此没有将路由值附加到controller方法级别定义的路由值上。 对于Index2方法,我们传递了Index3值,并且Index3将被附加到controller级别定义的路由值。 请注意,只有带有GET方法的 url 才会映射到action方法。 如果您使用POST方法访问相同的 URL 模式,这些路由将不会得到匹配,因此这些action方法将不会被调用。

namespace Validation.Controllers 
{     
    [Route("Home")] 
    public class HomeController : Controller 
    { 
        // GET: /<controller>/ 
        [HttpGet()] 
        public IActionResult Index() 
        { 
            return Content("Index action method"); 
        } 

        [HttpGet("Index3")] 
        public IActionResult Index2() 
        { 
            return Content("Index2 action method"); 
        } 
    } 
} 

路由约束

路由约束使您能够约束传递给控制器操作的值的类型。 例如,如果希望限制传递给int类型int的值,可以这样做。 下面就是这样一个例子:

[HttpGet("details/{id:int?}")] 
    public IActionResult Details(int id) 
    { 
      return View(); 
    } 

ASP。 净 5 (ASP.NET Core)甚至支持默认参数值,以便您可以传递默认参数:

[HttpGet("details/{id:int = 123}")] 
    public IActionResult Details(int id) 
    { 
      return View(); 
    } 

总结

在本章中,我们学习了路由及其工作原理。 我们了解了各种可用的路由。 我们通过不同的例子讨论了基于约定的路由和基于属性的路由。 我们还讨论了路由约束和可以传递的默认参数值。

在下一章中,我们将看到如何使应用看起来更好。

八、使用 Bootstrap 美化 ASP.NET MVC 应用

您可能已经创建了一个具有所有必需功能的应用。 它甚至可以在所有情况下完美地工作而没有任何问题。 但是应用的成功取决于用户访问它的能力。 您的应用应该看起来很好(如果不是很好的话),并且用户友好,这样才能成功。

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

  • HTML 和 CSS 在 ASP 中的作用.NET Core 应用
  • 前端框架的特点和不同的框架可用
  • Bootstrap 和它的网格系统沿其特性
  • Bootstrap 中用于表单元素(如输入和选择元素)的 CSS 类
  • CSS 类用于不同类型的 HTML 元素,比如 table
  • 使用 Bootstrap 在您的 ASP.NET Core 应用

在讨论如何使我们的应用看起来更好之前,让我们先回过头来讨论一下 HTML 和 CSS 在应用中所扮演的角色。

了解 HTML 和 CSS

如前所述,所有浏览器只能理解 HTML、CSS 和 JavaScript。 因此,您构建的应用应该生成 HTML、CSS 和 JavaScript 的输出。 对于使用 Java 或 Ruby on Rails 等其他技术构建的 web 应用来说,这是正确的。 话虽如此,我们只讨论 HTML 和 CSS。

HTML(超文本标记语言)是用来构建网页内容的。 例如,您可以在title标记中添加内容,这样它就可以在浏览器的选项卡或窗口中使用。 让我们看一个例子。

打开任何文本编辑器(甚至可以使用记事本),键入以下 HTML 内容并将保存文件设为Bootstrap.html。 请注意分机.html:

<!DOCTYPE html> 
<html> 
<head> 
    <title> Beautify your ASP.NET MVC applications using Bootstrap </title> 
</head> 
<body> 
    <h1> Bootstrap </h1> 
    <p> 
        Bootstrap is the most popular HTML, CSS, and JS framework for developing responsive, mobile first projects on the web. 
    </p> 
</body> 
</html> 

第一行告诉我们 HTML 内容遵循 HTML 5 标准(HTML 的最新和当前版本),并且应该这样解释。 标签告诉浏览器它是一个 HTML 文档的开始。 标签中的信息代表元数据,并告诉浏览器该网页而不是网页本身。 示例包括页面标题、页面描述和搜索引擎优化的关键字。 body标签的所有内容都将显示在浏览器的主窗口中。 在前面的 HTML 代码中,我们制作了Bootstrap标题,其余内容被制作为一个段落。

在浏览器中打开该文件,你应该会看到如下截图:

Knowing HTML and CSS

您将注意到放置在title标记中的内容显示为浏览器的选项卡标题,标题内容被加粗并增大了大小,段落从新的一行开始。

CSS 是关于样式的。 你可以使用 CSS 来定制网页中每个元素的外观。 您可以更改按钮的颜色、标题文本的字体、表格的边框大小等等。 可以包含内联的 CSS 样式,也可以使用单独的文件。 如果你使用的是内联 CSS,它应该在一个style标签中。 如果您正在使用外部 CSS,我们可以使用link标记并引用外部 CSS 文件。

CSS 只是一组用于表示的规则。 每个规则由两个部分组成:必须为其应用声明的选择器和包含样式信息的声明。 样式信息有一个属性和该属性的值。

让我们来看看以下简单的 CSS 规则:

    h1{ 
        color : #0094ff; 
    } 

这条 CSS 规则规定所有的标题文本都应该是蓝色的。 h1是选择器,它告诉浏览器必须对所有h1标记应用以下声明。 在声明中,我们设置了蓝色(#0094ff在十六进制格式中是蓝色)。

以下是更新后的 HTML 文件,我已经更新了 CSS 样式(突出显示的粗体):

<!DOCTYPE html> 
<html> 
<head> 
    <title> Beautify your ASP.NET MVC applications using Bootstrap </title> 
    <style type="text/css"> 

        body{ 
            font-family:Arial,Verdana,sans-serif; 
        } 

        h1{ 
            color : #0094ff; 
        } 

        p { 
            color: #5f5e5e; 
        } 
    </style> 

</head> 
<body> 
    <h1> Bootstrap </h1> 
    <p> 
        Bootstrap is the most popular HTML, CSS, and JS framework for developing responsive, mobile first projects on the web. 
    </p> 
</body> 
</html> 

当你在浏览器中修改样式后打开该文件,你会得到以下输出:

Knowing HTML and CSS

话虽如此,您需要创建 CSS 规则,以使 web 应用中的元素看起来很好。 但是为每个元素创建不同的样式是一项既耗时又乏味的任务。 您可以从任何可用的前端框架中进行选择。

任何应用都应具备以下特点:

  • 一致性:用户应该熟悉应用正在使用的元素和控件。 例如,如果使用下拉列表,用户应该只能从中选择一个值。
  • 响应式:您构建的应用应该在所有不同大小的设备上看起来都很好。 您编写的代码应该适应用户设备的屏幕大小。
  • 移动友好:这与前面提到的一点有关。 如今,许多应用是通过移动设备而不是台式电脑或笔记本电脑访问的。 我们必须确保我们构建的应用在移动设备上看起来很棒。
  • 可定制:如果你打算使用任何前端应用框架,它应该根据你的需要进行定制。 例如,如果您想更新标题文本的颜色,您应该能够更新或重写 CSS 文件以进行必要的更改。
  • 易于上手:学习前端框架的学习曲线应该是最小的,因为你应该把时间花在为客户创造价值和交付解决方案上。 我们并不是靠学习新的花哨的前端框架来赚钱的。

目前可用的前端框架很少,如 Bootstrap、FoundationPureCSS。 在本章中,我们将使用 Bootstrap 框架,因为它是应用最广泛的前端框架。

引导

Bootstrap 是最流行的 HTML, CSS 和 JS 框架开发响应性,移动优先项目在 web 上,你可以访问它的http://getbootstrap.com/

Bootstrap

Bootstrap 有许多特性可以满足 web 应用的各种需求。 它为每个 HTML 元素提供了不同的 CSS 类,因此您可以使用它而不需要接触任何 CSS 代码。 但是,如果您希望重写它,您可以这样做。

让我们简要地看一下 Bootstrap 的每个特性。

引导网格系统

Bootstrap 中的网格系统可以帮助您为应用创建响应式布局。 该特性使您的应用在所有不同形状的设备(包括移动设备)中看起来都很棒。

Bootstrap 提供了一个流体网格系统,当设备或视口尺寸增加时,它可以扩展到 12 列。 你可以把网格系统想象成 Excel 表格中的列(就像下面的截图):

Bootstrap Grid system

我们可以组合多个列来创建一个更宽的列。 在前面截图的第二行中,我们使用了单一列(using class)。 col-md-1)12 次。 在第三行,我们使用一个 CSS 类(.col-md-2)来创建 6 个更宽的列,而不是 12 个更短的列。 同样,我们创建的更宽的列的数量更少。

表格

在应用中使用 Bootstrap 时,所有表单控件都接受全局样式。 下面是一个这样的例子(截图取自 Bootstrap 网站):

Forms

下面是上述表单的代码。 相关的 HTML 元素在表单中分组在一起。 例如,Email 和Email 地址输入文本框的标签分组在一起。 密码文件输入也是如此。

Forms

内联表单

内联表单是所有表单元素都在同一行的表单(如下面的截图所示)。 我们需要将类form-inline添加到form元素中。

Inline forms

水平形式

在水平形式中,我们将每个元素组放在单独的一行上; 电子邮件标签,电子邮件一行输入,密码标签,密码一行输入(如下截图):

Horizontal forms

要应用水平表单样式,我们只需要添加类form-horizontal,如下面的代码所示。 就像其他表单一样,我们需要通过 CSS 类form-group将相关的 HTML 元素分组到一起:

Horizontal forms

表 CSS 类

对于基本的样式,添加基本的 CSS 类tabletableHTML 元素中,如下截图所示:

Table CSS classes

条纹表

在条纹表中,交替行的背景将是相同的颜色。 在下面的截图中,第一行和第三行背景颜色是相同的。 您可以应用table-striped类将条带表行为应用于表 HTML 元素。

Striped tables

悬浮表

当您将鼠标移动到表中的任意行上时,该行的背景颜色将被更改。 这种悬停行为可以通过将 CSS 类table-hover和类table应用于 HTML 表元素来实现。

Hover tables

有边框的桌子

如果我们将 CSS 类table-bordered应用到table元素,我们就可以有一个带边框的表(如下面的截图所示)。

Bordered tables

上下文类

有时需要根据数据值高亮显示行。 例如,如果您正在以表格格式显示库存数据,您可能希望用红色背景突出显示计数小于规定计数的项目行。 在本例中,可以对表行应用一个danger类以用红色突出显示它们。 有不同类型的上下文类可以用不同的颜色突出显示。 您可以将这些类应用于单个单元格,而不是整个行。

Contextual classes in table

按钮

有不同的样式选项可使按钮显示在不同的颜色。 对于所有的按钮,基本按钮类btn必须应用:

Buttons

btn-primary按钮类用蓝色高亮按钮,而btn-success按钮类用绿色高亮按钮。 在前面的屏幕截图中,显示了样式化按钮的不同选项。

纽扣尺寸

您可以根据您的需要改变按钮的大小。 btn-lg类可以应用于大按钮,btn-sm类可以应用于按钮,使其看起来较小。 以下是可用于控制按钮大小的不同选项。

Button sizes

上下文色彩

根据上下文,您可能需要更改文本的颜色。 例如,如果前一个操作成功,您可能希望将文本显示为绿色。 对于不成功的操作,您可能希望用红色显示错误消息。 在这种情况下,您可以使用这个帮助器 CSS 类以不同的颜色显示它们。

Contextual colors

我们已经看到了 Bootstrap 的各种特性。 现在,让我们使用 Bootstrap 使我们的应用看起来很好。 基本上,我们的视图中有两个主要组件:顶部的表单用于从用户获取输入,底部的表用于在表中显示结果。

使用 Bootstrap 在你的 ASP。 净 MVC 应用

有不同的方法获得 Bootstrap 为您的应用:

  • 请参考应用中 CDN(内容分发网络)中可用的 Bootstrap 文件
  • 下载源代码
  • 安装与鲍尔
  • 编译与繁重

在这些选项中,最简单的选项是第一个。

在我们之前创建的应用中打开布局文件(_Layout.cshtml)。 包括顶部(在head标签内)的 CSS 文件和底部(在body标签末尾)的脚本:

<!DOCTYPE html> 

<html> 
<head> 
    <meta name="viewport" content="width=device-width" /> 
    <title>@ViewBag.Title</title> 
    <!-- Latest compiled and minified CSS --> 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous"> 
    <!-- Optional theme --> 
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous"> 

</head> 
<body> 
    <div> 
        @RenderBody() 
    </div>     

    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.2.3.js"></script> 
    <script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/jquery.validate.min.js"></script> 
    <script src="https://ajax.aspnetcdn.com/ajax/mvc/5.2.3/jquery.validate.unobtrusive.min.js"></script> 
    <!-- Latest compiled and minified JavaScript --> 
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script> 
</body> 
</html> 

使用 Bower 安装

右键单击项目菜单,从上下文菜单中选择管理 Bower 包选项:

Installing with Bower

一旦您选择管理 Bower 包,您将能够安装或卸载 Bower 包,就像您安装或卸载NuGet包一样。

Installing with Bower

HTML 文档类型

Bootstrap 使用某些需要使用 HTML 5 文档类型的 HTML 元素和 CSS 属性。 默认情况下,您在 ASP 中创建的视图.NET Core 将只有 HTML 5 文档类型。 所以,我们不需要做任何事情。


<!DOCTYPE html> 

<html lang="en"> 
... 
</html> 

让我们对屏幕进行以下更改以使用 Bootstrap:

  • 将 CSS 类form-horizontal应用于表单。
  • 对于标签,输入和验证错误范围分别使用 CSScol-sm-2col-sm-4col-sm-3
  • 对于标签,应用 CSS 类control-label
  • 对于输入的 HTML 元素,应用了form-controlCSS 类
  • 对于每个表单组(包含标签和输入等 HTML 元素),应用 CSS 类form-group
  • 对于所有验证错误消息,应用text-dangerCSS 类,使其显示为红色
  • 应用tabletable-borderedCSS 类来样式化表

下面是完整更新的视图代码; 我们使用了 Bootstrap CSS 类来让我们的应用看起来很棒:

@model Validation.ViewModels.EmployeeAddViewModel 

<div> 
    <br/> 
    <br/> 
    <form asp-controller="Employee" asp-action="Index" method="post" role="form" class="form-horizontal"> 

        <div class="form-group"> 
            <label asp-for="Name" class="col-sm-2 control-label"></label> 
            <div class="col-sm-4"> 
                <input asp-for="Name" class="form-control" /> 
            </div> 
            <div class="col-sm-3 text-danger"> 
                <span id="validationName" asp-validation-for="Name" ></span> 
            </div> 
        </div> 

        <div class="form-group"> 
            <label asp-for="Designation" class="col-sm-2 control-label"></label> 
            <div class="col-sm-4"> 
                <input asp-for="Designation" class="form-control" /> 
            </div> 
            <div class="col-sm-3 text-danger"> 
                <span id="validationDesignation" asp-validation-for="Designation" ></span> 
            </div> 
        </div> 

        <div class="form-group"> 
            <label asp-for="Salary" class="col-sm-2 control-label"></label> 
            <div class="col-sm-4"> 
                <input asp-for="Salary" class="form-control" /> 
            </div> 
            <div class="col-sm-3 text-danger"> 
                <span id="validationSalary" asp-validation-for="Salary" ></span> 
            </div> 
        </div> 

        <div class="form-group"> 
            <div class="col-sm-offset-2 col-sm-10"> 
                <button type="submit" class="btn btn-primary">Submit</button> 
            </div> 
        </div> 

    </form> 

</div> 

<br /><br /> <br /> 

<h4> List of employees:</h4> <br /> 

    <table class="table table-bordered"> 
        <tr> 
            <th> ID </th> 
            <th> Name </th> 
            <th> Designation </th> 
            <th> Salary </th> 
        </tr> 
        @foreach (var employee in Model.EmployeesList) 
        { 
            <tr> 
                <td>@employee.EmployeeId</td> 
                <td>@employee.Name</td> 
                <td>@employee.Designation</td> 
                <td>@employee.Salary</td> 
            </tr> 
        } 
    </table> 

在做了上述更改之后,当你运行应用时,你的屏幕应该如下所示:

HTML doctype

总结

在本章中,我们学习了 HTML 和 CSS 在任何 web 应用中的作用,包括 asp.net.NET Core 应用。 我们分析了前端框架的需求,并讨论了 bootstrap 的特性,bootstrap 是最流行的 HTML、CSS 和 JS 框架,用于在 web 上开发响应性强、移动优先的项目。 我们已经通过示例讨论了 Bootstrap 中用于不同类型 HTML 元素的 CSS 和组件。 最后,我们讨论了如何在我们的 ASP 中加入 Bootstrap.NET Core 应用。

九、ASP.NET Core 应用的部署

一旦我们完成了开发我们的 ASP.NET Core 应用,我们需要部署应用,以便我们的用户可以访问它。

在任何应用中,无论是 web、桌面还是移动应用,并不是所有的功能都是通过代码实现的。 事实上,您不应该试图通过代码实现所有事情。

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

  • 配置在 ASP.NET Core 应用
  • 注册微软 Azure 平台
  • 部署的 ASP.NET Core 应用到 Azure 云平台

如果你已经用以前的 ASP 版本构建了一个 web 应用.NET MVC 中,将有一个名为Web.config的文件(一个 XML 文件),您可以在其中配置应用的所有依赖项。 但在 ASP。 在你的解决方案中没有Web.config文件:

Deployment of ASP.NET Core Application

相反,我们有project.json(一个 JSON 文件),我们将在其中配置应用的依赖项。 在讨论project.json内容之前,让我们先讨论一下 JSON。

JSON 是JavaScript 对象标记的首字母缩写。 它是开放标准的数据交换格式。 它将是人类可读的文本,并由属性/值对组成。 考虑以下 JSON,让我们仔细分析它,看看它代表什么:

{ 
  "addressess": [ 
    { 
      "DoorNo": 16, 
      "Street": "King Street", 
      "areaname": "Mascot" 
    }, 
    { 
      "DoorNo": 12, 
      "Street": "High Street", 
      "areaname": "North Sydney" 
    } 
  ] 
} 

每个数据块都是一个属性值对,用冒号分隔。 例如,"DoorNo": 16表示第一个记录中DoorNo变量的值为 16。 每个属性值对(有时称为属性)用逗号分隔。 例如,考虑以下三个属性:

"DoorNo": 16, 
"Street": "King Street", 
"areaname": "Mascot" 

每个记录或对象都包含在一对花括号中。 例如,下面的 JSON 数据表示一条记录或一个对象:

{ 
    "DoorNo": 16, 
    "Street": "King Street", 
    "areaname": "Mascot" 
} 

类似的记录可以分组在一起,并可以形成一个(对象的)数组。 方括号用于表示 JSON 格式的数组,如下例所示:

"addressess": [ 
  { 
    "DoorNo": 16, 
    "Street": "King Street", 
    "areaname": "Mascot" 
  }, 
  { 
    "DoorNo": 12, 
    "Street": "High Street", 
        "areaname": "North Sydney" 
  } 
] 

如果我们必须用 XML 格式表示相同的数据,可以这样做。 请注意,对于每条信息,我们应该有一个开始标签和一个结束标签(以“/”结尾):

<addresses> 
  <address> 
    <DoorNo>16</DoorNo> 
    <Street>King Street</Street> 
    <areaname>Mascot</areaname> 
  </address> 

  <address> 
    <DoorNo>12</DoorNo> 
    <Street>High Street</Street> 
    <areaname>North Sydney</areaname> 
  </address> 
</addresses> 

项目。 json 文件

所有的项目配置都应该放到 ASP 的project.json文件中.NET Core 应用。 下面是在使用预定义的 ASP. php 时创建的project.json文件.NET Core web 应用模板:

The project.json file

这个 JSON 文件中有不同的预定义节点,用于不同的功能。 让我们从这个文件中选取一些重要的节点进行讨论。

依赖节点

节点列出了您的 ASP 的所有依赖项.NET Core 应用。

下面是 ASP 中dependencies节点的片段.NET Core 应用。 每个依赖项都是一个属性值对,其中属性表示依赖项,值表示依赖项的版本。 如果你需要提供更多的依赖信息,你可以有一个嵌套 JSON 配置,就像它在Microsoft.NETCore.App:

"dependencies":{ 
  "Microsoft.NETCore.App":{ 
    "version": "1.0.0-rc2-3002702", 
    "type": "platform" 
    }, 
    "Microsoft.ApplicationInsights.AspNetCore": "1.0.0-rc2-final", 
    "Microsoft.AspNetCore.Authentication.Cookies": "1.0.0-rc2- final",

框架节点

在这个节点中,我们提到了 ASP 所依赖的框架.NET Core 应用。 dotnet5.6代表成熟的。net 框架,dnxcore50代表包含完整。net 框架功能子集的。NET Core 框架:

"frameworks":{ 
  "netcoreapp1.0":{ 
    "imports":[ 
      "dotnet5.6", 
      "dnxcore50", 
      "portable-net45+win8" 
      ] 
    } 
  }, 

微软 Azure

Microsoft Azure 是微软提供的用于构建、部署和管理应用和服务的云计算平台和基础设施。 它支持不同的编程语言和服务数组。

您可以将应用部署到网络中具有Internet Information Service(IIS)的任何服务器上。 但是这限制了只能从网络内访问您的应用,假设只能从网络内访问您的服务器(在大多数网络设置中)。 在本节中,我们将部署 ASP.NET Core 应用在 Microsoft Azure 中,以便全球各地的用户可以访问您的应用。

注册微软 Azure

为了将您的应用部署到 Azure,您需要拥有一个 Azure 帐户。 你可以免费创建一个 Azure 帐户,在头 30 天内你就有足够的积分来免费部署你的应用(https://azure.microsoft.com/en-in/):

Signing up to Microsoft Azure

点击右上角的免费试用按钮或免费账户链接,您将被转到以下页面:

Signing up to Microsoft Azure

点击立即开始按钮,您将被重定向到以下页面。 输入您的 Microsoft 帐户凭证,然后单击Sign In按钮。 如果你没有微软帐户,你可以通过点击页面底部的立即注册链接来创建一个:

Signing up to Microsoft Azure

由于我已经有一个微软帐户,我已经用我的凭证登录了。 一旦您登录,您将被问及您的国家、姓名、姓名和工作电话的详细信息,如下:

Signing up to Microsoft Azure

一旦你输入了所有必要的细节,系统会要求你输入国家代码和电话号码,这样 Azure 就可以给你发短信或打电话,以验证你是真人,而不是机器人Signing up to Microsoft Azure。 如果您选择发短信给我选项,您将得到一个号码到您的手机; 您需要在最后一个字段中输入,然后点击验证码:

Signing up to Microsoft Azure

一旦您通过电话验证,您需要在以下表格中输入您的信用卡信息。 您将被收取大约 1 美元的费用,并将在 5 到 6 个工作日内退回到您的账户。 收集这些信息是为了识别用户的身份,除非用户明确选择了付费服务,否则用户不会被收费:

Signing up to Microsoft Azure

一旦您输入您的信用卡信息并点击下一步,您将不得不同意认购协议作为注册过程的最后一步:

Signing up to Microsoft Azure

点击注册按钮后,还需要 5 分钟才能完成注册过程。 你会看到下面的屏幕,直到这个过程完成:

Signing up to Microsoft Azure

注册过程完成后,您将看到以下屏幕。 您还将收到一封包含订阅细节的确认电子邮件(到您在第一步中给出的电子邮件 ID):

Signing up to Microsoft Azure

Azure 部署的先决条件

为了发布 ASP。 从 Visual Studio 2015 社区版到 Azure 的 NET Core 应用,你应该安装 Visual Studio 2015 更新 2(至少),你应该安装/启用 SQL Server 数据工具。

注意事项

如果你有 vs2015 的最新版本,没有必要安装更新 2。

您可以从 URLhttps://www.visualstudio.com/en-us/news/vs2015-update2-vs.aspx下载 Visual Studio 2015 Update 2 并安装它。

SQL Server 数据工具的安装路径为:控制面板|程序和功能 右键单击Microsoft Visual Studio Community 2015,选择Change选项,如下图所示:

Prerequisites to Azure deployment

一旦你点击改变选项,你会看到下面的窗口-你必须选择修改按钮。 单击Modify按钮后,将提供一个选项,可以在其中修改 Visual Studio 安装选项。 我选择了Microsoft SQL Server Data Tools,如下图所示:

Prerequisites to Azure deployment

一旦你点击Next,Visual Studio 将安装 SQL Server 数据工具,一旦安装完成,你将看到如下屏幕,显示安装完成状态:

Prerequisites to Azure deployment

部署 ASP。 Azure 中的 NET Core 应用

让我们创建一个 ASP.NET Core 应用,我们可以部署在微软 Azure:

Deploying the ASP.NET Core application in Azure

ASP.NET Core 应用将被创建,一旦你点击OK按钮:

Deploying the ASP.NET Core application in Azure

作为默认的 ASP.NET Core Web Application 模板使用了 Entity Framework,我们需要执行以下命令来创建数据库迁移:

dotnet ef database update

一旦您在命令提示符(在项目的路径中)中输入命令,迁移文件将被创建。 这个迁移文件将包含对数据库的所有更改。 该迁移将在部署 Azure 时应用,以便 Azure 可以为部署 Azure 创建必要的数据库脚本:

Deploying the ASP.NET Core application in Azure

一旦数据库迁移完成,右键单击创建的 Core 应用,选择Publish选项,如下图所示:

Deploying the ASP.NET Core application in Azure

当您点击发布选项时,您将看到以下屏幕,描述您可以使用的各种发布选项:

Deploying the ASP.NET Core application in Azure

请选择Microsoft Azure App Service选项在 Microsoft Azure 平台上发布 web 应用:

Deploying the ASP.NET Core application in Azure

点击New按钮,会看到如下画面:

Deploying the ASP.NET Core application in Azure

您可以将 web 应用的名称更改为您想要的任何名称。 我已经将 web app 的名称更改为learningmvc6

点击资源组旁边的新建按钮,输入资源组名称。 资源组只是一个标记,您可以在其中对所有计算资源进行分组,这样如果您想删除所有资源,您可以直接删除资源组。 例如,一个资源组可以由一个 web 服务器和一个数据库服务器组成——你可以把它想象成一个资源的集合。

现在,点击App 服务计划旁边的新增按钮。 您将看到以下窗口,您可以在其中选择 web 应用容器的位置和大小。 你的位置可以是任何地方,从美国中南部到欧洲,从日本到加拿大。 您的应用容器可以是任何东西,从免费到具有 7 GB RAM 的机器。 我选择了免费选项,因为我们的目标是部署 ASP.NET Core 应用在云环境中,而不是部署一个被数百万用户访问的应用。 当然,你也可以用 ASP 实现同样的功能.NET Core 和 Microsoft Azure:

Deploying the ASP.NET Core application in Azure

现在,我们可以配置 SQL 数据库,它可以作为额外的 Azure 服务使用。

Deploying the ASP.NET Core application in Azure

单击顶部部分的+按钮,这将引导我们配置 SQL 数据库。

Deploying the ASP.NET Core application in Azure

如果您在 Azure 环境中有任何现有的 SQL 服务器,您可以使用它。 因为我没有任何这样的服务器,我将通过点击 SQL server 旁边的按钮来创建一个 SQL 服务器:

Deploying the ASP.NET Core application in Azure

请输入 SQL Server 的管理员用户名和密码,单击OK。 你会看到以下画面:

Deploying the ASP.NET Core application in Azure

点击OK,会看到如下画面:

Deploying the ASP.NET Core application in Azure

在前面的屏幕上点击OK,我们会看到Create App Service屏幕:

Deploying the ASP.NET Core application in Azure

一旦我们配置了所有所需的 Azure 服务,点击创建:

Deploying the ASP.NET Core application in Azure

前面的屏幕显示了部署配置选项,例如应用的站点名称目的地 URL。 点击上一个屏幕上的Next:

Deploying the ASP.NET Core application in Azure

需要注意的是,您需要展开Databases选项和Entity Framework Migrations选项,并选择这两个复选框。 第一个复选框表示应在运行时使用的连接字符串,第二个复选框表示应在发布时应用的数据库迁移。

Deploying the ASP.NET Core application in Azure

前面的屏幕是预览屏幕,您可以在其中看到发布时将要部署的文件。 这是一个可选的步骤,如果你想查看文件,你可以点击开始预览按钮。 或者,您可以点击Publish按钮在 Azure 平台上发布 web 应用。

一旦你点击发布按钮,我们的 ASP.NET Core 应用将部署在 Azure 中,您的应用 URL 将在成功发布时打开。 你会看到以下画面:

Deploying the ASP.NET Core application in Azure

部署 ASP。 Linux 环境下的核心 web 应用

在本章的这一部分,我们将学习如何创建和部署 ASP.NET Core web 应用在 Linux 平台。 我将使用Amazon Web Services(AWS)将应用部署到云中。 显然,您不需要 AWS 来部署 ASP.NET 内核应用在 Linux 上。 我使用它只是为了不需要在我的本地机器上安装 Linux。 使用 AWS(或任何其他公共云服务提供商或任何托管提供商)托管的另一个好处是,我可以从任何地方访问 web 应用,因为它将是公共可用的。

在 Linux 环境下,我们有以下先决条件来创建和部署:

  • Linux 机器
  • Putty 客户端(如果您使用的是远程 Linux 机器)

创建 Linux 机器

我们将使用 AWS 来创建一台 Linux 机器。 使用 AWS 或其他云服务提供商的优势在于,我们只有在需要的时候才可以使用它们的服务,当你用完它时,我们可以关闭机器。 你只需要在使用它的时候支付时间。 第一年,AWS 有一个免费层,您可以在其中托管机器(如果它符合免费层的条件),而无需支付任何费用。 我已经使用 AWS 好几年了,尝试了云中的许多东西,因此我没有资格进入免费层。

然而,您可以使用任何虚拟化软件在 Windows PC 上安装 Linux。 Ubuntu Linux 有从 u 盘启动的选项,这样你就不需要干扰本地 PC 上的任何东西。

注册 AWS 帐户后,您可以进入EC2 仪表板,在那里您可以创建EC2 实例:

Creating a Linux machine

点击上一个屏幕中的Launch Instance。 将启动一个向导,它将帮助您选择和配置实例。 在这一步中,我们选择 Ubuntu Linux 服务器是因为它的易用性。

Creating a Linux machine

AWS 中有各种各样的实例,从nano(使用 0.5 GB RAM)到大型机器(使用 1952 GB RAM)。 我们将选择实例,因为它符合免费层:

Creating a Linux machine

在前面的步骤中,我们可以为云配置实例。 我们可以创建一个自动伸缩组,其中 AWS 云将在负载高时自动启动实例。 因为我们的目标是创建和部署 ASP.NET Core web 应用,我们将保留默认值,因为他们是,然后点击下一步:添加存储移动到下一个屏幕:

Creating a Linux machine

micro实例没有任何外部存储。 因此,我们需要添加存储才能使用它。 我们有三种存储方式可供选择:通用 SSD预留 SSD磁性 SSD。 在这三种存储中,通用 SSD是通常使用的存储。

当应用进行高输入输出操作时,吞吐量可能会下降。 但是在 provisioning SSD 中,您可以从存储设备维护所需的吞吐量。 磁存储器只是一种老式的存储器。 我们将使用通用 8gb固态硬盘(SSD),因为它能很好地满足我们的要求。

Creating a Linux machine

如果使用多个实例,可以对它们进行标记,以便通过使用标记名称来控制实例。 因为我们只会启动一个实例,所以我将把它留空,然后进入下一个步骤:

Creating a Linux machine

在这一步中,我们可以为实例配置安全组—应该为传入的流量打开哪些端口。 任何配置中的一般规则都是只打开您需要的端口,而不打开其他任何端口。 您还需要告诉 IP(或其范围)可以从哪里访问计算机。 由于它是一个演示应用,我们将为Secure Shell(SSH)开放端口22; 使用 PuTTY 和80访问 Core web 应用。

配置好安全组后,单击审查和启动

Creating a Linux machine

在以下屏幕上,您可以查看所选选项:

Creating a Linux machine

一旦您对所选选项满意,就可以单击启动。 否则,您可以回到上一步,使用正确的值重新配置它们。

当您点击Launch时,它会要求您选择一个密钥对,您将使用该密钥对登录到任何 AWS 服务器。 如果你没有,你可以创建一个。 因为我已经创建了一个,我将使用现有的一个,如下截图所示:

Creating a Linux machine

选择密钥对并单击Launch Instances。 AWS 将为我们启动新的实例,状态将显示出来(如下面的截图所示)。 实例 ID 也将可用(屏幕截图中的框):

Creating a Linux machine

点击蓝色的链接会获取状态(如下截图所示)。 公共 DNS公共 IP是您将用来连接到该服务器的重要值。 因此,我在截图中把它们框起来:

Creating a Linux machine

安装 PuTTY 客户端

在创建了一个新的 Linux 服务器之后,我们可以在那里创建一个 ASP.NET 5 web 应用和主机,我们需要安装 PuTTY 客户端,一个可以向 Linux 服务器发送命令和接收响应的小应用。 因为我们将在 Linux 服务器上安装应用,所以我们需要一种从 Windows PC 连接到 Linux 服务器的方法。 PuTTY 客户端应用就是这样做的。

PuTTY 客户端可通过“http://www.chiark.greenend.org.uk/~sgtatham/putty/”下载。

Installing the PuTTY client

点击下载链接,在下面的屏幕中选择链接(屏幕截图中的框):

Installing the PuTTY client

它将下载 MSI 文件。 下载完成后,启动安装程序,你会看到如下欢迎屏幕:

Installing the PuTTY client

点击Next,你会看到以下画面:

Installing the PuTTY client

选择安装文件夹-你可以让它保持原样,然后点击Next:

Installing the PuTTY client

选择要安装的产品特性。 您可以保持默认选择,单击Install。 安装完成后,你会看到如下画面:

Installing the PuTTY client

单击完成并启动 PuTTY 应用。 它将打开 PuTTY 配置窗口,我们将在这里输入主机名和身份验证细节。 主机名为<username>@<public DNS>。 在我们的例子中,它是ubuntu@ec2-107-22-121-81.compute-1.amazonaws.com。 Ubuntu 是我们所选择的 Ubuntu AMI 的默认用户。 我们可以在状态窗口中获得公共 DNS 值,如下所示:

Installing the PuTTY client

对于身份验证,在左侧窗格中选择Connection|SSH|Auth并选择我们之前创建的PPK文件(私钥文件):

Installing the PuTTY client

点击打开。 你会收到一个警告,问你是否信任这个主机。 单击yes,您将看到 Linux 屏幕上的命令提示符。

Installing the PuTTY client

接下来,我们需要在创建 asp.net 之前安装。net Core.NET 5 应用并最终托管它们。

在 Linux 机器上安装。net Core

为了在 Ubuntu 上安装。net Core,我们需要首先设置 apt 并获取包含我们需要的包的 feed。 输入以下命令:

sudo sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet-release/ trusty main" > /etc/apt/sources.list.d/dotnetdev.list'sudo apt-key adv --keyserver apt-mo.trafficmanager.net --recv-keys 417A0893

你会看到以下画面:

Installing of .NET Core in a Linux machine

然后通过发出以下命令来更新它,这将下载所需的包并安装它们:

sudo apt-get update

你会看到下面这个命令的屏幕:

Installing of .NET Core in a Linux machine

使用如下命令安装。net Core:

sudo apt-get install dotnet-dev-1.0.0-preview2-003121 

系统显示如下界面:

Installing of .NET Core in a Linux machine

创建一个新的 ASP。 净 5 项目

发出以下命令来创建一个新目录,我们将在其中创建 ASP。 净 5 应用。 第一个命令( **mkdir** - make directory)用于在 Linux 中创建一个目录,第二个命令( **cd** - change directory)用于进入文件夹内部。 最后一个命令是创建。net Core 应用的命令行:

mkdir aspnetcoreapp
cd aspnetcoreapp
dotnet new

系统显示如下界面:

Creating a new ASP.NET 5 project

这将创建。net Core 应用,它有两个文件——Program.csproject.json。 它是一个甚至没有Startup文件的最小应用。

我们需要在project.json中添加Kestrel HTTP Server包作为一个依赖项。 您可以通过发出命令 **vi project.json** 来编辑该文件。 默认情况下,vi 编辑器将以只读模式打开文件。 你需要按Esc+I才能进入编辑模式。 添加一行“Microsoft.AspNetCore.Server.” Kestrel": "1.0.0"如下截图:

Creating a new ASP.NET 5 project

按 Escape 键和“:”,输入wq,写入并退出vi编辑器。

由于我们已经添加了依赖关系,我们需要通过执行以下命令来恢复这些包:

dotnet restore

一旦你输入这个命令,所有的包将被恢复如下截图所示:

Creating a new ASP.NET 5 project

创建一个包含以下内容的新文件Startup.cs。 可以通过发出命令 **vi Startup.cs** 来创建一个新文件。 像往常一样,我们需要按Esc+I使文件处于读写模式。 粘贴以下内容(从这里复制后,点击鼠标右键即可粘贴):

using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
namespace aspnetcoreapp
{
  public class Startup
  {
    public void Configure(IApplicationBuilder app)
    {
      app.Run(context =>
        {
          return context.Response.WriteAsync("This is ASP.NET Core application running in Linux!");
         });
    }
  }
}

Esc+😗*,输入wq,保存文件。 将Program.cs文件更新为以下内容:

*```
namespace aspnetcoreapp
{
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseStartup()
.Build();
host.Run();
}
}
}


您将看到以下屏幕:

![Creating a new ASP.NET 5 project](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00189.jpg)

我们已经创建了 ASP.NET Core web 应用。 现在我们需要安装**Nginx**,这是一个反向代理服务器,它可以帮助您减轻诸如提供静态内容、缓存和压缩请求等工作。 你可以配置 Nginx 监听一个特定的端口(我们将在本章的后面讨论细节)。 你可以通过以下命令安装 Nginx:

sudo apt-get install nginx


安装完成后,您可以发出以下命令来启动服务:

sudo service nginx start


当你运行这个命令时,你会看到下面的屏幕:

![Creating a new ASP.NET 5 project](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00190.jpg)

# 配置 Nginx 服务器

通过修改文件(`/etc/nginx/sites-available/default`)来配置 Nginx 服务器,使其包含以下内容——这样 Nginx 就可以将请求转发给 ASP.NET。 为了修改这个文件,您需要有足够的权限—尝试切换到超级用户。 `**Sudo su**` 是将其切换为超级用户的命令。 参见以下代码:

server {
listen 80;
location / {
proxy_pass http://localhost:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}


代码如下所示:

![Configuring the Nginx server](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00191.jpg)

运行应用,执行如下命令:

dotnet run


你会看到以下画面:

![Configuring the Nginx server](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00192.jpg)

现在使用公共 DNS 从浏览器访问应用(AWS 在实例启动时创建了公共 DNS):

![Configuring the Nginx server](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00193.jpg)

瞧! 我们已经创建了 ASP.NET Core web 应用,并在 Linux 盒子中启动它。 我们甚至通过**Amazon Web Services**(**AWS**)使用云。

# 总结

在本章中,您已经了解了`project.json`文件中可用的不同组件,其中包含了您的 ASP. conf 的所有配置.NET Core。 我们讨论了如何注册微软 Azure 云平台并部署 ASP。 Azure 平台中的 NET Core 应用。 我们还学习了如何创建和部署 ASP.NET Core web 应用在 Linux 中使用 Amazon web Services 在云。*

# 十、使用 ASP.NET Web API 构建基于 HTTP 的 Web 服务

到目前为止,我们已经学习了如何使用 ASP 创建 web 应用。 净的核心。 但有时仅仅创建一个 web 应用是不够的。 让我们假设你正在使用 ASP.NET Core 创建一个 web 应用,为世界上所有城市提供天气信息。 人们访问您的 web 应用来查找天气信息,他们对服务很满意。 但许多其他网站或网络应用可能需要这些天气信息,如旅游网站、新闻网站和许多其他移动应用。

您可以创建并发布 web 服务,而不是为他们的网站重新编写代码,网站可以在需要的时候使用所需的 web 服务。

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

*   什么是基于 http 的服务,它是多么有用
*   提琴手是什么
*   如何使用 Fiddler 组成一个 HTTP 请求并触发它以获得 HTTP 响应
*   如何使用 Web API 设计和实现 HTTP 服务

微软提供了 ASP.NET Web API 用于程序员构建基于 http 的服务。 但是 HTTP 不仅仅用于服务网页。 您可以使用 HTTP 作为平台。 这带来了几个好处:

*   作为使用 ASP 构建的 web 服务.NET Web API 使用 HTTP 进行通信,这些 Web 服务可以从各种应用中使用,从控制台应用到 Web 应用,从 WCF 服务到移动应用
*   每当 web 服务的逻辑/代码发生任何更改时,客户机(使用服务的网站)不需要更改任何内容。 他们可以像以前一样使用 web 服务

# HTTP 基础知识

HTTP 是构建服务的强大平台。 您可以使用现有的 HTTP 谓词构建服务。 例如,您可以使用现有 HTTP 谓词 GET 来获取产品列表,或者使用 POST 来更新关于产品的信息。 让我们快速了解一下 HTTP 如何构建服务。

在 ASP 中提供 HTML 页面的底层机制没有区别.NET MVC,并在 HTTP 服务上下文中提供数据服务。 两者都遵循请求-响应模式和相同的路由机制。

HTTP 请求可以从任何客户机(台式机、笔记本电脑、平板电脑、移动设备等)发送到服务器,服务器将用 HTTP 响应进行响应。 HTTP 响应可以以任何格式(如 JSON 或 XML)发送到客户机。 如下图所示:

![HTTP basics](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00194.jpg)

在上面的图表中,一个请求是从桌面计算机发送的(它可以从移动设备或平板电脑发送; 这没有什么区别),服务器返回请求的 HTTP 响应。 由于大多数设备都支持 HTTP,所以它是无处不在的。

## HTTP 动词

HTTP 动词描述如何发送请求。 这些是在 HTTP 中定义的方法,它们规定了如何将 HTTP 请求从客户机发送到服务器

## GET 方法

当我们使用 HTTP GET 请求时,信息通过 URL 本身传递:

GET api/employees/{id}


此`GET`请求根据传递的 ID 获取员工信息。 使用`GET`请求的优点是它是轻量级的,所有需要的信息都将在 URL 或 header 本身中传递,如下图所示:

![GET method](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00195.jpg)

## PUT 方法

`PUT`方法用于创建或更新资源。 `PUT`是幂等操作,即即使执行多次,预期的行为也不会改变:

![PUT method](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00196.jpg)

## POST 方法

您可以使用 POST 来创建或更新资源。 通常,POST 用于创建资源,而不是更新它。 根据 HTTP 标准,当你创建一个新资源时,你应该返回一个**201 HTTP**状态码:

![POST method](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00197.jpg)

## DELETE 方法

DELETE 方法用于删除资源。 通常,当你删除一个资源时,你会传递 ID 作为一个参数,你不会在请求体中传递任何东西:

![DELETE method](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00198.jpg)

通常,HTTP 服务将由其他应用和服务使用。 使用服务的应用称为客户机。 测试 HTTP 服务的一个选项是构建客户机。 但这将耗费时间,而且在测试 HTTP 服务后,我们可能会丢弃客户机代码。

另一种广泛使用的选择是使用应用,使我们能够触发 HTTP 请求并监视响应。 有许多可用的应用,Fiddler 就是其中一个广泛使用的应用。

## 小提琴工具

Fiddler 是用于监视 HTTP 和 HTTPS 流量的代理服务器应用。 您可以监视从客户机发送到服务器的请求、发送到客户机的响应以及从服务器接收到的响应。 这就像看到服务器和客户机之间的管道中的流量一样。 您甚至可以编写请求、触发它并分析接收到的响应,而不需要为服务编写客户机。

您可以在[http://www.telerik.com/fiddler](http://www.telerik.com/fiddler)下载小提琴手。 你会看到以下窗口:

![Fiddler tool](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00199.jpg)

足够的理论。 让我们使用 ASP 创建一个简单的 web 服务。 净 Web API。

启动 Visual Studio 2015:

![Fiddler tool](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00200.jpg)

当您单击**OK**时,将创建一个 Web API 解决方案。 就像 ASP.NET Core 应用控制器继承自 controller 类。

![Fiddler tool](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00201.jpg)

Web API 类也将继承相同的 Controller 类。 这是 ASP 的区别.NET Core 和早期版本的 asp.net。 净 MVC。 在早期版本中,所有 Web API 控制器类都继承自`ApiController`类。 在 ASP.NET 5,它已经被统一了,同样的基本控制器类被用于构建 web 应用和服务。

以下是创建项目时选择**Web API**模板选项时默认创建的`ValuesController`类:

![Fiddler tool](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00202.jpg)

在我们创建自己的自定义控制器之前,让我们分析一下默认的 API 控制器。 在`ValuesController`文件中,已经定义了几个 API 方法。

有两个重载的`GET`方法—一个带有参数,另一个没有参数。 不带参数的`GET`方法返回该类型的所有资源。 在本例中,我们只返回两个字符串。 在现实世界中,我们将返回资源的元数据。 例如,如果我们在电影 API 控制器上触发`GET`请求,它将返回关于所有电影的信息。 带有`id`参数的`GET`方法返回其 ID 与传递的 ID 匹配的资源。 例如,如果您传递一个电影 ID,它将返回关于该电影的信息。 其他方法(如`PUT`、`POST`和`DELETE`)的主体在这个控制器中是空的,我们将在后面讨论这些方法。

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

![Fiddler tool](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00203.jpg)

默认情况下,它触发一个到`api/values`的请求,这些值显示在浏览器中。

让我们学习如何从 Fiddler 应用启动 HTTP 请求。 打开提琴手应用。 在左下角,选择红色框中的**Web Browsers**选项。 选择此选项将使我们能够查看来自**Web 浏览器**的流量:

![Fiddler tool](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00204.jpg)

选择**Composer**选项卡,输入 URL`http://localhost:49933/api/values`,如下图所示,然后单击右上角的**Execute**按钮:

![Fiddler tool](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00205.jpg)

单击**Execute**按钮后,将创建一个 HTTP 会话,在左侧窗格中可见(在蓝色框中突出显示)。 单击会话,并选择右上角窗格上的**inspector**选项卡。 在右下角窗格中选择 JSON 选项卡(在下面的截图中由紫色边框框突出显示)。

您可以看到 HTTP 请求返回的 JSON 数据——**value1**和**value2**如下截图:

![Fiddler tool](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00206.jpg)

现在轮到我们编写自定义 API 了。

在这个自定义 API 中,我们将提供 API 方法来创建员工对象、列出所有员工对象和删除员工对象。

首先,让我们为员工创建一个模型。 我们需要创建一个文件夹来保存这些模型。 右键单击项目,选择**添加**|**新建文件夹**,将文件夹命名为`Models`:

![Fiddler tool](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00207.jpg)

右键单击`Models`文件夹,选择**Add**|**New Item…**来创建一个员工模型类。 这个员工模型类只是一个 POCO 类。 参见以下代码:

public class Employee
{
public int Id {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
public string Department {get; set;}
}


然后,我们定义存储库接口来处理模型:

public interface IEmployeeRepository
{
void AddEmployee(Employee e);
IEnumerable GetAllEmployees();
Employee GetEmployee(int id);
Employee RemoveEmployee(int id);
void UpdateEmployee(Employee employee);
}


然后我们为这个模型实现接口:

public class EmployeeRepository : IEmployeeRepository
{
private static List employees = new List();

public EmployeeRepository()
{

Employee employee1 = new Employee
{
  FirstName = "Mugil",
  LastName = "Ragu",
  Department = "Finance",
  Id = 1
};

Employee employee2 = new Employee
{
FirstName = "John",
LastName = "Skeet",
Department = "IT",
Id = 2
};

employees.Add(employee1);
employees.Add(employee2);
}

public IEnumerable GetAllEmployees()
{
return employees;
}

public void AddEmployee(Employee e)
{
e.Id = GetNextRandomId();
employees.Add(e);
}

public Employee GetEmployee(int id)
{
return employees.Where(emp => emp.Id == id).FirstOrDefault();
}

public Employee RemoveEmployee(int id)
{
Employee employee = employees.Where(emp => emp.Id == id).FirstOrDefault();
if (employee !=null )
{
employees.Remove(employee);
}
return employee;
}

public void UpdateEmployee(Employee emp)
{
Employee employee = employees.Where(e => e.Id == emp.Id).FirstOrDefault();
if(employee != null)
{
employee.Department = emp.Department;
employee.FirstName = emp.FirstName;
employee.LastName = emp.LastName;
}
}

private int GetNextRandomId()
{
int id = -1;
bool isIdExists;
Random random = new Random();
do
{
id = random.Next();
isIdExists = employees.Any(emp => emp.Id == id);
} while (isIdExists);
return id;
}
}


在实现类中有一些事情需要注意:

*   我们决定不使用数据库,因为我们的目标是使用 Web API 创建 HTTP 服务,而不是编写数据访问代码。
*   我们使用内存中的列表来保存数据。 所有的操作都将在这个列表中执行。 事实上,数据可以是任何形式的,从关系数据库到简单的内存列表。
*   在构造函数方法中,我们将一个对象添加到列表中。 这个列表将作为我们的 HTTP 服务的数据库。
*   `GetAllEmployees`API 方法将作为`IEnumerable`接口返回所有员工。
*   `AddEmployee`方法将员工(作为参数传递)添加到列表中。
*   `GetEmployee`方法将返回 ID 与参数匹配的员工。
*   方法将从列表中删除该员工。
*   `UpdateEmployee`方法将更新员工信息。
*   方法将返回下一个可用的随机整数。 这个整数值用于生成员工 ID。

# 依赖注入

在大多数实际项目中,我们不会在任何控制器中使用`new`实例实例化任何对象,原因是我们不希望依赖组件(控制器和存储库之间)之间有紧密耦合。 相反,我们将一个接口传递给控制器,依赖注入容器(如**Unity**)将在控制器需要它时为我们创建一个对象。 这种设计模式通常称为**Control 的反转**。

假设一个名为*class a*的类使用另一个类*class b*。 在本例中,*ClassA*只要知道*ClassB*的行为、方法和属性就足够了,而且不需要*ClassB*的内部实现细节。 因此,我们可以抽象*ClassB*并从类中创建一个接口,然后将该接口作为参数而不是具体类。 这种方法的优点是,我们可以在运行时传递任何类,只要它实现了一个公认的契约(接口)。

在 ASP.NET 5(包括 asp.net.NET Core 和 Web API),我们内置了对依赖注入的支持。 在`ConfigureServices`方法中,我们添加了执行依赖注入的行(用粗体突出显示)。 我们指示内置的依赖注入容器在引用`IEmployeeRepository`接口的地方创建`EmployeeRepository`类,并且指示它为单例; 这意味着相同的对象(将由依赖注入容器创建)将在应用的整个生命周期中共享:

public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddMvc();

services.AddSingleton<IEmployeeRepository, EmployeeRepository>();

}


在前面的代码中,我们为依赖注入使用了单例模式,该模式仅在第一次请求服务时才创建服务。 还有其他类型的生命周期服务,如**Transient**和**Scoped**。 瞬态生命周期服务在每次请求时创建,范围生命周期服务在每个请求中创建一次。 以下是使用生命周期时创建的代码片段:

services.AddTransient

<IEmployeeRepository, EmployeeRepository>();

services.AddScoped <

IEmployeeRepository, EmployeeRepository>();


现在是时候进入创建 API 控制器的操作的核心部分了。 右键单击**Controllers**文件夹,选择**Add |****New Item**。 然后从列表中选择**Web API Controller Class**,如下图所示。 命名你的控制器,然后点击**添加**按钮:

![Dependency Injection](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00208.jpg)

移除 Controller 中生成的代码,并添加以下构造函数:

public EmployeeController(IEmployeeRepository employeesRepo)
{
employeeRepository = employeesRepo;
}
private IEmployeeRepository employeeRepository {get; set;}


在前面的构造函数中,我们注入了依赖项。 在调用这个构造函数时,将创建`EmployeeRepository`对象。

让我们实现两个`GET`方法——第一个方法将返回所有员工的详细信息,第二个`GET`方法将根据传递的员工 ID 返回员工:

public IEnumerable GetAll()
{
return employeeRepository.GetAllEmployees();
}

[HttpGet("{id}",Name ="GetEmployee")]
public IActionResult GetById(int id)
{
var employee = employeeRepository.GetEmployee(id);
if(employee == null)
{
return NotFound();
}
return new ObjectResult(employee);
}


让我们从 Fiddler 调用这些 HTTP 方法。

运行解决方案,打开 Fiddler 应用,并单击**Composer**选项卡。

选择 HTTP 方法(我们选择了`GET`方法,因为我们有一个 GET API 方法)并输入 URL`http://localhost:49933/api/employee`。

请注意,当我运行应用时,它运行在端口`49933`上; 在您的情况下,端口号将不同,因此相应地构造您的 URL。

输入 URL 并选择方法后,点击**Execute**按钮,如下截图所示:

![Dependency Injection](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00209.jpg)

单击**Execute**按钮后,将创建一个 HTTP 会话,并触发请求。

单击左侧窗格上的会话(如下截图所示),并在右侧窗格中选择**inspector**选项卡。 您可以在右下角窗格的**JSON**选项卡中查看结果:

![Dependency Injection](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00210.jpg)

让我们触发另一个 HTTP 请求以获取特定员工的信息,比如 ID 为 2 的员工。 我们将通过附加 ID`http://localhost:49933/api/employee/2`来构建 URL,如下所示:

![Dependency Injection](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00211.jpg)

选择最近创建的 HTTP 会话,并单击它:

您可以在右侧窗格中看到 JSON 格式的结果。

现在,我们将向我们的服务中添加`Create`、`Update`和`Delete`操作。 首先,我们将提供 Create 功能,将员工的添加到我们的服务:

[HttpPost]
public IActionResult Add([FromBody] Employee emp)
{
if (emp == null)
{
return BadRequest();
}
employeeRepository.AddEmployee(emp);
return CreatedAtRoute("GetEmployee", new { id = emp.Id }, emp);
}


采用上述`Add`方法时,应考虑以下几点:

1.  我们将`Employee`对象作为参数传递。 我们通过指定`[FromBody]`属性来指示`Add`方法从请求体中获取该对象:
    *   如果没有传递 employee 对象,我们将把错误的请求返回给调用的客户端
    *   如果它不为空,我们将调用存储库方法来将员工添加到列表中(在现实世界中,我们将把它添加到数据库中)
2.  一旦我们添加了雇员,我们将在创建新资源时返回*201 状态码*(根据 HTTP 标准)。

打开 Fiddler 应用,按照以下步骤添加员工:

1.  选择 HTTP 方式为`POST`,输入 URL`http://localhost:54504/api/employee/`。
2.  您需要在请求头中将内容类型指定为`application/json`。 请看下面的截图,我们在其中添加了`Content-Type: application/json`到请求头。
3.  As mentioned in the code, we have to pass the employee object in the form of JSON in the body of the request. In the following request, we have formed a JSON that contains the properties of the `Employee` object with the values in the brackets { "FirstName" : "James", "LastName" : "Anderson","Department" : "IT"}:

    ![Dependency Injection](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00212.jpg)

创建好请求后,可以单击**Execute**按钮来触发请求。 这会返回*201 HTTP 状态码*,这是创建新资源的标准 HTTP 响应:

![Dependency Injection](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00213.jpg)

一旦我们在服务器中创建了资源,我们就会重定向响应以获得新创建的资源。 当我们调用`CreatedAtRoute`方法并将新创建的员工 ID 作为参数传递时,就会发生这种情况。

单击左侧的会话,并在右侧窗格中选择**Inspector**选项卡。 现在您可以看到请求的响应。 响应包含在服务器中新创建的`Employee`对象。 我们必须注意,`Employee`对象的 ID 是在服务器上生成的,并且在下面的响应中可用。 本例中为该员工生成的 ID 为`1771082655`:

![Dependency Injection](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00214.jpg)

在前面的 Fiddler 窗口的右下角面板中,我们可以看到新创建资源的完整 JSON 响应。

现在我们将添加一个 Web API 方法来更新资源。 更新资源的方法与创建资源的方法非常相似,只有一些区别。 当我们创建资源时,我们使用`HTTP POST`方法,而当我们更新资源时,我们使用`HTTP PUT`方法。

如果在存储库中找不到传递的员工 ID,我们将返回一个*404 错误*响应,这是一个未找到资源的 HTTP 标准错误响应。

下面是更新资源的 Web API 控制器方法代码:

[HttpPut]
public IActionResult Update([FromBody] Employee emp)
{
if( emp == null)
{
return BadRequest();
}
Employee employee = employeeRepository.GetEmployee(emp.Id);
if(employee == null)
{
return NotFound();
}
employeeRepository.UpdateEmployee(emp);
return new NoContentResult();
}


以下是用于更新员工的存储库层代码:

public void UpdateEmployee(Employee emp)
{
Employee employee = employees.Where(e => e.Id == emp.Id).FirstOrDefault();
if (employee != null)
{
employee.Department = emp.Department;
employee.FirstName = emp.FirstName;
employee.LastName = emp.LastName;
}
}


打开 Fiddler 应用,并编写一个请求`HTTP PUT`。 因为我们要将`Employee`对象传递到请求的主体中,所以我们需要将内容类型提到为`application/json`。 在请求体中,我们需要以 JSON 格式提供`Employee`对象,如下截图所示:

![Dependency Injection](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00215.jpg)

当您单击**Execute**按钮时,`HTTP PUT`请求将被触发,我们的 Web API 方法将被调用。 一旦成功,将返回*HTTP 204*响应:

![Dependency Injection](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00216.jpg)

## 删除方法

删除资源时应使用`HTTP DELETE`方法。 不需要在请求体中传递任何内容。

### 删除资源的 Web API 方法

`Delete`Web API 方法有一个`void`返回类型,它将返回一个*HTTP 200*响应:

[HttpDelete("{id}")]
public void Delete(int id)
{
employeeRepository.RemoveEmployee(id);
}


### 用于删除员工数据的 Web 存储库层代码

在下面的存储库层方法中,我们从内部员工列表中删除员工(其 ID 与传递的参数的 ID 匹配)。 但在现实世界中,我们将与数据库交互以删除特定的员工。 考虑以下代码:

public Employee RemoveEmployee(int id)
{
Employee employee = employees.Where(emp => emp.Id == id).FirstOrDefault();
if(employee != null)
{
employees.Remove(employee);
}
return employee;
}


打开 Fiddler 应用,选择`DELETE`HTTP 方法,传递带有参数的 URL,然后单击**Execute**按钮。 请注意,我们没有在请求头中传递内容类型,因为我们没有在请求体中传递任何 employee 对象:

![Web Repository layer code for deleting the employee data](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00217.jpg)

当我们返回 void 时,Web API`DELETE`方法返回一个*HTTP 200*状态,正如你在 Fiddler 应用的左侧窗格中所看到的:

![Web Repository layer code for deleting the employee data](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00218.jpg)

# 总结

在本章中,您了解了 HTTP 服务及其用途。 我们讨论了如何使用 Web API 设计和实现 HTTP 服务。 我们使用 Fiddler 工具构造 HTTP 请求并返回响应。 我们还学习了如何编写 Web API 方法来从头到尾执行 CRUD 操作,从编写 Web API 方法到触发请求和返回响应。

# 十一、提高 ASP.NET Core 应用的性能

当您考虑频繁访问的应用(我们每天使用的那些应用)时,例如谷歌、YouTube 和 Facebook,这些应用的性能将它们与类似应用区分开来。 想一下。 如果谷歌需要超过 10 秒的时间来提供搜索结果,大多数人会切换到必应或其他搜索引擎。 因此,性能是应用成功的主要因素之一。

在本章中,我们将学习以下内容:

*   分析应用性能问题的方法
*   如何利用浏览器开发工具来分析应用的性能
*   UI 层的性能改进
*   web/应用层的性能改进
*   数据库层的性能改进

通常,当人们谈论应用的性能时,他们考虑的是应用的速度。 虽然速度对应用的性能有很大的贡献,但是我们还需要考虑应用的可维护性、可伸缩性和可重用性。

一个维护良好的代码将是清晰的,并且具有较少的技术债务,这反过来又将提高开发人员的生产力。 当我们编写基于面向服务的体系结构或微服务的代码时,我们的代码将更便于他人使用。 这也将使我们的代码具有可伸缩性。

通常,人们在几乎完成应用的开发时才考虑应用的性能,试点用户还在抱怨应用的速度。 讨论性能的最佳时机是在应用开发之前; 我们需要与产品所有者、业务分析师和实际用户合作,以达到应用可接受的性能水平的标准。 然后,我们以这个预期的性能水平作为目标来设计和编码。

这也取决于应用的领域。 例如,任务关键型医疗保健应用将要求很高的性能(它们可能期望在不到一秒的时间内得到响应),而后台应用的性能要求可能没有那么高。 因此,了解我们工作的领域是至关重要的。

如果要求您调优现有应用的性能,那么了解应用的现有体系结构也很重要。 ASP.NET Core,您可以将一个简单的 CRUD 应用构建为一个服务于世界各地数百万用户的关键任务应用。 一个大型应用可能有许多其他组件,例如负载平衡器、独立的缓存服务器、**内容分发网络**(**CDN**)、一组从数据库服务器,等等。 因此,在分析应用的性能时,首先需要研究体系结构,分析涉及的每个组件,度量每个组件的性能,并在应用不能满足您的可接受性能时尝试优化它们。 最重要的是不要在不研究和分析应用的架构的情况下就急于研究性能改进技术。 如果您正在创建一个新的应用,那么您可以从创建应用的一开始就考虑性能问题。

我们将研究一个典型的 web 应用设置,如下面的截图所示。 然后我们将分析它并考虑如何改进它:

![Improving Performance of an ASP.NET Core Application](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00219.jpg)

以下步骤展示了使用 web 应用的过程:

1.  用户访问 ASP.NET 来自浏览器的核心 web 应用,如 Internet Explorer、Firefox 或 Chrome。 当用户在浏览器中输入 URL 并按下*Enter*键时,浏览器将创建一个会话并触发 HTTP 请求。 这不是 ASP 特有的.NET Core 应用。 这种行为对于所有 web 应用都是相同的,不管它们是基于何种技术构建的。
2.  请求到达 web 服务器。 如果是一个简单的请求,web 服务器本身将服务该请求。 提供静态 HTML 文件就是一个典型的例子。 如果请求有点复杂,例如根据业务逻辑返回一些数据,则请求将被转发到应用服务器。
3.  应用服务器将查询数据库以获取数据。 然后,在将数据返回给 web 服务器之前,它可能会对接收到的数据进行一些业务处理。 有时,web 服务器可能充当较小的 web 应用的应用服务器。
4.  然后,web 服务器将响应(通常是 HTML)返回给请求的客户端。

因此,我们可以将这些组件分为三层——UI 层、web/应用层和 DB 层。 关于提高 ASP 的整体性能.NET Core 应用,我们需要彻底了解如何提高每个层的性能。

在实现任何性能改进技术之前,我们需要首先分析应用中每个层的性能。 只有这样,我们才能提出改进应用整体性能的方法。

# UI 层

UI 层表示浏览器和服务器之间发生的所有事件(以及相关内容)。 有很多活动,包括但不限于以下:

*   触发 HTTP 请求
*   得到的响应
*   下载的资源
*   在浏览器中呈现它们
*   任何 JavaScript 代码执行

# 减少 HTTP 请求数量

一个典型的网页可能不只有 HTML 内容。 它可能引用 CSS 文件、JS 文件、图像或其他源。 因此,当您试图访问一个网页时,客户端将为每个引用触发 HTTP 请求,并将这些引用从服务器下载到客户端。

当您想要分析从客户机发出的 HTTP 请求时,浏览器开发工具就派上用场了。 大多数浏览器都有可以使用的开发工具。

在 ie 浏览器中按*F12*,ie 浏览器窗口下方会弹出**开发人员工具**窗口,如下图所示:

![Reducing the number of HTTP requests](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00220.jpg)

点击**网络**选项卡。 在浏览器中输入 URL 前,点击*Start*按钮(绿色播放按钮),或者点击绿色播放按钮刷新页面:

![Reducing the number of HTTP requests](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00221.jpg)

一旦你按下**网络**选项卡的开始按钮,Internet Explorer 的**网络**选项卡将侦听当前选项卡发出的每个请求。 每个请求将包含信息,例如 URL、协议、方法、结果(HTTP 状态代码)和其他信息。

我再次运行应用(**跟踪网络请求**选项 ON),我可以看到正在跟踪的请求,如下截图所示:

![Reducing the number of HTTP requests](https://github.com/OpenDocCN/freelearn-csharp-zh/raw/master/docs/learn-aspdn-mvc-prog/img/Image00222.jpg)

在 Network 选项卡中有许多有用的数据。 首先,URL 列显示正在访问的资源。 协议列,顾名思义,显示了访问资源所使用的协议。

首先,URL 列显示正在访问的资源。 协议列,顾名思义,显示了访问资源所使用的协议。 Method 列显示了请求的类型,在 Result 列中,我们可以看到请求的 HTTP 状态码(HTTP 200 响应意味着 GET 请求成功)。

Type 列显示正在访问的资源类型,Taken 列显示从服务器接收文件所花费的时间。 Received 列显示作为请求的一部分下载的文件的大小。

## 使用 GZip 压缩

在提供内容时,可以使用 GZip 压缩内容,以便通过网络发送更少的数据。 您需要添加适当的 HTTP 头,以便浏览器能够理解所传递的内容的模式。 在 IIS 中,默认情况下为静态资源启用此选项。 您可以通过访问路径`C:\Windows\System32\inetsrv\config`上的`applicationHost.config`文件来验证这一点:

```

如果它在您的applicationHost.config文件中不可用,您必须进行必要的更改。

使用内容分发网络(CDN)

内容交付网络是一个分布在全球各地的分布式服务器系统,根据访问内容的地理位置为内容提供服务。 亚马逊的CloudFront就是 CDN 的一个例子。 亚马逊在世界各地都有边缘位置(服务器所在的位置),因此可以从最近的位置向用户提供内容。

在接下来的一行中,我们从 jQuery 官方网站提供的 CDN 中访问 jQuery:

<script src="https://code.jquery.com/jquery-3.1.1.min.js" ></script>

尽可能使用 JavaScript

如果可以使用 JavaScript 实现某个功能,那么就使用它。 例如,在服务器上验证表单数据之前,总是尝试先进行客户端验证。 这种方法有两个优点—站点将非常快,因为所有事情都是在客户端本身完成的;服务器将处理大量的请求,因为有些请求是在客户端处理的。

Using JavaScript wherever possible

使用 CSS 样式表

随着浏览器逐渐呈现网页(浏览器将显示它所拥有的任何内容,一旦它收到它),最好把样式表放在顶部而不是在网页的末尾。 如果我们将样式表放在底部,它将禁止渐进式呈现,因为浏览器必须使用样式重新绘制内容。

当下载 JavaScript 文件时,大多数浏览器都会阻止并行下载,所以最好将脚本放在底部。 这意味着您的内容在浏览器下载脚本时显示给用户。 下面是在 ASP 中创建的示例布局文件.NET Core 应用,CSS 文件被引用在顶部,JavaScript 文件被引用在底部:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>

    <!-- Latest compiled and minified CSS -->

    <link rel="stylesheet"  href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">

    <!-- Optional theme -->

    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">

  </head>
  <body>
    <div>
      @RenderBody()
    </div>
    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.2.3.js"></script>

    <script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/jquery.validate.min.js"></script>

    <script src="https://ajax.aspnetcdn.com/ajax/mvc/5.2.3/jquery.validate.unobtrusive.min.js"></script>

    <!-- Latest compiled and minified JavaScript -->
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>

  </body>
</html>

最小化 JavaScript 和 CSS 文件及其组合

下载网页相关资源所花费的时间与所下载文件的大小成正比。 如果我们减少文件的大小而不改变实际的内容,它将大大提高性能。 缩小是为了减少文件的大小而改变文件内容的过程。 删除多余的空格和将变量名更改为更短的名称都是最小化过程中使用的常见技术。

流行的 JavaScript 库,如 jQuery 和前端框架,默认情况下提供了最小化的文件。 你可以直接使用它们。 在下面的截图中,我下载了 jQuery 的压缩版本。 你可以减少你为你的应用编写的自定义 JavaScript 和 CSS 文件:

Minification of JavaScript and CSS files and their combination

捆绑是将两个或多个文件合并为一个的过程。 捆绑和缩小,当一起使用时,将减少有效载荷的大小,从而提高应用的性能。

你可以通过以下网址安装Bundler&MinifierVisual Studio 扩展:

https://visualstudiogallery.msdn.microsoft.com/9ec27da7-e24b-4d56-8064-fd7e88ac1c40

一旦你安装这个 Visual Studio 扩展,你可以选择你想要的文件包和贬低通过选择文件并选择&打包机缩小镜上下文选项菜单,单击右键。 如下截图所示:

Minification of JavaScript and CSS files and their combination

一旦你选择捆绑和最小化文件选项,它会要求你保存捆绑的文件,如下截图所示:

Minification of JavaScript and CSS files and their combination

你可以命名你希望的文件和保存文件。 一旦您保存了该文件,另一个文件将在您的解决方案中创建—在我们的例子中,它是bundleconfig.json文件:

Minification of JavaScript and CSS files and their combination

该文件将包含关于输入文件和绑定的输出文件的信息。 下面就是这样一个例子:

[
  {
    "outputFileName": "wwwroot/css/site.min.css",
    "inputFiles": [
    "wwwroot/css/site.css"
    ]
  },

{
  "outputFileName": "wwwroot/js/site.min.js",
  "inputFiles": [
  "wwwroot/js/site.js"
  ],
  "minify": {
  "enabled": true,
  "renameLocals": true
  }
},

{
  "outputFileName": "wwwroot/css/bundle.css",
  "inputFiles": [
  "wwwroot/css/site.css",
  "wwwroot/css/StyleSheet1.css"
    ]
  }
]

您可以在应用中使用这个绑定文件,从而提高性能。

缓存过程

缓存是复制数据并将其保存在内存中的过程,而不是通过外部资源(如网络、文件或数据库)再次获取数据。 缓存中使用的数据是临时的,可以随时删除。 由于我们直接访问数据,缓存可以极大地提高应用的性能。

缓存可以在任何一层中完成——在浏览器的客户端,在代理服务器(或某些中间件),或在 web/应用服务器上。 对于数据库层缓存,我们可能不需要进行任何自定义编码。 根据所使用的数据库服务器的类型,您可能需要进行一些配置更改。 然而,现在的大多数数据库都足够强大,可以在需要时缓存数据。

客户端缓存

如果添加适当的 HTTP 响应头,就可以在客户端进行缓存。 例如,如果我们想缓存所有的静态资源,如 CSS、图像和 JavaScript 文件,我们可以在cache - control头中添加max-age响应头:

Client-side caching

前截图开发工具窗口网络选项卡中,当请求被解雇,我们得到 HTTP**304 响应(不修改)作为响应。 这意味着相同的文件不会通过网络传输两次,因为它们可以在浏览器中使用。

为静态文件实现浏览器缓存非常简单,只需要几个步骤—添加依赖项和配置应用。

project.json文件的依赖项列表中添加以下NuGet包:

"Microsoft.AspNet.StaticFiles": "1.0.0-rc1-final" 

Startup.cs文件中添加以下命名空间,并配置应用使用这些静态文件:

using Microsoft.AspNet.StaticFiles;
using Microsoft.Net.Http.Headers;

public void Configure(IApplicationBuilder app)
{
  app.UseIISPlatformHandler();
  app.UseMvc();
  app.UseMvc(routes =>
  {
    routes.MapRoute(name:"default", template:"{controller=Employee}/{action=Index}/{id?}");});

 app.UseStaticFiles(new StaticFileOptions

() 

 { 

      OnPrepareResponse = (context) => 

 { 

 var headers = context.Context.Response.GetTypedHeaders(); 

 headers.CacheControl = new CacheControlHeaderValue() 

 { 

 MaxAge = TimeSpan.FromSeconds(60), 

 }; 

} 

 }); 

}

响应缓存

在响应缓存中,当 MVC 操作返回时,与缓存相关的 HTTP 头被添加到 HTTP 响应中。 Cache-Control 报头是添加到响应中的主 HTTP 报头。

Response caching

上图显示了响应缓存的实际情况。 在第一个请求中,我们调用了 Controller 的 action 方法; 请求来自客户端,并通过代理服务器,实际上击中 web 服务器。 由于我们已经添加了响应缓存,任何后续的请求将不会被转发到 web 服务器,而响应将从代理服务器本身返回。 这将减少对 web 服务器的请求数量,从而减少 web 服务器的负载。

缓存控制器的动作方法的响应非常简单。 只需添加带有 duration 参数的ResponseCache属性。 在下面的 action 方法中,我们添加了一个持续时间为 60 秒的响应缓存,这样,在接下来的 60 秒内,如果有任何请求再次出现,响应将从代理服务器本身返回,而不是前往 web 服务器:

[ResponseCache(Duration = 60)]
public IActionResult Index()
{
  EmployeeAddViewModel employeeAddViewModel = new   EmployeeAddViewModel();
  using (var db = new EmployeeDbContext())
  {
    employeeAddViewModel.EmployeesList = db.Employees.ToList();
  }
  return View(employeeAddViewModel);
}

web/应用层

web/application 层由从客户端接收请求到返回响应(或查询 DB 层以获得所需数据)之间发生的任何事情组成。 大多数 web/application 层将使用服务器端语言,比如 c#,所以当你试图优化 web/application 层时,你需要结合 ASP 的最佳实践.NET MVC 和 c#。

视图中没有业务逻辑

视图是呈现给浏览器的内容,它可以包含表示逻辑。 表示逻辑表示数据显示的位置和方式。 ViewModels(实际上,特定于视图的模型)是保存特定视图数据的模型。

视图和 ViewModels 都不应该包含任何业务逻辑,因为这违反了关注点分离原则。

看看下面的 Razor View 代码。 我们只是循环遍历模型中的列表,并以表格格式显示数据——仅此而已:

<h4> List of employees:</h4> <br />
  <table class="table table-bordered">
  <tr>
    <th> ID </th>
    <th> Name </th>
    <th> Designation </th>
    <th> Salary </th>
  </tr>
  @foreach (var employee in Model.EmployeesList)
  {
  <tr>
    <td>@employee.EmployeeId</td>
    <td>@employee.Name</td>
    <td>@employee.Designation</td>
    <td>@employee.Salary</td>
  </tr>
  }
  </table>

在某些代码中,ViewModel 中可能有一个存储库层,这是不应该发生的。 请格外小心查看/ViewModel 代码中的内容。

使用异步日志记录

尽可能使用异步日志记录来提高性能。 大多数日志框架,如Log4Net,都提供了异步日志记录的选项。 关于 ASP.NET Core,你可以通过依赖注入来实现日志记录。

下面是一个在 MVC 控制器中实现日志框架的典型例子:

public class EmployeeController : Controller
{
  private readonly IEmployeeRepository _employeeRepo;
  private readonly ILogger<EmployeeController> _logger;
  public EmployeeController(IEmployeeRepository employeeRepository,
  ILogger<EmployeeController> logger)
  {
    _employeeRepo = employeeRepository;
    _logger = logger;
  }
  [HttpGet]
  public IEnumerable<Employee> GetAll()
  {
    _logger.LogInformation(LoggingEvents.LIST_ITEMS, "Listing all employees");
    return _employeeRepo.GetAll();
  }
}

DB 层

虽然 DB 层与 ASP 没有直接关系.NET Core 应用,开发人员有责任完全拥有应用的性能,这也包括关注数据库的性能。 现在我们来看看 DB 层中我们在提高 ASP 性能时需要考虑的几个方面.NET Core 应用。

了解 ORM 生成的查询

在目前的大多数应用中,我们使用对象关系映射(ORM),如实体框架(Entity Framework)或NHibernate。 您可能知道,ORM 的主要目标是使您能够使用基于域的类和对象编写数据访问层,而不是直接编写查询。 然而,这并不意味着您永远不需要了解生成的 SQL 查询的基础知识,或者这些查询的优化。 有时,实体框架生成的查询可能没有得到优化,因此更好的做法是运行分析器,分析生成的查询,并根据您的需要对它们进行调优。 您可以使用实体框架中的拦截器来记录 SQL 查询。

使用经典 ADO.NET,如果你真的想

ASP.NET Core 只是一个 web 开发框架,它与任何数据访问框架或技术无关。 如果您在应用中使用的 ORM 不支持您期望的性能,您可以使用经典的 ADO.NET 并手动编写查询/存储过程。

只返回所需的数据

总是只返回您需要的数据,一点也不多,一点也不少。 这种方法减少了我们通过网络(从数据库服务器到 web/应用服务器)发送的数据。

例如,我们不会使用以下语句:

Select * from employees

相反,我们应该这样使用:

Select FirstName,LastName from employees

后一个查询将只从表中获得必需的字段,因此,只有必需的数据被传递给调用客户机。

微调指标

初学者在遇到数据库问题时倾向于添加索引。 为表中的每一列添加索引是一种糟糕的做法,而且会降低性能。 正确的方法是获取最频繁执行的查询的列表。 有了这个列表后,尝试对它们进行微调——删除不必要的连接、避免相关的子查询等等。 只有在尝试并用尽了所有查询调优选项之后,才应该开始添加索引。 这里需要注意的重要一点是,您应该只在所需的列数量上添加索引。

为数据库列使用正确的列类型和大小

当您想使用 int 作为列的数据类型时,请使用整数。 不要使用双。 如果表中有很多行,这将节省大量空间。

避免相关子查询

关联子查询使用来自父查询的值,从而使其逐行运行。 这将显著影响查询性能。

下面是一个相关子查询的例子:

SELECT e.Name, 
e.City, 
(SELECT DepartmentName FROM EmployeeDepartment WHERE ID = e.DepartmentId) 
AS DepartmentName 
FROM Employee e

一般的性能改进技巧

下面是一些在 ASP 中提高应用整体性能的指针.NET Core Web 应用。

回避回应。 重定向的方法

当我们想要进行客户端重定向时,开发人员可以使用 URL 作为参数传递来调用Response.Redirect方法。 但这种方法存在一个小问题。 如果我们使用Response.Redirect,浏览器将再次向服务器发送请求,这需要再次往返于服务器。 因此,如果可能,最好避免使用Response.Redirect方法,尽可能使用RedirectToAction方法。

使用字符串生成器

如果您的应用涉及很多字符串操作,最好使用字符串生成器而不是通常的字符串连接。 字符串拼接导致为每个操作创建一个新的字符串对象,而字符串生成器只处理单个对象本身。 当我们在大型字符串操作操作中使用字符串生成器时,我们可以获得更好的性能。

总结

在本章中,我们学习了如何分析 web 应用的性能,以及在提高性能时应以哪些层为目标。 然后我们讨论了如何提高每个层的性能——UI 层、web/应用层和 DB 层。

十二、ASP.NET Core 认证

安全性对于所有类型的应用都是至关重要的,包括 web 应用。 如果有人可以冒充你来更新你的状态,你还会使用 Facebook 吗? 如果这是可能的,那么没有人会再回到 Facebook。 从这个示例中,我们可以看到,安全性与其说是一种特性,不如说是所有应用的必要特性。

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

  • 身份验证和授权
  • ASP。 净的身份
  • 如何在 ASP 中实现安全性。 asp.NET Core 应用使用.NET 身份与实体框架

当我们讨论应用的安全性时,我们主要希望防止任何未经授权的访问,这意味着只有访问信息的人才能够访问它——不多也不多。

在进一步讨论之前,我想澄清一些关于安全性的核心概念。

认证

身份验证是验证用户是否有权访问系统的过程。 在任何应用中,都将首先对用户进行身份验证。 这可以通过要求用户输入他们的用户 ID 和密码来实现。

授权

授权是验证用户是否有权访问所请求的资源的过程。 他们可能拥有对系统的合法访问权,但他们可能没有访问请求的资源的权限,因为他们没有必需的访问权。 例如,只有管理员用户可以访问应用的配置页面,而普通用户不应该被允许使用该页面。

ASP.NET Identity 为保护应用提供了几个特性。

让我们考虑以下简单场景,其中用户试图访问安全页面,该页面只有经过授权的人员才能访问。 由于用户没有登录,它们将被重定向到登录页面,以便我们可以对用户进行身份验证和授权。 认证成功后,用户将被重定向到安全页面。 如果由于任何原因,我们不能对用户进行身份验证和授权,我们可以将他们重定向到“拒绝访问”页面:

Authorization

ASP.NET Core Identity 是一个会员系统,它使您能够轻松地保护应用,并且具有向应用添加登录功能等特性。 以下是使用ASP 需要遵循的步骤.NET Identity(使用实体框架)

  1. 将相关依赖项添加到project.json文件中。
  2. 创建一个appsettings.json文件并存储数据库连接字符串。
  3. 创建一个ApplicationUser类和ApplicationDbContext类。
  4. 配置应用以使用 ASP。 净的身份。
  5. 创建用于注册和登录的 ViewModels。
  6. 创建必要的控制器和关联的操作方法和视图。

向项目中添加相关的依赖项。 json 文件

如果你想使用 ASP.NET Identity with Entity Framework 在你的应用中,你需要添加以下依赖:

"EntityFramework.Commands": "7.0.0-rc1-final", 
    "EntityFramework.MicrosoftSqlServer": "7.0.0-rc1-final", 
    "Microsoft.AspNet.Authentication.Cookies": "1.0.0-rc1-final", 

创建一个appsettings.json文件并存储数据库连接字符串。

在项目的根级别创建一个名为appsettings.json的文件,如下截图所示:

Adding the relevant dependencies to the project.json file

将以下连接字符串存储在appsettings.json中。 这个连接字符串将被 ASP.NET Identity 将数据存储在相关的表中:

{ 
  "Data": { 
    "DefaultConnection": { 
      "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=aspnet_security;Trusted_Connection=True;MultipleActiveResultSets=true" 
   } 
  } 
} 

添加 ApplicationUser 和 ApplicationDbContext 类

创建一个Models文件夹和两个文件——ApplicationDbContext.csApplicationUser.cs -,如下截图所示:

Adding ApplicationUser and ApplicationDbContext classes

ApplicationUser类继承自IdentityUser类(在AspNet.Identity.EntityFramework6命名空间中可用),如下所示:

public class ApplicationUser : IdentityUser 
{
..  
} 

您可以根据应用的需要向用户添加属性。 我没有添加任何属性,因为我想保持事情简单,以显示 ASP 的功能。 净的身份。

ApplicationDbContext类继承自ApplicationUserIdentityDbContext类。 在构造函数方法中,传递connectionstring,最终传递给基类。

甚至OnModelCreating方法也被覆盖了。 如果你想改变任何表名(由 Identity 生成),你可以这样做:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser> 
    { 
        public ApplicationDbContext(string nameOrConnectionString) : base(nameOrConnectionString) { } 

        protected override void OnModelCreating(DbModelBuilder modelBuilder) 
        { 
            base.OnModelCreating(modelBuilder);             
        } 
    } 

创建Models文件之后,需要配置应用和服务。 您可以在ConfigureConfigureServices中配置这些,它们在Startup类中找到。

配置应用使用身份

为了使用 Identity,我们只需要在Startup类的Configure方法中添加以下一行:

app.UseIdentity(); 

完整的Configure方法如下所示,同时调用UseIdentity方法,即app.UseIdentity():

public void Configure(IApplicationBuilder app, IHostingEnvironment env,  ILoggerFactory loggerFactory) 
        { 
            loggerFactory.AddConsole(Configuration.GetSection("Logging")); 
            loggerFactory.AddDebug(); 

            if (env.IsDevelopment()) 
            { 
                app.UseBrowserLink(); 
                app.UseDeveloperExceptionPage(); 
                app.UseDatabaseErrorPage(); 
            } 
            else 
            { 
                app.UseExceptionHandler("/Home/Error"); 

            app.UseIISPlatformHandler(options => options.AuthenticationDescriptions.Clear()); 

            app.UseStaticFiles(); 

            app.UseIdentity(); 

            // To configure external authentication please see http://go.microsoft.com/fwlink/?LinkID=532715 

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

ConfigureServices方法中,我们将做以下改动:

  • 我们将使用从appsettings.json文件获取的连接字符串添加ApplicationDbContext

  • 我们将添加身份与UserStoreRoleStore

  • 最后,我们将问 ASP.NET Core 返回AuthMessageSender每当我们请求IEmailSenderISMSSender

    public void ConfigureServices(IServiceCollection services 
    { 
    // Add framework services. 
    
                services.AddScoped<ApplicationDbContext>(f => { 
                    return new ApplicationDbContext(Configuration["Data:DefaultConnection:ConnectionString"]); 
                }); 
    
                services.AddIdentity<ApplicationUser, IdentityRole>() 
                    .AddUserStore<UserStore<ApplicationUser, ApplicationDbContext>>() 
                    .AddRoleStore<RoleStore<ApplicationDbContext>>() 
                    .AddDefaultTokenProviders(); 
    
                services.AddMvc(); 
    
                // Add application services. 
                services.AddTransient<IEmailSender, AuthMessageSender>(); 
                services.AddTransient<ISmsSender, AuthMessageSender>(); 
            } 
    
    

创建视图模型

接下来,我们将创建几个 viewmodel,它们将在我们的 Views 模型中使用。

首先,我们将创建一个包含三个属性——EmailPasswordConfirmPasswordRegisterViewModel类。 我们用适当的属性装饰属性,这样我们就可以使用一个不引人注目的 jQuery 验证来使用客户端验证。 我们将所有字段的要求如下:

public class RegisterViewModel 
    { 
        [Required] 
        [EmailAddress] 
        [Display(Name = "Email")] 
        public string Email { get; set; } 

        [Required] 
        [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] 
        [DataType(DataType.Password)] 
        [Display(Name = "Password")] 
        public string Password { get; set; } 

        [DataType(DataType.Password)] 
        [Display(Name = "Confirm password")] 
        [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] 
        public string ConfirmPassword { get; set; } 
    } 

现在,我们可以创建LoginViewModel model,用户可以使用它登录到您的应用。 还有一个额外的属性RememberMe,勾选该属性后,您无需再次输入密码即可登录:

public class LoginViewModel 
    { 
        [Required] 
        [EmailAddress] 
        public string Email { get; set; } 

        [Required] 
        [DataType(DataType.Password)] 
        public string Password { get; set; } 

        [Display(Name = "Remember me?")] 
        public bool RememberMe { get; set; } 
    } 

创建控制器和关联的操作方法

现在我们需要创建一个AccountController类,在其中我们将定义用于身份验证和授权的操作方法:

public class AccountController : Controller 
    { 
        private readonly UserManager<ApplicationUser> _userManager; 
        private readonly SignInManager<ApplicationUser> _signInManager; 
        private readonly IEmailSender _emailSender; 
        private readonly ISmsSender _smsSender; 
        private readonly ILogger _logger; 

        public AccountController( 
            UserManager<ApplicationUser> userManager, 
            SignInManager<ApplicationUser> signInManager, 
            IEmailSender emailSender, 
            ISmsSender smsSender, 
            ILoggerFactory loggerFactory) 
        { 
            _userManager = userManager; 
            _signInManager = signInManager; 
            _emailSender = emailSender; 
            _smsSender = smsSender; 
            _logger = loggerFactory.CreateLogger<AccountController>(); 
        } 
    } 

在前面的代码中,我们使用由不同组件提供的服务。 UserManagerSignInManager由 ASP 提供。 净的身份。 IEmailSenderISmsSender是我们编写的用于发送电子邮件和短信的自定义类。 我们将在本章后面更多地讨论电子邮件和 SMS。 日志记录是由 Microsoft Logging 扩展提供的。 下面是一个简单的登录HTTPGET方法。 它只是存储从Login方法访问的 URL,并返回登录页面:

[HttpGet] 
        [AllowAnonymous] 
        public IActionResult Login(string returnUrl = null) 
        { 
            ViewData["ReturnUrl"] = returnUrl; 
            return View(); 
        } 

创建视图

现在,我们将为登录创建各自的 View 页面。 在这个视图页面中,我们只显示以下细节:

@using System.Collections.Generic 
@using Microsoft.AspNet.Http 
@using Microsoft.AspNet.Http.Authentication 
@using AspNet.Identity.EntityFramework6 

@model LoginViewModel 
@inject SignInManager<ApplicationUser> SignInManager 

@{ 
    ViewData["Title"] = "Log in"; 
} 

<h2>@ViewData["Title"].</h2> 
<div class="row"> 
    <div class="col-md-8"> 
        <section> 
            <form asp-controller="Account" asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal" role="form"> 
                <h4>Use a local account to log in.</h4> 
                <hr /> 
                <div asp-validation-summary="ValidationSummary.All" class="text-danger"></div> 
                <div class="form-group"> 
                    <label asp-for="Email" class="col-md-2 control-label"></label> 
                    <div class="col-md-10"> 
                        <input asp-for="Email" class="form-control" /> 
                        <span asp-validation-for="Email" class="text-danger"></span> 
                    </div> 
                </div> 
                <div class="form-group"> 
                    <label asp-for="Password" class="col-md-2 control-label"></label> 
                    <div class="col-md-10"> 
                        <input asp-for="Password" class="form-control" /> 
                        <span asp-validation-for="Password" class="text-danger"></span> 
                    </div> 
                </div> 
                <div class="form-group"> 
                    <div class="col-md-offset-2 col-md-10"> 
                        <div class="checkbox"> 
                            <input asp-for="RememberMe" /> 
                            <label asp-for="RememberMe"></label> 
                        </div> 
                    </div> 
                </div> 
                <div class="form-group"> 
                    <div class="col-md-offset-2 col-md-10"> 
                        <button type="submit" class="btn btn-default">Log in</button> 
                    </div> 
                </div> 
                <p> 
                    <a asp-action="Register">Register as a new user?</a> 
                </p> 
                <p> 
                    <a asp-action="ForgotPassword">Forgot your password?</a> 
                </p> 
            </form> 
        </section> 
    </div> 

</div> 

@section Scripts { 
    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } 
} 

Creating Views

当用户第一次登录到应用时,他们可能没有任何登录凭据,因此我们的应用应该提供一个特性,用户可以使用它为自己创建登录。 我们将创建一个简单的Register操作方法,它将返回一个用户可以注册自己的视图:

[HttpGet] 
[AllowAnonymous] 
public IActionResult Register() 
{ 
    return View(); 
} 

我们还将创建相应的 View,其中包含电子邮件、密码、密码确认和Register按钮的输入控件:

@model RegisterViewModel 
@{ 
    ViewData["Title"] = "Register"; 
} 

<h2>@ViewData["Title"].</h2> 

<form asp-controller="Account" asp-action="Register" method="post" class="form-horizontal" role="form"> 
    <h4>Create a new account.</h4> 
    <hr /> 
    <div asp-validation-summary="ValidationSummary.All" class="text-danger"></div> 
    <div class="form-group"> 
        <label asp-for="Email" class="col-md-2 control-label"></label> 
        <div class="col-md-10"> 
            <input asp-for="Email" class="form-control" /> 
            <span asp-validation-for="Email" class="text-danger"></span> 
        </div> 
    </div> 
    <div class="form-group"> 
        <label asp-for="Password" class="col-md-2 control-label"></label> 
        <div class="col-md-10"> 
            <input asp-for="Password" class="form-control" /> 
            <span asp-validation-for="Password" class="text-danger"></span> 
        </div> 
    </div> 
    <div class="form-group"> 
        <label asp-for="ConfirmPassword" class="col-md-2 control-label"></label> 
        <div class="col-md-10"> 
            <input asp-for="ConfirmPassword" class="form-control" /> 
            <span asp-validation-for="ConfirmPassword" class="text-danger"></span> 
        </div> 
    </div> 
    <div class="form-group"> 
        <div class="col-md-offset-2 col-md-10"> 
            <button type="submit" class="btn btn-default">Register</button> 
        </div> 
    </div> 
</form> 

@section Scripts { 
    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } 
} 

以下是对应的POST动作注册方法。 这里,程序检查模型是否有效,如果有效,它将使用模型数据创建一个ApplicationUser对象,并调用 Identity API(CreateAsync方法)。 如果可以创建user变量,则用户将使用该用户 ID 登录,并被重定向到Home页面:

[HttpPost] 
        [AllowAnonymous] 
        [ValidateAntiForgeryToken] 
        public async Task<IActionResult> Register(RegisterViewModel model) 
        { 
            if (ModelState.IsValid) 
            { 
                var user = new ApplicationUser { UserName = model.Email,  Email = model.Email }; 
                var result = await _userManager.CreateAsync(user,  model.Password); 
                if (result.Succeeded) 
                { 
                    await _signInManager.SignInAsync(user, isPersistent:  false); 
                    return RedirectToAction(nameof(HomeController.Index),  "Home"); 
                } 
                AddErrors(result); 
            } 

            return View(model); 
        } 

登出功能非常简单。 它只需要调用 Identity API 的SignoutAsync方法,并被重定向到Index页面:

[HttpPost] 
        [ValidateAntiForgeryToken] 
        public async Task<IActionResult> LogOff() 
        { 
            await _signInManager.SignOutAsync(); 
            _logger.LogInformation(4, "User logged out."); 
            return RedirectToAction(nameof(HomeController.Index), "Home"); 
       } 

回到登录功能,下面是各自的操作方法。 我们正在调用 Identity API 的PasswordSignInAsync方法。 成功登录后,我们将访问登录功能的 URL 重定向:

[HttpPost] 
        [AllowAnonymous] 
        [ValidateAntiForgeryToken] 
        public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null) 
        { 
            ViewData["ReturnUrl"] = returnUrl; 
            if (ModelState.IsValid) 
            { 
                var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false); 
                if (result.Succeeded) 
                { 
                    return RedirectToLocal(returnUrl); 
                } 

            } 
            // If there is any error, display the form again 
            return View(model); 
        } 

电子邮件和短信服务

如果你想将电子邮件和短信服务添加到你的应用的身份验证功能中,你可以通过创建如下所示的接口和类来实现:

public interface IEmailSender
{
        Task SendEmailAsync(string email, string subject, string message)
  }
  public interface ISmsSender
    {
        Task SendSmsAsync(string number, string message);
    }
public class AuthMessageSender : IEmailSender, ISmsSender
    {
        public Task SendEmailAsync(string email, string subject, string message)
        {
            // We can plug in our email service here to send an email.
            return Task.FromResult(0);
        }
        public Task SendSmsAsync(string number, string message)
        {
            // We can plug in our SMS service here to send a text message.
            return Task.FromResult(0);
        }
    }

在控制器中固定操作方法

为了便于解释,让我们假设About页面是一个安全页面,只有经过身份验证的用户才能访问它。

我们只需要用[Authorize]属性装饰Home控制器中的About动作方法:

[Authorize] 
        public IActionResult About() 
        { 
            ViewData["Message"] = "This is my about page"; 
            return View(); 
        } 

当用户试图访问登录页面而不登录应用时,进行上述更改将把用户重定向到登录页面:

Securing an action method in a Controller

在下面的屏幕截图中,您将注意到 URL 中有一个额外的查询参数ReturnURL,。 这个ReturnURL参数将应用重定向到该特定的页面(在本例中,通过ReturnURL参数—Home/About传递的值)。

一旦您登录,您将被重定向到您先前请求的页面:

Securing an action method in a Controller

当您注册一个新用户时,用户的详细信息将存储在由 ASP 创建的相关表中。 净的身份。

打开 SQL Server 对象管理器窗口,选择View|SQL Server 对象管理器,如下图所示:

Securing an action method in a Controller

一旦您选择SQL Server 对象管理器选项,您将看到一个类似如下截图的窗口。 ASP.NET Identity 使用实体框架和前面在appsettings.json包中提供的连接字符串为我们创建数据库。

ASP.NET Identity 创建几个表来维护与身份相关的信息和实体框架的数据库迁移历史。 因为我们使用 ASP.NET 身份在基本级别上,除了dbo 之外,没有任何与身份相关的表会被填充。 AspNetUsers.:

Securing an action method in a Controller

您可以右键单击dbo。 AspNetUsers表,选择View Data查看数据:

Securing an action method in a Controller

由于在我们的应用中只注册了一个用户,所以只创建了一行。 请注意,散列密码(由 ASP 标记.NET Identity 为我们),并且表中不会存储空白密码。

总结

在本章中,我们学习了身份验证和授权。 我们还学习了如何实现 ASP。 asp.net 标识.NET Core 应用,遵循一个循序渐进的过程。 我们还讨论了 ASP 中涉及到的表.NET 身份,并学习了如何查看由 asp.net 创建的数据。 净的身份。

posted @ 2025-10-21 10:43  绝不原创的飞龙  阅读(3)  评论(0)    收藏  举报