ASP-NET-Core-基础知识-全-

ASP.NET Core 基础知识(全)

原文:ASP.NET Core Essentials

协议:CC BY-NC-SA 4.0

零、前言

Web 应用在企业应用开发中蓬勃发展,即使移动应用继续流行。 此外,web api 的使用提供了更大的灵活性,当涉及到构建一个后端,可以同时服务于 web 应用和移动应用。 ASP.NET Core 1.0,你可以从成熟的编程语言(如 c#)中获得好处,它具有全新的。NET Core 的性能,允许你的 web 应用和 web api 在 Windows、Mac 和 Linux 上运行。

ASP.NET Core 允许您在开发期间选择操作系统,无论您是在 Surface Book、MacBook Air 还是在流行的 Linux 发行版上编写代码。 虽然本书主要涵盖了在 Windows 系统上使用 Visual Studio 2015 进行开发,但如果你希望在不同的操作系统上设置环境,你可以找到在线指南的链接。

ASP.NET Core 1.0 Essentials 将向你介绍微软的最新版本.NET,以及你今天开始所需要的一切。 如果你已经用过 ASP.NET MVC 之前,您将得到一些您已经知道的东西,然后是新的东西。 如果你只使用 ASP.NET Web Forms 或其他竞争的 Web 框架,你会发现最初的章节非常有用,为你需要知道的东西奠定了基础。

这本书的内容

第一章ASP 入门 asp.net Core,教你如何使用 ASP.NET Core 1.0,包括 MVC 和 web API。 本章将解释完整的。net Framework 4.6 和。net Core 之间的区别,同时介绍 Visual Studio 2015 和全新的跨平台 Visual Studio Code。

第二章asp.NET Core 应用,展示了如何创建一个新的 asp.NET Core 应用.NET Core web 应用。 本章将剖析现代 ASP 的各个部分.NET web 应用,同时解释什么是新的和什么改变了。

第三章,了解 MVC,将会让您了解 ASP.NET Core MVC 通过深入到控制器、视图和模型。 本章将解释如何创建一个现代 MVC 应用的所有部分,然后将它们结合在一起。

第四章,Using Web API to Extend Your Application,是关于使用 Web API 来扩展你的 Web 应用来支持 Web 和移动应用。 本章将解释如何创建路由和配置 web API 应用,然后从客户端应用使用它。

第五章,使用 JavaScript 与 Web API 交互,教你如何与 ASP 交互.NET web API 使用直接的 JavaScript 以及流行的框架,如 AngularJS 和 KnockoutJS。 本章还将介绍开发人员工具,这些工具可以在开发过程中自动化重要的任务和帮助。

第 6 章,使用实体框架在代码中与数据库交互,教你如何在 web 应用的数据层中使用实体框架。 本章将介绍对象关系映射(ORM)工具和 EF 代码优先迁移的使用。

第七章,依赖注入和健壮 Web 应用的单元测试,展示了如何使用控制反转实现依赖注入。 本章将介绍 DI、IoC 容器,以及微软对依赖注入的新内置支持。 本章还将涵盖单元测试的基础知识。

第 8 章认证、授权和安全是关于在 ASP 中启用认证的.NET web 应用,同时实现特定应用特性的授权。 本章还将涵盖保护用户数据的指导方针,以及对抗 web 应用中常见安全漏洞的策略。

第九章,部署你的应用,讲述了如何将你的应用部署到 web 服务器上的 IIS 或者微软的 Azure 云平台上。 本章还将介绍如何使用持续集成来运行单元测试和部署成功编译的应用。

你需要什么来写这本书

学习 ASP.NET Core 和运行代码示例为本书,推荐使用以下软件:

  • Windows 7 或更高版本(推荐 Windows 10)
  • Visual Studio 2015 Update 3 或更高版本(免费社区版可以)
  • 任何现代网络浏览器,如 Microsoft Edge,谷歌 Chrome,或 Mozilla Firefox
  • 可选:Visual Studio Code (Windows, Mac, Linux)

有关开发和运行 ASP 的更多信息。 在 Windows 以外的系统上运行 asp.net Core,请参考运行 ASP.NET Core on Windows, Mac OS X, Linuxsection inChapter 1NET Core。 在那里,你会看到 Mac 和 Linux 操作说明的高级概述,还有经常更新的在线指南链接。

这本书是写给谁的

这本书是为那些有。net 经验的软件开发人员编写的,最好使用 c#或其他一些面向对象的编程语言,这些语言是构建 ASP 所必需的.NET Core web 应用。 对 web 应用开发的基本理解也是必不可少的。

约定

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

文本中的代码字、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 url、用户输入和 Twitter 句柄如下所示:“使用 HTTPGET方法和可选的querystring参数”

一段代码设置如下:

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

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

dotnet restore
dotnet build
dotnet ef migrations add Initial
dotnet ef database update

新词语、重要词语以粗体显示。 您在屏幕上看到的字,例如菜单或对话框中,会出现如下文本:“输入一些值并单击Create按钮。”

注意事项

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

提示

提示和技巧是这样的。

读者反馈

我们欢迎读者的反馈。 让我们知道你对这本书的看法——你喜欢或不喜欢这本书。 读者反馈对我们来说很重要,因为它能帮助我们开发出你能真正从中获益最多的游戏。 要给我们发送一般性的反馈,只需发送电子邮件 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/ASPdotNET-Core-Essentials。 我们在https://github.com/PacktPublishing/中还提供了丰富的图书和视频目录中的其他代码包。 检查出来!

勘误表

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

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

盗版

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

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

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

问题

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

一、开始使用 ASP.NET Core

活动服务器页面于 20 世纪 90 年代中期首次在微软的 IIS web 服务器上提供。 快进 20 年左右,ASP。 asp.net Core(即 asp.net; 现在,Visual Studio是开放源代码的,可以在 Windows、Linux 和 OS x 上运行。此外,除了 Windows 上的全功能 IDE 外,Visual Studio现在是一个跨平台的轻量级代码编辑器。

作为一个. net的开发人员,您可以利用所有最近的改进,同时构建您已有的经验。 . net Framework 已经超越了完整的框架,现在可以作为一个名为. net Core的跨平台运行时使用。

在本章中,我们将讨论以下原则和概念,让你开始使用 ASP.NET Core:

  • net 架构
  • ASP.NET 统一编程模型
  • 新的 Visual Studio ide
  • 跨平台运行时

NET Core -统一 MVC、Web API 和 Web 页面

当开发人员听到术语ASP。 asp.net,一些人将它与 asp.net 联系起来。 asp.netWeb Forms,而其他人认为asp.netWeb Forms.NET MVC甚至Web API。 许多开发人员已经开始了他们的 Web 表单之旅,并且在最近几年已经迁移到模型-视图-控制器(MVC)。

ASP 的采用。 由于其干净的代码和可测试性,NET MVC 多年来一直在崛起。 而你仍然可以在。net 4 上开发 Web Forms。 x,你将在。net Core 1.0 上使用 MVC。 幸运的是,ASP.NET Core 运行在。NET 4.5.1+和。NET Core 上,所以你可以选择你想要的运行时。

展望未来,微软在 ASP 中统一了 MVC、Web API 和 Web 页面。 净的核心。 这对作为开发者的你来说意味着什么? 这意味着在使用 MVC、Web API 和 Web 页面构建应用时,您将享受到更大的一致性。 在过去,MVC 和 Web API 是分开实现的,这导致了重复和不一致,但使用 ASP.NET Core,它们被合并成一个单一的编程模型。

高层概述

如果你对上述任何一个术语都不熟悉,这里有一个高层次的概述:

  • NET MVC:微软 MVC 架构模式的实现,用于构建健壮且轻量级的测试友好的 web 应用。
  • Web API:微软对 RESTful API 的回应,Web API 允许开发者构建 HTTP 服务,作为 Web 应用和移动应用的后端。
  • 网页:微软创建轻量级动态网站的解决方案。 在撰写本文时,Web 页面在 asp.net Core 1.0 中是不可用的,所以在本书中不会涉及到它。

下图说明了一个典型的 ASP.NET web 应用,使用 MVC 和 web API。 请注意,Web API 可以用于 Web 浏览器以外的客户端,比如移动应用。 值得一提的是,这里的浏览器也可以包括移动网页浏览器:

High-level overview

版本号

您可能已经注意到,我在谈论 ASP 时提到了 MVC 6。 净的核心。 ASP.NET MVC 6 已经被重命名为 ASP .NET Core 1.0 MVC。 为了记录所有的新版本号码,以下列出了你需要知道的信息:

  • c# 6.0
  • ASP.NET Core:
    • 运行在。net Core 1.0 或。net Framework 4.5.1+上
  • .NET Framework 4.6.2(撰写本文时)
  • net 1.0 核心
  • ASP.NET Core 1.0 MVC
  • SignalR 3(在撰写本文时尚未发布)

Version numbers

把它们放在一起

现在您已经熟悉了 ASP 的移动部分.NET Core 和它的各种版本号,你将如何决定使用哪些部分? 好消息是,在你开始你的项目之前,你不需要弄清楚所有的事情。

考虑一下您的 web 软件项目的需求和客户的需求。 选择对你和你的团队有意义的技术,开始着手于最小可行产品(MVP)。

如果你需要支持网页浏览器,使用 ASP.NET MVC,并构建响应性良好的 web 应用,在任何屏幕尺寸上看起来都很好。 如果需要使用 Web API 扩展应用,那么只公开需要公开的内容。

如果您需要实时功能,SignalR是一个很好的起点。 在撰写本文时,微软已经宣布 SignalR 3 将不会包含在 ASP 1.0 发行版中。 净的核心。 因此,本书将不涉及 signalr3。 更多信息请关注 SignalR 网站:http://www.asp.net/signalr

.NET Framework 和.NET Core 的区别

自从。net 框架出现以来,每次都只发布一个版本。 每当有新版本发布时,您都可以升级到最新版本或同时安装多个版本。 但总是有一个最新的版本。

展望未来,现在有两个截然不同的版本:完整的。net 框架和全新的。net Core。 而 ASP.NET Core web 应用可以在其中任何一个平台上运行,您将决定哪一个更适合您的需要。 您可以在开发期间或发布期间更改运行时。 最好的是,您可以将运行时与发布的产品一起部署,这样就可以将. net 的多个版本部署到同一台服务器上。

下面的截图说明了 ASP.NET Core 运行在。NET Framework 4.6 和。NET Core 1.0 上:

Differences between .NET Framework and .NET Core

完整的。net Framework 4.6

即使有了。net Core 的发布,完整的。net 框架仍然有一席之地。 它将继续是富 Windows 桌面应用的首选框架,这些应用是用Windows Presentation Foundation(WPF)或 Windows 窗体创建的。 它将是两个选择之一的 ASP.NET Core 开发人员。

ASP.NET 4 的开发者们,.NET Framework 4.6 将会成为。NET 4 的替代。 x 运行时。 这包括。net 4、4.5、4.5.1 和 4.5.2。 升级到。net 4.6 的一个很好的理由是新的改进带来的好处,比如更好的编译和添加的语言特性。

轻量级。net Core

新的。net Core 是完整的。net 框架的一个轻量级的跨平台子集,可以在 Windows、Linux 和 OS x 上使用。net Framework。预计它将在新的特性中超越。net Framework,这些新特性可能会回到。net Framework。

值得注意的是。net Core 不是 Windows 桌面开发人员或 ASP 的选项。 净 4 开发人员。 除了支持 ASP.NET Core web 应用的CoreCLR运行时,.NET Core 还包括。NET 本机运行时,这是专门用于 Windows 10 上的通用 Windows 应用

运行 ASP.NET Core 和。NET Core

总结一下,让我们关注一下运行 ASP 的以下好处。 .NET Core 上的 web 应用,除了它的跨平台支持:

  • 灵活性:使用。net Core, web 应用可以使用。net Core 框架的特定版本进行部署,这将允许您只使用它所需要的版本来部署每个应用
  • 性能:使用。net Core,由于其更低的内存占用和更快的启动时间,您将享受到性能上的好处

是否运行 ASP。 在完整的。NET 框架或新的。NET Core 中,您将享受到一个现代的应用框架,它使您轻松进入云部署,并通过动态编译促进更快的开发。

Visual Studio 2015 和 Visual Studio Code 有什么新变化?

在每个发布周期中,Visual Studio 都有几个不同的 sku 可用。 从 Visual Studio 2013 开始,微软为学生、开源开发人员和小型团队添加了一个社区版,作为专业版的免费替代品。 除了 Visual Studio 2015,微软还增加了一个名为Visual Studio code的跨平台代码编辑器。

对于开发者来说,以下是 Visual Studio 2015 的现有产品:

  • 社区版
  • 专业版
  • 企业版

您可能还记得 Visual Studio 以前的版本也包括高级版和终极版。 这两个版本已经合并到企业版中。

要了解 Visual Studio 的各个版本,你可以参考以下网址的比较表:

https://www.visualstudio.com/products/vs-2015-product-editions

社区版

在 Community Edition 之前,许多学生、独立开发人员和小型团队使用免费的 Express Edition。 然而,Express Edition 缺少许多 Pro 功能,比如扩展。 现在,通过 Community Edition,非企业开发人员可以获得用于跨平台应用开发的全功能 IDE。

更好的是,社区版为成千上万的 Visual Studio 扩展打开了大门。 如果你没有在Visual Studio Gallery中找到你需要的,你也可以创建你自己的扩展。

专业版和企业版

对于有更多需求的企业开发人员和专业团队,您可以从专业版或企业版中进行选择。 完整的 ide 的功能之外的社区版,专业版也给你进入CodeLens,Team Foundation Server(TFS),敏捷项目规划工具。 CodeLens 提供了对代码历史的更深入的了解,而 TFS 可以用于源代码控制和持续集成。

企业版提供了上述所有功能,以及用于软件架构、建模、测试和代码覆盖的企业级工具。

要详细了解 Visual Studio 的各个版本,您可以参考https://www.visualstudio.com/products/compare-visual-studio-2015-products-vs中的比较表。

Visual Studio 代码

为了给今天的开发者提供更多的选择,微软已经开始提供一个叫做 Visual Studio code 的跨平台代码编辑器。 这个新产品是免费的,适用于您所选择的平台:Windows、Mac OS X 和 Linux。

Visual Studio Code 主要是为 web 和云应用,如 ASP.NET web 应用或 Node.js 后端代码。 但你可以自由地将其用于其他用途,如 Unity 游戏开发和使用 c++、Java、PHP、Python、Ruby 等最流行的语言进行跨平台应用开发。

对于那些想知道的人来说,Visual Studio Code 与流行的Sublime Text有着惊人的相似之处,并且是它的一个很好的替代品。 下面的截图显示了 Visual Studio Code:

Visual Studio Code

Visual Studio 代码:project.json

有关其他语言的更多信息,请参考https://code.visualstudio.com/Docs/languages

所有这些版本的 Visual Studio 2015 和 Visual Studio Code 都可以用于 ASP.NET Core web 应用开发。 当你创建一个新项目在 Visual Studio 2015 中,您将看到项目模板为空项目,Web API 项目,的 Web 应用项目,如下面屏幕截图所示。 另一方面,Visual Studio Code 是基于文件/文件夹的(而不是基于项目/解决方案的),所以你可以通过打开一个包含支持项目的文件夹来打开一个项目的文件:

Visual Studio Code

除了 Visual Studio 之外,您还可以使用Yeoman来创建一个新的 ASP.NET 项目工作。 Yeoman 是一个脚手架工具,可以帮助你生成各种现代 web 应用,包括 ASP.NET。 欲了解更多关于约曼的信息,请访问其网站http://yeoman.io

本书将主要关注 Visual Studio 2015。 所有的截图和说明都将使用 Visual Studio 2015 Community Edition 和运行在 Windows 10 上的 Edge 网络浏览器。 如果使用不同的编辑器、操作系统或 web 浏览器,则必须在自己的环境中执行相同的步骤。 无论使用什么平台,应用代码都应该保持不变。

运行 ASP.NET Core 在 Windows, Mac OS X 和 Linux

ASP.NET Core,你现在可以在 Mac OS X 和 Linux 上开发和部署你的应用,除了 Windows。 如果您在很久以前使用过活动服务器页面(ASP),您可能会记得Chili! 软 ASP,一个第三方解决方案的 web 服务器插件,它允许 ASP 在 Solaris, Linux 和其他 Unix 平台上运行。 新的。net Core 远不止于此。

通过一个跨平台的运行时,它可以与你的 web 应用一起部署,. net Core 提供了来自微软的官方支持,以享受 ASP 的好处.NET 在您最喜欢的平台上。 使用前面提到的 Visual Studio Code,您还可以轻松地构建 ASP。 这些平台上的核心应用。

虽然这本书将主要涵盖 ASP。 下面的说明将帮助您在 OS X 和 Linux 上设置您的环境。

Windows 上的 NET Core

为 asp.net 设置开发环境.NET Core 在 Windows 上是相当简单的。 如果你已经安装了最新的 Web 开发工具 Visual Studio 2015 Update 3,那么你已经拥有了你所需要的。 新的 ASP.NET Core 模板足以让你入门。

如果您只是需要一台或多台开发机器上的基本代码编辑器,那么您也可以在 Windows 上使用 Visual Studio Code。 请注意,社区版仅适用于非企业客户,因此不能将其与新的专业版或企业版一起安装。

Mac OS X 上的 NET Core

要在 Mac OS X 上设置开发环境,可以从安装最新版本的 Visual Studio Code 开始。 您也可以安装 ASP.NET Core 的命令行。 这里有一个高层次的三步流程让你开始:

  1. 安装打开 SSL。
  2. 安装。net Core SDK。
  3. 安装 Visual Studio Code for OS X。

获取关于设置 ASP. net 的最新详细说明(和命令行说明)。 在 Mac OS X 上,你可以参考微软的官方指南https://www.microsoft.com/net/core#macos

Linux 下的 NET Core

在 Linux 上设置开发环境有点复杂。 如您所料,您可以从在 Linux 机器上安装 Visual Studio Code 开始。

要总结设置运行时的步骤,您必须执行以下操作:

  1. 安装先决条件,根据您的 Linux 版本可能有所不同。
  2. 安装。net Core SDK。
  3. 安装 Visual Studio Code for Linux。

获取关于设置 ASP 的最新详细说明.NET Core 在 Linux 上,你可以参考微软的官方指南,网址如下。 选择一个 Linux 发行版,可以在https://www.microsoft.com/net/core上查看关于这个特定发行版的更多细节,如 Red Hat、Ubuntu、Debian 和其他许多发行版。

总结

在本章中,我们已经对 ASP 进行了介绍.NET Core、。NET Framework 以及各种版本的 Visual Studio。 我们还了解了. net Core 的跨平台特性,并简要介绍了如何在各种操作系统上设置开发环境。

在下一章中,我们将学习如何构建你的第一个 ASP.NET Core web 应用在 Visual Studio 2015 上运行在 Windows 10 上。 我们将浏览项目模板,并剖析一个基本的 web 应用,以便更好地理解它的各个部分及其配置。

二、构建您的第一个 ASP.NET Core 应用

不管你是经验丰富的 ASP.NET 开发人员或对控制器方法有点生疏,最好的方法是在 ASP 上获得最新的速度.NET Core 使用了Hello World应用。 除了新的配置文件之外,还需要了解新的项目类型。

超越它对 IIS 或 IIS Express 的传统依赖,您的新的 ASP.NET web 应用也可以在没有 web 服务器的情况下自托管。 这种新模式是其跨平台抱负的基础。

在本章中,我们将讨论以下原则和概念,以帮助您熟悉 ASP 的基本结构.NET Core 应用:

  • 项目模板
  • 模型、视图和控制器
  • 网络配置

Visual Studio 2015 中的项目模板

在 Visual Studio 2015 中,让自己接触到新项目模板的最快方法就是开始使用它们。 ASP.NET Core,目前有三个项目模板可以使用。

启动 Visual Studio 2015,然后点击File|New|Project。 在Visual c#|Web模板中,选择一个 ASP 模板.NET Core Web 应用继续。 在 Core 1.0 版本中,有两个不同的版本:一个使用全新的。net Core 运行时,另一个使用完整的。net 框架,如下图所示:

Project templates in Visual Studio 2015

在预发布版本中,有其他项目类型可以作为Web模板使用。 这些现在都列在。net Core 下面,如下所示:

  • ASP。 净的 Web 应用
  • 类库(包)
  • 控制台应用(包)

项目模板的 ASP.NET Core 1.0 创建了一个控制台应用,该应用在Program.cs文件中有一个Main方法作为其入口点。 这类似于您可能已经熟悉的其他控制台应用。 当你继续使用任何 ASP.NET Core 选项,迎接你的将是全新的 asp.net.NET Core 模板。 下面的屏幕截图显示了ASP 下可用的项目模板.NET Core 模板:

Project templates in Visual Studio 2015

新模板如下:

  • Web API
  • Web 应用

您将注意到在新模板列表中没有 Web Forms。 正如在第一章中所提到的,ASP 入门。 asp.net Core,新的 ASP. NET Core NET 更喜欢 MVC 而不是 Web 表单,它在跨平台实现中避免使用 MVC。

空模板

使用一些较老版本 Visual Studio 的开发人员的一个常见抱怨是:Empty模板不够空。 结果,这些开发人员将以空的模板开始,只是开始剥离新创建项目的组件。

这在 Visual Studio 2013 中得到了改进,并在 Visual Studio 2015 中继续发挥预期的作用。 选择选项可以确保你拥有一个只够启动的基本项目,如下图所示:

Empty template

在一个空的模板项目中,您可以识别以下项目:

  • 物业
  • 文献
  • wwwroot(web 根目录)
  • 依赖项(初始为空)
  • Program.cs(包含Main入口点方法)
  • 项目。 json(最小设置)
  • (关于 ASP. html 的信息) 净核心)
    *** Startup.cs:
    • ConfigureServices()
    • Configure()
    • Main()* 网络。 配置(仅用于 IIS 启动; 使用基于 json 的文件进行配置)**

**## Web API 模板

ASP.NET Web API 模板是由 asp.net 发布的.NET MVC 4 在 2012 年。 它让 ASP.NET 开发人员可以轻松构建供 web 或移动客户端使用的 HTTP 服务。 ASP.NET Core MVC,开发人员可以在 MVC 项目中构建 Web API 时享受更精简的体验。

Web API 模板是为您的 Web 项目创建 RESTful HTTP 服务的一个很好的起点。 选择 Web API 项目模板将比空模板提供更多信息。 一个 Web API 项目的结构如下图所示:

Web API template

在一个 Web API 模板项目中,你可以识别以下项目:

  • (Project) Properties
    • 启动设置。 json
  • 文献
  • wwwroot(web 根目录)
  • 依赖项(初始为空)
  • Controllers folder
    • ValuesController.cs
  • app 设置 json(最小设置)
  • Program.cs(包含Main入口点方法)
  • 项目。 json(基本设置和引用)
  • (关于 ASP. html 的信息) 净核心)
    *** Startup.cs
    • 构造器
    • ConfigureServices()
    • Configure()**

**除了空模板之外,Web API 模板还有以下附加项:

  • ValuesController.cs
  • app 设置 json

Web 应用模板

与其他模板相比,Web Application 模板提供了更多的初始代码和内容。 这是学习 ASP 的来龙去路的一个很好的起点。 净的核心。 在使用此 web 模板时,单击Change Authentication按钮以包括Individual User Accounts作为您的初学者应用的身份验证类型。 Web Application 项目的结构如下图所示:

Web Application template

在 Web Site 模板项目中,您可以标识以下项目。 首先,在你的项目文件夹上方有一些顶级项目:

  • (Project) Properties
    • 启动设置。 json
  • 文献
  • wwwroot
    • css
    • 图像
    • js
    • lib
    • _references.js
    • 图标图标
  • Dependencies
    • Bower(适用于BootstrapjQuery等)

接下来,你的代码、MVC 组件和 DB 迁移有几个文件夹,如下所示:

  • Controllers folder
    • 每个控制器的类文件
  • Data
    • 用于初始迁移的 Migrations 子文件夹
    • DB 上下文快照
  • Models
    • ModelsViewModels
  • Services
    • 服务类,例如用于消息传递和电子邮件
  • Views
    • 视图组织在每个控制器的子文件夹中
    • 共同的观点
    • _ViewImports.cshtml(视图共享导入)
    • _ViewStart.cshtml(共享视图头)

最后,有几个 JSON 配置文件和几个.cs代码文件,如appsettings.jsonbundleconfig.jsProgram.csproject.jsonProject_Readme.htmlStartup.cs:

  • appsettings.json(app 设置,如连接字符串)
  • bundleconfig.json(捆绑和小型化配置)
  • Program.cs(入点用Main法)
  • project.json(项目设置,服务器端引用)
  • Project_Readme.html(关于 ASP 的信息。 净核心)
  • Startup.cs
    • 构造器
    • ConfigureServices()
    • Configure()
  • web.config(仅用于 IIS 启动; 使用基于 json 的文件进行配置)

您可能必须单击解决方案资源管理器中的显示所有文件图标,以查看最初可能隐藏的其他文件。

你好,ASP.NET -你的第一个 ASP。 网络应用

大多数编程书籍和教程都会帮助您开始使用 Hello World 应用。 这指的是一个简单的应用,它具有应用的所有基本元素,使其能够在您的环境中运行。 如果应用可以在屏幕上显示 Hello World,那么就成功地设置了它。

创建一个新的 ASP.NET web 应用项目,并选择本章前面描述的 Empty 模板。 这应该为您的 Hello World 应用提供一个基本模板。

写一篇文章

默认情况下,空模板包含一个带有Configure()方法的Startup.cs类。 该方法应该有一行代码将Hello World写入 HTTP 响应输出流,以便在浏览器中显示。 下面的代码是针对Configure()方法的:

public void Configure(IApplicationBuilder app, 
    IHostingEnvironment env, ILoggerFactory loggerFactory) 
{ 
    loggerFactory.AddConsole(); 
    if (env.IsDevelopment()) 
    { 
        app.UseDeveloperExceptionPage(); 
    } 
    app.Run(async (context) => 
    { 
        await context.Response.WriteAsync("Hello World!"); 
    }); 
} 

通过启用静态文件、Identity、MVC 等,可以使用Configure()方法来配置 HTTP 请求管道。 它可以用于在Startup.cs类中设置默认路由,取代需要global.asax文件的情况。

注意,Configure()方法是在ConfigureServices()方法之后调用的,它可以用来添加各种服务,如实体框架、身份和 MVC。 这两个方法都由运行时调用。

要更改正在显示的文本,只需将Hello World!替换为Hello, ASP.NET Core!。 此文本更改在功能上与 Empty 模板提供的内容没有任何不同,但它确实向您公开了Startup类及其方法。

启动应用

要启动应用,请按键盘上的F5,或在Debug菜单中单击开始调试。 这应该会在默认的 web 浏览器中启动应用,如下图所示:

Launching the application

您可能还熟悉 Visual Studio 工具栏上的绿色Play按钮,它用于启动您的应用。 在 Visual Studio 2015 中,这个按钮提供了一些新的选择,如下图所示:

Launching the application

选择如下:

  • IIS Express:在默认浏览器中启动的默认选项
  • 内部 Web 主机:以项目名称命名
  • web、gen、ef、其他:各种命令,通过project.json文件进行配置
  • 使用浏览:更改默认浏览器或添加新浏览器

您可能已经注意到,在调试时可以选择不同的 web 浏览器。 只需浏览浏览器列表,选择不同的浏览器进行调试,如下图所示:

Launching the application

还可以在解决方案资源管理器中右键单击项目以访问其属性。 在项目属性面板中,单击Debug选项卡,进一步定制每个调试配置文件,如下图所示:

Launching the application

Web 文件和文件夹

现在让我们深入研究 Web Application 模板项目中的所有这些文件和文件夹,以了解它们的用途。 首先,我们将关注配置文件,从bower.json开始。

该 JSON 配置文件用于管理Bower依赖项。 它以一个依赖项列表开始,然后是每个依赖项的可选详细配置,如下面的截图所示:

Web files and folders

你会注意到这和 Solution Explorer 面板中显示的 Bower 依赖列表是一样的,如下图所示:

Web files and folders

接下来,让我们看一下appsettings.json,它是您的应用配置文件。 ASP.NET Core 中,这个新的 JSON 文件(在测试版中暂时称为config.json)取代了将设置存储在基于 xml 的web.config文件中的需要。 您可以在Startup.cs代码中指定配置文件。

你的设置文件可能看起来像这样:

{ 
  "ConnectionStrings": { 
    "DefaultConnection": "<YourDbConnString>" 
  }, 
  "Logging": { 
    "IncludeScopes": false, 
    "LogLevel": { 
      "Default": "Debug", 
      "System": "Information", 
      "Microsoft": "Information" 
    } 
  } 
} 

最后但并非最不重要的是,我们有project.json,这是一个 JSON 文件,包含特定于项目的信息和服务器端依赖关系。 更多信息请参见本章的捆绑和发布部分。

模型,视图和控制器-一个 MVC 刷新器

对于那些不熟悉 MVC 的人,这里有一个快速复习,也包括了新的 ASP 信息。 净的核心。 下图显示了一个简单的架构图,它代表了 MVC 软件架构模式:

Models, views, and controllers - an MVC refresher

即使图中显示了每一项中的一个,图中也可以有多个模型、视图和控制器。 每个请求通过一个特定的控制器路由,以确定将在用户的 web 浏览器中显示的结果。

当微软第一次发布 ASP 时,MVC 本身并不是一个新的概念。 净 MVC。 但它立即为。net 开发人员引入了一种开发 web 应用的新方法。 对于早期采用者来说,Web Forms 的好处变得很明显:更清晰的关注点分离,更好的可测试性,以及可以定制的轻量级客户机输出。

一些开发人员由于各种原因没有离开 Web Forms:新的学习曲线、个人偏好、公司需求和许多其他原因。 展望未来,ASP。 由于跨平台。NET Core 不支持 Web 表单,因此 NET MVC 变得更加必要。 此外,ASP.NET Core 不像它的前辈那样依赖于System.Web

所以,如果你还没有看过 MVC,现在是一个很好的时机。

控制器

在某种程度上,控制器是 ASP 的心脏。 净 MVC 应用。 每个控制器根据匹配的路由负责处理用户请求。 控制器可以更新模型中的数据,然后选择一个视图返回给用户。

每个控制器都是基类Controller的子类,它位于Microsoft.AspNetCore.Mvc命名空间中。 这与以前版本的 ASP 的Controller类不同.NET,它位于System.Web.Mvc命名空间中。 事实上,Web API 控制器也使用了新的Controller基类。

在 MVC 中,每个控制器通常从其操作方法中返回一个IActionResult。 对于 web 应用项目,这可以是一个视图。 对于 Web API 项目,这可以是一组数据。 从控制器返回视图和结果是可能的。

型号

与前面一样,您的模型将包含项目的数据域。 每个模型类都可以在代码中表示实体,同时用方括号包围的属性进行修饰。 这种声明式语法还允许您在模型中添加验证规则。

模型的数据会受到控制器的影响。 通过将模型绑定到视图,每个视图将自动决定显示什么以及如何显示它。

为了更清晰的架构,您可以使用特定于视图的模型(或ViewModel)来绑定到视图。 对于通过实体框架反映数据库实体的模型,可以使用带有服务层的存储库模式。 这将让您拥有不必依赖数据库实体结构的 UI 元素。

看到第六章,使用实体框架与数据库进行交互的代码,实体框架的更多信息,一个对象关系映射(ORM****). net 应用的框架。

视图

应用中的视图可能是 MVC 中最简单的部分。 每个视图代表 UI 层,产生将显示给最终用户的客户端 HTML、CSS 和 JavaScript。

视图存储在.cshtml文件中,可以在同一个文件中包含服务器端代码和客户端代码。 一个名为ViewBag的内置对象允许你存储自己的属性并在视图中显示它们。 对象及其属性可以通过控制器代码进行操作。

不必在服务器端代码和客户端代码之间来回切换,现在可以在视图中使用标记助手来实现更流畅的语法。 标签助手允许你在 HTML 标签中使用自定义属性,这可能是那些使用AngularJS的开发者所熟悉的。

参见第 3 章理解 MVC,了解更多关于标签助手和视图的信息。

使用 project.json 进行 Web 配置

project.json文件中,您可以找到以下信息:

  • userSecretsId:类似于 guide 的值,用于处理用户秘密
  • 依赖项:服务器端依赖项列表
  • 版本:项目版本
  • 框架:.NET Core 框架名称和版本
  • 构建/运行时/发布选项:构建/运行时配置和发布 web 应用时包含的文件/文件夹列表
  • 脚本:可以由特定事件(如prepublishpostpublish和其他事件)触发的操作的脚本列表

下面的project.json文件截图显示了一个典型的项目配置文件的内容,折叠起来适合所有顶级项目:

Web configuration with project.json

依赖和框架

在配置文件的依赖项部分,您将发现所有服务器端依赖项的列表,例如 ASP.NET MVC,实体框架等等。 它们在解决方案资源管理器中也可以作为项目引用可见。

Dependencies and frameworks

前面的屏幕截图显示了依赖项部分的扩展,以显示引用及其版本号。 下面的截图显示了解决方案资源管理器中相同的一组引用:

Dependencies and frameworks

命令和工具

预发布版本的 ASP.NET Core 包含了可以在project.json配置文件中配置并在 Visual Studio IDE 中触发的命令。 下面的屏幕截图显示了配置文件的命令部分:

Commands and tools

ASP 不再支持commands部分。 1.0 NET Core。 相反,在project.json文件中有一个新的tools部分,它可以用来指定包含项目工具的包。

捆绑发布

最后,我们还将在部署应用时包含用于发布的特定组件。 下面的屏幕截图显示了publishOptions部分,展开显示了要包含的组件:

Bundling and publishing

总结

在本章中,我们已经了解了 ASP 的基本结构.NET Core 1.0 web 应用。 我们深入研究了组成应用的各个部分,包括全新的配置文件。 我们还介绍了 MVC 及其组件的基础知识。

在下一章中,我们将深入探讨 ASP.NET Core MVC。 我们将从头构建一个示例应用,以演示 ASP 中模型、视图和控制器的特性.NET Core MVC web 应用。****

三、理解 MVC

在前一章中,我们快速回顾了 ASP.NET 中的 MVC 及其组件。 在本章中,我们将深入探讨 ASP 中的 MVC.NET Core,它是 beta 版的 MVC 6。

开发人员常见的两个问题是:如何将旧的 MVC 项目升级到 ASP.NET Core MVC ? 是否存在自动迁移过程? 从 MVC 5 迁移到 asp.net 中新的 MVC.NET Core 涉及一些手动步骤,因为没有自动迁移过程。 您可以将所有静态客户端文件复制到wwwroot位置,并调整对这些文件的任何引用以指向正确的位置。

对于服务器端代码,您可以在您的模型、视图和控制器上进行迁移,而无需进行很多更改。 本章将涵盖以下各方面的知识:

  • 控制器
  • 的观点
  • 模型

对于已经使用过以前版本的 ASP 的开发人员来说,本章的部分内容会比较熟悉。 净 MVC。 我们将浏览一些熟悉的材料,同时介绍对 MVC 的更新添加。

建筑控制器

您的 MVC 控制器是奇迹发生的地方。 请求来自最终用户,然后返回内容和数据。 在这两者之间会发生什么取决于你——开发者。

控制器方法

默认情况下,控制器方法可以使用[HttpGet]属性用于 HTTPGET请求,或者完全忽略这个动作谓词属性,因为它是默认行为。 很可能,您已经使用了以下 HTTPGETPOST动词:

  • HttpGet:使用 HTTPGET方法,带有可选的querystring参数
  • HttpPut:使用 HTTPPOST方法提交表单,创建实体

除了上述内容,您还应该注意可以用作控制器属性的其他 HTTP 谓词; 它们如下:

  • HttpPut:使用 HTTPPUT方法编辑现有实体
  • HttpDelete:使用 HTTPDELETE方法删除已有实体
  • HttpPatch:允许部分模型更新,而不是完整的PUT请求
  • AcceptVerbs:允许指定多个动作动词

基本控制器

使用 Visual Studio,您可以添加一个基本控制器,并逐步向其添加代码。 您还可以使用 scaffolding 添加一个更完整的控制器,其中包括模型绑定和操作方法来操作模型。 但我们会在本章的后面讲到。

首先,让我们创建一个示例项目,我们将在其中添加一个基本控制器,如下所示:

  1. 创建一个新的 ASP。 通过点击文件|新建|项目
  2. 选择ASP.NET Web 应用(.NET Core)
  3. 命名您的项目患者记录
  4. ASP. net 列表下选择Web 应用.NET Core 模板
  5. 点击更改认证切换为个人用户账号
  6. 点击OK创建您的 web 项目。

我们也可以从一个空的模板开始,但是这样我们就必须添加更多的依赖项和配置才能启动和运行。 让我们继续添加基本控制器。

**要添加基本控制器,请执行以下步骤:

  1. 在“解决方案资源管理器”中右键单击“Controllers”文件夹。
  2. 在上下文菜单中,单击添加|新项目
  3. 在。net Core 和 ASP.NET 下的Add New Item对话框中,选择MVC Controller Class
  4. 将控制器命名为PatientController,单击添加继续操作。

以下截图显示了添加新项目窗口:

Basic controllers

此时,您应该拥有一个带有一个Index()方法的基本控制器,该方法返回一个简单的View。 这对于有经验的 ASP 应该已经很熟悉了。 净 MVC 开发人员。 如果我们现在运行项目,web 应用应该在 web 浏览器中启动,使用任意端口号,比如12345。 您的端口号会有所不同。 您的 web 应用的链接类似于http://localhost:12345

在浏览器仍然打开的情况下,您可以尝试通过将控制器名称Patient添加到 URL:http://localhost:12345/Patient的末尾来访问PatientController

这将导致未处理的InvalidOperationException,因为我们还没有为Patient控制器添加Index视图。 与以前版本的 MVC 一样,该视图通常位于Views文件夹中名为Patient的子文件夹中。 如果没有,回退视图将位于Shared子文件夹和Views文件夹中。 当这两个常规位置都不包含预期的视图时,就会发生异常。

为了绕过预期的路径,让我们用以下代码替换PatientController类的Index方法:

public string Index() 
{ 
    return "Patient Info"; 
} 

注意,我们已经将返回类型改为string,这样我们就可以返回一个字符串字面值来测试控制器。 如果你再次运行应用,当你在 http://localhost:12345/Patient访问控制器Patient时,你现在应该能够在浏览器中看到占位符文本:

Basic controllers

URL 路由和约定

如果你看一下Startup.cs中的Configure()方法,你可以看到你的 web 应用的默认路由:

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

你可能还记得第二章构建你的第一个 ASP。 asp.NET Core 应用,即在 asp.net 中采用Configure()方法.NET Core 用于应用在ConfigureServices()方法中添加的组件和服务。 从示例代码中可以看到,控制器和操作值分别默认为HomeIndex

可以选择将id参数添加到 URL 的末尾斜杠之后。 注意,这与向QueryString添加id参数不同,但两种方法都可以通过方法参数将值传递给控制器方法。

为了使用Encode()方法,请确保您已经添加了以下using语句以在代码中包含WebEncoders命名空间:

using System.Text.Encodings.Web; 

为了使用默认约定,将PatientController类中的Index方法更新如下以接受参数:

public string Index(int id, string name = "Unknown") 
{ 
    return "Patient Info: " + HtmlEncoder.Default.Encode( 
        "ID: " + id + ", " + 
        "Name: " + name); 
} 

参数idname分别表示患者的 ID 和姓名。 如果没有指定名称,则name参数默认为"Unknown"。 传递的值被封装在对Encode()的调用中,以确保没有恶意标记或脚本通过QueryString传递。

您现在应该能够使用http://localhost:12345/Patient?id=1&name=John作为QueryString参数。 下面的截图是相同的输出:

URL routes and conventions

实施视图

MVC 中的视图构成了表示层,这是一个用户界面,它保存着供用户交互的屏幕元素。 任何 web 应用的 UI 都可能很快变得非常繁忙,要么同时充满了太多的条目,要么承载了业务逻辑和不必要的代码。 MVC 的关注点分离的目标是帮助开发人员创建架构良好的应用,其中视图是轻量级的,验证在模型层中执行,模型层通过绑定与视图连接。

基本观点

与添加基本控制器的方式类似,我们将创建一个基本视图并逐步向其添加代码。 正如您可以使用 scaffolding 创建一个更完整的控制器一样,您也可以创建一个附加到模板和模型的更完整的视图。 但是同样的,我们会在本章的后面讲到。

首先,让我们为Patient控制器使用的视图创建一个子文件夹,这样我们就可以将我们的新视图添加到其中。 遵循以下步骤:

  1. 在“解决方案资源管理器”中,右键单击Views文件夹,并向其中添加一个新文件夹。
  2. 将子文件夹命名为Patient,以利用命名约定。
  3. 右键单击Patient子文件夹,然后单击添加|新建项目
  4. 添加新项目对话框中,选择ASP 下的MVC 视图页面
  5. 将视图命名为Index.cshtml,然后单击添加继续。

我们使用默认视图名Index.cshtml,这样这个视图将由Patient控制器中的Index()控制器方法返回。 此时,视图没有实际的内容,只有一些占位符符号用于注释和服务器端代码。 代码如下:

@* server-side comments *@ 
@{ // server-side code } 

用以下内容替换视图的内容:

@{ 
    ViewData["Title"] = "Patient Index"; 
} 
<h2>Patient Index</h2> 

"Title"ViewData值由默认的_Layout.cshtml布局文件使用,该文件可以在Views文件夹中的Shared子文件夹中找到。 以下代码适用于Patient控制器的Index视图:

<title>@ViewData["Title"] - PatientRecordsWebApp</title> 

用以下代码替换控制器的Patient视图:

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

要浏览此页面,请运行应用并将Patient控制器名称添加到 URL 末尾的斜杠后面,如http://localhost:12345/Patient

您现在应该在浏览器中看到视图的内容,包括标题和页眉文本。 如下截图所示:

Basic views

视图中的标签助手

为了更容易地浏览到这个页面,让我们更新它的布局文件,以包含一个到controller方法的链接,该方法将返回这个视图。 首先,在布局文件中找到一组可点击链接,<li>列出<ul>无序列表中的项目。

请注意,<a>标记每个都有两个属性作为标记助手:

  • asp-controller
  • asp-action

标签助手是 ASP 中新引入的.NET Core MVC,并且类似于您过去可能使用过的HtmlHelpers。 这两个属性的值将决定控制器名称以及单击链接时将触发的后续操作方法。

让我们为Patient控制器添加一个附加的可点击链接,就在Contact链接之后,使用以下代码:

<li><a asp-controller="Patient" asp-action="Index">Patients</a></li> 

运行 web 应用来查看顶部标题区域的新链接,这将允许您更快地跳转到患者的索引视图:

Tag helpers in views

在本章的后面,我们将学习如何在视图中使用标记助手来进行验证。 我们还将向模型中添加特定的属性,以帮助在提交表单时验证特定的字段。

ViewData、ViewBag 和 TempData

通过在控制器中使用ViewData项设置值,然后在视图中检索值,可以将数据从控制器传递到视图。 这些值被存储为具有基于字符串的键的键值字典项。 与使用ViewData相比,ViewBag是一种更动态的选择,允许您在不进行类型转换的情况下使用复杂数据类型。 赋值将在重定向时重置。

下面是如何在控制器中使用ViewDataViewBag的两个例子:

ViewData["PatientId"] = id; 
ViewBag.PatientData = "someData"; 

TempData略有不同,因为它在重定向期间保留其数据,这允许您在重定向操作期间在控制器之间传递数据。 它的语法类似于ViewData,所以它可以作为一个字典项使用,具有以下基于字符串的键:

TempData["UserToken"] = userTokenData; 

让我们在Patient控制器中添加以下Details()方法,为ViewData对象赋值:

public IActionResult Details(int id, string name = "Unknown") 
{ 
    ViewData["PatientId"] = id; 
    ViewData["PatientName"] = name;  
    return View(); 
} 

这里,我们分别为患者的 ID 和名称设置了两个ViewData值。 为了在视图中显示数据,我们将添加一个新的Details视图,如下所示:

  1. 在“解决方案资源管理器”中展开Views文件夹。
  2. 右键单击Patient子文件夹,然后单击添加|新建项目
  3. 添加新项目对话框中,选择ASP 下的MVC 视图页面
  4. 命名您的视图Details.cshtml以便继续。

用以下代码替换Details视图的内容:

@{ 
    ViewData["Title"] = "Patient Details"; 
} 
<h2>Patient Details</h2> 
<ul> 
    <li>ID: @ViewData["PatientId"]</li> 
    <li>Name: @ViewData["PatientName"]</li> 
</ul> 

为了提供Index视图和Details视图之间的简单链接,我们还将更新位于Views文件夹中Patient子文件夹中的Index视图。

用以下代码替换控制器Patient``Index视图的内容:

@{ 
    ViewData["Title"] = "Patient Index"; 
} 
<h2>Patient Index, with Tag Helpers</h2> 

<ul> 
    @for (int i = 0; i < 10; i++) 
    { 
        <li><a asp-controller="Patient" asp-action="Details"  
               asp-route-id="@i" asp-route-name="Patient @i"> 
               Patient # @i</a></li> 
    } 

</ul> 

在前面的代码中,for循环用于生成可单击链接的列表。 点击每个链接可以显示特定患者的详细信息。 同样,您可以在<a>标记中看到以下标记帮助器属性的使用:

  • asp-controller
  • asp-action
  • asp-route-id
  • asp-route-name

最后两个属性足够开放,可以引用 ID 和 name 等命名参数。 id属性将自动遵循 URL 路由约定,而name属性将作为 QueryString 参数添加。

连结将以下列格式显示:

  • http://localhost:12345/Patient/Details/0?name=Patient%200
  • http://localhost:12345/Patient/Details/1?name=Patient%201
  • http://localhost:12345/Patient/Details/2?name=Patient%202

问号前面的数字值是由为应用定义的 URL 路由定义的 ID 值。 注意,参数值中的空格是 url 编码为%20的。

运行 web 应用,单击顶部工具栏中的Patients链接,导航到Patient索引页。 在下面的截图中,你会看到一个包含 10 个项目的列表,每个项目都有自己的链接。 单击其中的一个链接进入该特定患者的Details页面。 详情页面将显示选定患者的 ID 和姓名:

ViewData, ViewBag, and TempData

设计模型和 ViewModels

您可以使用模型来存储一组数据,而不是将单个值从控制器逐个传递到视图。 控制器负责更新模型,模型可以与视图关联以获取其数据。

创建模型

模型只是一个文件扩展名为.cs的类文件。 在解决方案资源管理器面板中,您可以将新的模型类文件添加到Models文件夹中。 您可能已经注意到,对于 MVCController类和 MVCView页有一些选择,但是对于Model类没有选择。

要创建一个模型类,我们可以在添加新项目时选择class选项,如下所示:

  1. 在“解决方案资源管理器”中,右键单击Models文件夹,然后单击添加|新项目
  2. Add New Item对话框中,选择Code下的Class
  3. 将文件命名为Human.cs以创建一个新模型。
  4. 验证您有一个名为Human的空类。

Human类中添加以下字段:

public class Human 
{ 
    public int ID { get; set; } 
    public string SocialSecurityNumber { get; set; } 
    public DateTime DateOfBirth { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
} 

您可能还注意到在下面的截图中,在Models文件夹中有ViewModels子文件夹。 当您将视图绑定到模型时,模型中定义的字段表示关联视图中的 UI 元素和表单字段。 这些子文件夹是组织这些模型的好地方,它们的名称与你的控制器名称相匹配:

Creating models

绑定模型到视图

要将视图绑定到模型,可以在视图的.cshtml文件中看到以下语法:

@model MyModelName 

视图的其余部分应该包含一个服务器端表单,以及与模型字段关联的表单元素。 虽然在一个视图中可能有多个表单,但如果可以避免多个表单,MVC 视图通常倾向于使用一个表单。 在任何情况下,用于视图的模型都应该包含所有必要的字段,以覆盖视图中的所有表单。

在以前的版本中.NET MVC,你可能已经为服务器端表单使用了以下语法:

@using (Html.BeginForm("action", "controller", FormMethod.Post, new {})) 
{ 
    @Html.LabelFor(m => m.Field1) 
    @Html.TextBoxFor(m => m.Field1) 

    @Html.LabelFor(m => m.Field2) 
    @Html.TextBoxFor(m => m.Field2) 

    <input type='Submit' value='Submit' /> 
} 

在 ASP.NET Core MVC,下面介绍了使用标记助手的语法:

<form asp-controller="controller" asp-action="action" method="post"> 
    <label asp-for="Field1"></label> 
    <input asp-for="Field1" /> 
    <label asp-for="Field2"></label> 
    <input asp-for="Field2" /> 
    <input type="submit" value="Submit" /> 
</form> 

这种新方法也使代码更干净、更高效。 当表单被提交时,控制器方法可以接受模型的数据类型作为参数来接收它的所有字段:

[HttpPost] 
public IActionResult MyAction(MyModelName model) 
{ 
    return View(model); 
} 

ViewModels 和映射

当您在它们自己的文件夹中组织您的ViewModels时,您通常会将每个视图绑定到一个特定的ViewModel。 但是,数据库设计可能需要在代码中定义实体作为与数据库对象匹配的模型文件。 您需要弄清楚如何将特定于数据库的模型映射到您的ViewModels

有几种不同的方法可以映射你的ViewModels和数据库实体模型:

  • 当您的控制器方法接受一个ViewModel时,您可以逐个将每个字段映射到它们对应的实体模型字段
  • 您可以使用像AutoMapper这样的库,它可以在 GitHub/NuGet 上免费获得,并在http://automapper.org中描述为基于约定的基于对象的映射器

为了更深入地了解实体模型,我们将在第 6 章中介绍对象关系映射和实体框架,在代码中使用实体框架与数据库交互。

把它结合在一起

为了把它们结合在一起,让我们利用 Visual Studio 内置的代码生成特性来利用脚手架和绑定。 在本节中,我们将讨论帮助模型验证的字段属性。

在结束本章之前,我们还将学习如何使用异常处理来捕获代码中的错误。

脚手架、验证和模型绑定

使用到目前为止构建的PatientRecords项目,让我们按照以下步骤向其添加一个新控制器,以利用 scaffolding。 而不是点击添加项目,我们将选择控制器选项:

  1. 在解决方案资源管理器中,右键单击Controllers文件夹。
  2. 在上下文菜单中,单击添加|控制器
  3. 选择使用 EF 添加带有视图的新 MVC 控制器的选项。

在添加控制器时,系统将提示您输入其他信息。 选择/输入以下信息:

  • 模型类:Human,来自您的模型名称空间
  • 数据上下文类:ApplicationDbContext
  • 控制器名称:HumanController

对于以下所有的复选框,你可以保持默认值不变:

  • 生成视图:默认选中,用于为每个动作生成相应的视图。
  • 引用脚本库:默认选中,包含对客户端代码中的脚本库的引用。
  • 使用布局页面:默认选中,允许输入布局名称,或者可以为空以允许_viewstart.cshtml被使用。 这个页面通常包含一个对项目默认布局页面的引用,默认设置为_Layout

您可能已经注意到,控制器名称默认为HumenController,其中有一个e。 这是因为脚手架工具试图将代码中的实体多元化,所以它错误地假设Human的复数形式是Humen。 要修复此问题,请确保在添加控制器之前用a将其重命名为HumanController

在添加了搭建的控制器之后,您可以检查HumanController的内容,以验证它是否为您自动生成了一些代码。 它应该从一个数据库上下文对象开始,然后是一个利用该上下文的构造函数。

类的其余部分应该有一系列用于基本 CRUD 操作的操作方法,即用于创建、读取、更新和删除条目的操作:

  • 索引:Human对象列表
  • Details(id):具体Human对象的详细信息
  • Create:GET/POST 方法显示创建表单并创建新实体
  • Edit:GET/POST 方法来显示编辑表单并编辑现有实体
  • Delete:GET/POST 方法,显示确认界面,执行删除操作

查看Views文件夹,并验证是否有一个Human子文件夹,其中包含前面所有操作方法的单独视图:创建、删除、详细信息、编辑和索引。 如果您打开这些.cshtml文件中的任何一个,您将在每个视图的第一行中找到对每个对应模型的引用。

这些视图中包含的验证使用新的标记助手语法。 这些标记可以用于验证,我们在前一节中看到了这一点。 在此基础上,我们可以编辑Human模型,以包含一些属性来帮助验证。

你需要的属性定义在DataAnnotations命名空间中,所以你应该确保以下 using 语句被添加到你的Human.cs文件中:

using System.ComponentModel.DataAnnotations; 

编辑Models目录下的Human.cs文件,以包含以下字段的属性:

public class Human 
{ 
    public int ID { get; set; } 
    [Required] 
    [StringLength(11)] 
    [Display(Name = "SSN")] 
    public string SocialSecurityNumber { get; set; } 
    [Display(Name = "DOB")] 
    [DataType(DataType.Date)] 
    public DateTime DateOfBirth { get; set; } 
    [Display(Name = "First Name")] 
    public string FirstName { get; set; } 
    [Display(Name = "Last Name")] 
    public string LastName { get; set; } 
} 

这里使用了以下属性:

  • Required:必填项
  • StringLength(n):限制字段值为 n 个字符
  • Display(Name ="Alt Name"):在用户界面中显示的备用名称
  • :限制字段为特定的数据类型,例如 date

注意事项

如果您想试验其他属性,请参考 ASP.NET 文档来查找此名称空间中的其他验证属性。

在再次启动 web 应用之前,让我们添加一个到Human控制器Index视图的新链接。 我们可以在Views/Shared目录下_Layout.cshtml的顶部菜单中添加另一个可点击的链接:

<li><a asp-controller="Human" asp-action="Index">Humans</a></li> 

前面的行可以添加到前面添加的Patients链接之后。 如果此时单击顶部工具栏中的Humans链接,您将得到一个错误,因为我们的数据库还不存在。

数据库设置和数据录入

要设置初始数据库,我们必须从命令提示符运行初始迁移。 当前文件夹必须设置为项目的根位置,即Startup.cs所在的位置。 这与您的ModelsViewsControllers文件夹所在的位置相同。

打开文件夹所在位置的命令提示符后,可以运行以下命令。 您的 DNVM 版本可能会有所不同:

dotnet restore
dotnet build
dotnet ef migrations add Initial
dotnet ef database update

以上命令的作用如下:

  • restore:为你的项目恢复包
  • :编译并构建您的项目
  • :创建数据库模型的初始快照
  • 创建/更新您的数据库

Visual Studio 应该为你安装dotnet工具,所以你可以参考第一章ASP 入门.NET Core,用于初始设置说明,如果您遗漏了任何内容。 关于实体框架的更深入的介绍,请参见第 6 章在代码中使用实体框架与数据库交互

命令运行完成后,您应该拥有数据库的初始版本,以及与实体模型相匹配的必要数据库表,例如Human表。 Human表中的列应该匹配Human.cs模型文件中的字段。

在 Visual Studio 中查看 SQL Server 对象资源管理器,在顶部菜单中选择view|SQL Server 对象资源管理器,如下图所示:

Database setup and data entry

现在,您可以再次运行应用,并单击顶部工具栏上的Humans链接。 这应该会把你带到Index视图,显示你还没有Humans视图,如下截图所示:

Database setup and data entry

单击Create New链接创建一个新的Human表项。 单击此链接将带您进入Human控制器的Create视图。 输入一些值,然后点击Create按钮,如下截图所示:

Database setup and data entry

一旦创建了新的Human条目,您的提交将由后版本的Create方法处理。 如果您遗漏了任何必需的值或输入了错误的字符数,验证消息将自动显示在 UI 中。 正确提交后,你会被带回到Index视图,在那里你会看到你刚刚创建的新Human条目,如下截图所示:

Database setup and data entry

异常处理

在 ASP.NET Core MVC 中,异常处理已经得到了发展,并且出现了处理异常的新方法。 UseDeveloperExceptionPage()UseExceptionHandler()的使用可以在 ASP 中看到。 净 web 应用。 要查找这些方法调用的位置,只需查看Startup.cs文件即可。 在Configure()方法内部,你可以找到对以下方法的调用:

app.UseDeveloperExceptionPage(); 
app.UseExceptionHandler("/Home/Error"); 

当您在开发中运行应用时,第一个方法调用是相关的,因此您可以获得额外的调试信息。 在生产环境中,实际的异常处理程序将用于重定向到适当的错误页面。 上述代码触发控制器的HomeError动作方法,然后触发控制器的Error视图。 在我们的样例应用中,在Shared文件夹中有一个Error.cshtml文件,在生产环境中出现异常时将使用该文件。

为了模拟开发和生产环境,可以将环境变量ASPNETCORE_ENVIRONMENT的值更改为DevelopmentProduction。 此更改可以在您的launchSettings.json文件中更改,也可以在项目属性屏幕的Debug标签中更改。

与以前的版本一样.NET,您应该使用 try/catch 块来预测在运行时可能发生的各种异常,并适当地处理它们。 在某些情况下,您可能不得不在应用代码中抛出异常,以便让调用方法处理异常。

总结

在本章中,我们已经了解了模型、视图和控制器如何结合在一起形成一个可工作的 ASP.NET Core MVC web 应用。 我们接触了数据库设置,并以异常处理结束。

在下一章中,我们将学习 Web api,以及如何使用 ASP.NET 超越传统的 Web 应用。 我们将看看可以利用 Web API 后端的各种客户机。**

四、使用 Web api 扩展您的应用

如果你用过 ASP。 asp.net Web API 的早期版本.NET,您可能还记得 Web API 控制器通常继承自APIController。 这在 ASP 中已经改变.NET Core,作为 MVC 和 Web API 统一的一部分。 因此,每个 Web API 控制器都继承自Controller类,它也是每个 MVC 控制器的基类。

在本章中,我们将完成一个 Web API 项目的工作示例。 在本章中,我们将涵盖以下内容:

  • Web api
  • Web API 配置
  • Web API 的路线
  • 使用 Web API 应用

了解 Web API

构建 web 应用可能是一种有益的体验。 接触到广泛的潜在用户所带来的满足感可以胜过花在微调应用和修复 bug 上的令人沮丧的夜晚。 但有些手机用户要求只有原生手机应用才能提供更流畅的体验。

移动浏览器在低带宽的情况下可能会遇到性能问题,在这种情况下,HTML5 应用只能在服务器端后台很重的情况下发挥作用。 Web API 及其 RESTful 端点是用对移动友好的服务器端代码构建的。

Web api 的例子

为了创建一个软件,多年的智慧告诉我们,我们在构建软件时应该考虑用户。 如果没有用例,它的特性实际上毫无用处。 通过围绕用户故事设计特性,揭示与用户操作直接相关的公共端点是有意义的。 因此,您最终将得到一个为更多用户工作的更精简的 web 应用。

如果你需要更多的说服力,这里是一个 Web API 的特性和好处的概述:

  • 它允许您构建现代的轻量级 web 服务,只要您不需要 SOAP,这对您的应用来说是一个很好的选择
  • 使用它比以往任何使用 ASP 完成的工作都要容易.NETWindows Communication Foundation(WCF)服务
  • 它支持 RESTful 端点
  • 它适用于各种客户端,包括移动端和网络端
  • 与 ASP 统一.NET MVC,可以包含/不包含您的 web 应用

从零开始创建新的 Web API 项目

让我们构建一个名为Patient Records的示例 web 应用。 在这个应用中,我们将从头创建一个 Web API 来允许以下任务:

  • 增加一个新病人
  • 编辑现有患者
  • 删除现有患者
  • 查看特定患者或患者列表

这四个操作构成了我们系统中所谓的 CRUD 操作:创建、读取、更新或删除患者记录。

按照下面的步骤,我们将在 Visual Studio 2015 中创建一个新项目:

  1. 创建一个新的 Web API 项目。
  2. 添加 API 控制器。
  3. 为 CRUD 操作添加方法。

前面的步骤已经扩展为详细的说明,如下截图:

  1. In Visual Studio 2015, click File | New | Project. You can also press Ctrl + Shift + N on your keyboard.

    Creating a new Web API project from scratch

  2. On the left panel, locate the Web node below Visual C#, then select ASP.NET Core Web Application (.NET Core), as shown in the following screenshot:

    Creating a new Web API project from scratch

  3. With this project template selected, type in a name for your project, for example PatientRecordsApi, and choose a location on your computer, as shown in the following screenshot:

    Creating a new Web API project from scratch

  4. 您还可以选择右下角的复选框为您的解决方案文件创建一个目录,并/或将您的新项目添加到源代码控制中。 点击OK继续。

  5. In the dialog that follows, select Empty from the list of the ASP.NET Core Templates, then click OK, as shown in the following screenshot:

    Creating a new Web API project from scratch

  6. 或者,您可以选中Microsoft Azure的复选框,将您的项目托管在云中。 点击确定继续。

构建您的 Web API 项目

在解决方案资源管理器中,您可能会看到正在恢复您的引用。 这发生在每次你创建一个新项目或添加新的引用到你的项目必须通过NuGet恢复,如下截图所示:

Building your Web API project

遵循以下步骤,修复您的引用,并构建您的 Web API 项目:

  1. Right click on your project, and click Add | New Folder to add a new folder, as shown in the following screenshot:

    Building your Web API project

  2. Perform the preceding step three times to create new folders for your Controllers, Models, and Views, as shown in the following screenshot:

    Building your Web API project

  3. Right click on your Controllers folder, then click Add | New Item to create a new API controller for patient records on your system, as shown in the following screenshot:

    Building your Web API project

  4. In the dialog box that appears, choose Web API Controller Class from the list of options under .NET Core, as shown in the following screenshot:

    Building your Web API project

  5. 命名您的新 API 控制器,例如PatientController.cs,然后单击添加继续。

  6. In your new PatientController, you will most likely have several areas highlighted with red squiggly lines due to a lack of necessary dependencies, as shown in the following screenshot. As a result, you won't be able to build your project/solution at this time:

    Building your Web API project

在下一节中,我们将学习如何配置您的 Web API,以便它在配置文件中具有适当的引用和依赖项。

在 Web 应用中配置 Web API

当一个特定的 URL 被请求时,web 服务器如何知道向浏览器发送什么? 答案在于您的 Web API 项目的配置。

设置依赖项

在本节中,我们将学习如何使用 IDE 自动设置依赖项,或者通过编辑项目的配置文件手动设置依赖项:

  1. To pull in the necessary dependencies, you may right-click on the using statement for Microsoft.AspNet.Mvc and select Quick Actions and Refactorings.... This can also be triggered by pressing Ctrl + . (period) on your keyboard or simply by hovering over the underlined term, as shown in the following screenshot:

    Setting up dependencies

  2. Visual Studio should offer you several possible options, from which you can select the one that adds the package Microsoft.AspNetCore.Mvc.Core for the namespace Microsoft.AspNetCore.Mvc. For the Controller class, add a reference for the Microsoft.AspNetCore.Mvc.ViewFeatures package, as shown in the following screenshot:

    Setting up dependencies

    添加 Microsoft.AspNetCore.Mvc.Core 1.0.0 包

  3. If you select the latest version that's available, this should update your references and remove the red squiggly lines, as shown in the following screenshot:

    Setting up dependencies

    更新您的参考资料,并删除红色的波浪线

  4. The preceding step should automatically update your project.json file with the correct dependencies for the Microsoft.AspNetCore.Mvc.Core, and Microsoft.AspNetCore.Mvc.ViewFeatures, as shown in the following screenshot:

    Setting up dependencies

  5. The "frameworks" section of the project.json file identifies the type and version of the .NET Framework that your web app is using, for example netcoreapp1.0 for the 1.0 version of .NET Core. You will see something similar in your project, as shown in the following screenshot:

    Setting up dependencies

  6. Click the Build Solution button from the top menu/toolbar. Depending on how you have your shortcuts set up, you may press Ctrl + Shift + B or press F6 on your keyboard to build the solution. You should now be able to build your project/solution without errors, as shown in the following screenshot:

    Setting up dependencies

在运行 Web API 项目之前,打开Startup.cs类文件,用Configure()方法中的app.UseMvc()调用替换app.Run()语句/块(及其内容)。 要将Mvc添加到项目中,请在ConfigureServices()方法中添加对services.AddMvcCore()的调用。 要编译此代码,请添加对Microsoft.AspNetCore.Mvc的引用。

Web API 项目的一部分

让我们来仔细看看PatientController这门课。 自动生成的类有以下方法:

  • public IEnumerable<string> Get()
  • public string Get(int id)
  • public void Post([FromBody]string value)
  • public void Put(int id, [FromBody]string value)
  • public void Delete(int id)

Get()方法只是返回一个JSON对象作为一个可枚举的值字符串,而Get(int id)方法是一个覆盖的变量,它为指定的 ID 获取特定的值。

Post()Put()方法可用于创建和更新实体。 请注意,Put()方法将 ID 值作为第一个参数,以便它知道要更新哪个实体。

最后,我们有Delete()方法,可以使用该方法删除使用指定 ID 的实体。

这些操作将工作最好与一个对象-关系映射(ORM)框架(如实体框架,我们将在第六章、【显示】使用实体框架与数据库进行交互的代码。 同时,我们将在 API 控制器代码中创建占位符对象和值。

**## 运行 Web API 项目

你可以在一个可以显示 JSON 数据的 Web 浏览器中运行 Web API 项目。

如果你使用谷歌 Chrome 浏览器,我建议使用JSONView 扩展(或其他类似的扩展)正确显示 JSON 数据。

上述扩展也可在 GitHub 在以下 URL:

https://github.com/gildas-lormeau/JSONView-for-Chrome

如果您使用Microsoft Edge,您可以直接在浏览器中查看原始 JSON 数据。 一旦您的浏览器准备好了,您就可以从 Visual Studio 的顶部工具栏中选择您想要的浏览器。 点击Debug按钮旁边的小三角形图标,然后选择浏览器,如下图所示:

Running the Web API project

在上面的截图中,您可以看到安装了多种浏览器,包括 Firefox、谷歌 Chrome、Internet Explorer 和 Edge。 要选择一个不同的浏览器,只需点击浏览与… ,在菜单中选择一个不同的。

现在,点击Debug按钮(也就是绿色的play按钮)来查看 Web API 项目在您的 Web 浏览器中的运行情况,如下面的截图所示。 如果你没有设置一个 web 应用,你将无法从根 URL 浏览网站:

Running the Web API project

如果你看到这个错误,不要担心,你可以更新 URL 来包含 API 控制器的路径; 例如,见http://localhost:12345/api/Patient

请注意,您的端口号可能有所不同。 现在,你应该能够看到 API 控制器吐出的视图列表,如下面的截图所示:

Running the Web API project

添加路由来处理预期的 URL 路径

在经典 ASP 时代,应用 URL 路径通常反映物理文件路径。 这在 ASP 中继续.NET web 表单,尽管引入了自定义 URL 路由的概念。 ASP.NET MVC,路由被设计为迎合功能而不是物理路径。

ASP.NET Web API 延续了这一较新的传统,它能够在代码中设置自定义路由。 您可以在启动代码中使用流畅的配置或使用方括号包围的声明性属性为应用创建路由。

了解路线

为了理解使用路由的目的,让我们关注一下路由在应用中的特性和好处。 这适用于 ASP。 asp.net MVC 和 asp.net.NET Web API:

  • 通过定义路由,您可以为 URL 访问引入可预测的模式
  • 这使您能够更好地控制如何将 url 映射到您的控制器
  • 人类可读的路径也是 seo 友好的,这对于搜索引擎优化非常好
  • 当涉及到揭示系统中的底层 web 技术和物理文件名时,它提供了某种程度的隐晦

设置路由

让我们从这个简单的类级属性开始,它为你的 API 控制器指定一个路由,如下所示:

[Route("api/[controller]")] 
public class PatientController : Controller 
{ 
  // ... 
} 

在这里,我们可以剖析属性(在方括号中看到,用于影响它下面的类)和它的参数,以了解发生了什么:

  • Route属性表明我们将为这个控制器定义一个路由。
  • 在后面的括号中,路由路径用双引号定义。
  • 该路径的第一部分是字符串字面值api/,它声明到 API 方法调用的路径将以术语api开始,后跟一个正斜杠。
  • 路径的其余部分是方括号中的单词controller,它指的是控制器名称。 根据约定,控制器名是控制器类名的一部分,该类名位于术语Controller之前。 对于类PatientController,控制器名称就是单词Patient
  • 这意味着该控制器的所有 API 方法都可以使用以下语法访问,其中MyApplicationServer应该替换为您自己的服务器或域名:http://MyApplicationServer/api/Patient

对于方法调用,可以定义带参数或不带参数的路由。 下面的例子演示了这两种类型的路由定义:

[HttpGet] 
public IEnumerable<string> Get() 
{ 
    return new string[] { "value1", "value2" }; 
} 

在本例中,Get()方法执行与 HTTP 动词HttpGet相关的操作,该动作声明在该方法的正上方的属性中。 这标识了在不带任何参数的情况下通过浏览器访问控制器的默认方法,这意味着可以使用以下语法访问此 API 方法:

http://MyApplicationServer/api/Patient 

要包含参数,我们可以使用以下语法:

[HttpGet("{id}")] 
public string Get(int id) 
{ 
    return "value"; 
} 

这里,HttpGet属性与"{id}"参数耦合,用双引号括起来的花括号。 方法的覆盖版本还包括一个名为id的整数值,以与期望的参数相对应。

如果不指定任何参数,则id的值等于default(int),即为零。 它可以在不带任何参数的情况下调用,语法如下:

http://MyApplicationServer/api/Patient/Get 

为了传递参数,你可以在控制器名称后添加任何整数值,语法如下:

http://MyApplicationServer/api/Patient/1 

这将把数字1分配给整数变量id

测试路线

要测试上述路由,只需从 Visual Studio 运行应用并访问不带参数的指定 url。

Testing routes

上图显示了进入以下路径的结果:

http://MyApplicationServer/api/Patient/1 

从客户端应用中使用 Web API

如果 Web API 公开公共端点,但没有客户端应用来使用它,那么它真的存在吗? 不需要太过理性,让我们来讨论一下使用客户机应用的可能方法。

你可以做以下任何事情:

  • 使用外部工具使用 Web API
  • 使用移动应用使用 Web API
  • 使用 Web 客户端使用 Web API

使用外部工具进行测试

如果没有设置客户机应用,可以使用外部工具,如Fiddler。 Fiddler 是一个免费的工具,现在可以从 Telerik 获得,在http://www.telerik.com/download/fiddler,如下截图所示:

Testing with external tools

您可以使用 Fiddler 检查在您的机器上检索和提交的 url。 您还可以使用它来触发任何 URL,并更改请求类型(Get、Post 等)。

从移动应用中使用 Web API

因为本章主要是关于 ASP.NET Core Web API,我们不会详细讲移动应用开发。 但是,需要注意的是,Web API 可以为您的移动应用项目提供后端。

移动应用可能包括 Windows 移动应用,iOS 应用,Android 应用,以及任何你可以为今天的智能手机和平板电脑构建的现代应用。 您可以参考您所选择的特定平台的文档,以确定需要什么来调用 RESTful API。

从 Web 客户端使用 Web API

在这种情况下,web 客户端指的是任何能够调用 RESTful API 的 HTML/JavaScript 应用。 至少,您可以使用直接的 JavaScript 构建一个完整的客户端解决方案来执行必要的操作。 为了获得更好的体验,您可以使用 jQuery 和许多流行的 JavaScript 框架之一。

一个 web 客户端也可以是一个更大的 ASP 的一部分.NET MVC 应用或单页应用(SPA)。 只要您的应用输出 HTML 页面中包含的 JavaScript,您就可以构建一个与后端 Web API 一起工作的前端。

总结

在本章中,我们已经了解了 ASP 的基本结构.NET Web API 项目,并观察了在 asp.net 中 Web API 与 MVC 的统一。 净的核心。 我们还学习了如何使用 Web API 作为后端来为各种前端应用提供支持。

在下一章中,我们将学习更多关于使用 HTML5、JavaScript 和 CSS3 开发交互式前端 UI 的知识。 我们将关注使用 JavaScript 来使用 Web API,同时介绍客户端工具,如 Bower、Grunt 和 Gulp。**

五、使用 JavaScript 与 Web API 交互

一个基于浏览器的 web 应用如果没有客户端代码来补充服务器端代码就不完整。 不管在 web 服务器上使用的技术和语言是什么,所有的 web 应用开发人员都应该学习如何在客户端使用 HTML 使用 JavaScript。

在本章中,我们将从构建一个充满 JavaScript 代码的 HTML 页面开始,以开发一个交互式用户界面。 我们将涵盖以下议题:

  • 使用 ASP.NET Web API 使用 JavaScript
  • JavaScript 框架
  • 客户端工具如BowerGruntGulp

使用 JavaScript 与 Web API 交互

有很多 JavaScript 框架正在争夺世界范围内的 web 开发人员的喜爱,其中一些已经流行起来。 在接下来的几节中,我们将学习AngularJSKnockoutJS。 但是首先,让我们关注使用一些基本的 JavaScript 来从 Web API 中读写数据。

本节的示例将使用以下文件:

  • HTML 文件与客户端 JavaScript 使用 Web API
  • 服务器端控制器类
  • 服务器端模型类

下面的截图显示了前面三个我们需要的文件:

Using JavaScript to interact with Web API

我们将在本章的例子中处理这些文件:

  • 静态 HTML 文件位于 web 应用的wwwroot位置
  • 控制器位于Controllers文件夹中(按惯例)
  • 患者模型在Models文件夹中(按惯例)

为了直观地看到该应用的外观,请看下面的患者记录应用的截图:

Using JavaScript to interact with Web API

准备服务器端代码

Models文件夹中创建一个新的类文件,并使用以下代码将其命名为Patient.cs:

public class Patient 
{ 
    public int Id { get; set; } 
    public string LastName { get; set; } 
    public string FirstName { get; set; } 
    public string SocialSecurityNumber { get; set; } 
} 

前面的代码为我们提供了适当数量的控制器和模型代码,以便开始使用客户端 JavaScript。

下面的 API 控制器至少有两个控制器方法来返回患者列表,或者在给定患者 ID 时只返回一个患者。 这个控制器应该有一个 using 语句来引用模型名称空间。

它以类级别的[Route]属性开始,表示如何从 web 浏览器的 GET 请求或通过浏览器中的 JavaScript 代码访问 API 控制器,如下所示:

[Route("api/[controller]")] 
public class PatientController : Controller 

在类本身内部,有一个类级实例变量,它包含一个数据结构,用于定义带有几个示例值的患者数组,如下面的代码所示。 在现实世界的应用中,这些数据通常来自一个数据库:

    Patient[] patients = new Patient[] 
    { 
        new Patient 
        { 
            Id = 1, 
            FirstName = "John", 
            LastName = "Smith", 
            SocialSecurityNumber = "123550001" 
        }, 
        new Patient 
        { 
            Id = 2, 
            FirstName = "Jane", 
            LastName = "Doe", 
            SocialSecurityNumber = "123550002" 
        }, 
        new Patient 
        { 
            Id = 3, 
            FirstName = "Lisa", 
            LastName = "Smith", 
            SocialSecurityNumber = "123550003" 
        } 
    }; 

接下来,我们有一个简单的Get()方法来返回一个可枚举的患者对象列表:

    // GET: api/patient 
    [HttpGet] 
    public IEnumerable<Patient> Get() 
    { 
        return patients; 
    } 

最后,我们有一个简单的Get(id)方法,当提供一个数字 ID 值来执行查找时,返回一个特定的患者:

    // GET api/patient/5 
    [HttpGet("{id}")] 
    public Patient Get(int id) 
    { 
        var patient = patients.FirstOrDefault((p) => p.Id == id); 
        if (patient == null) 
        { 
            return null; 
        } 
        return patient; 
    } 
} 

客户端 JavaScript

以下 HTML/JavaScript 代码可以包含在示例 HTML 文件中,以获取和显示患者记录。 它以一个典型的 HTML 开头:

<!DOCTYPE html> 
<html> 
<head> 
    <title>Patient Records App</title> 
</head> 

正文包含一个头部和一个<div>来显示数据:

<body> 

<div> 
    <h2>Patient Records</h2> 
    <ul id="patients"></ul> 
</div> 
<div> 
    <h2>Find Patient By ID</h2> 
    <input type="text" id="patientId" size="5" /> 
    <input type="button" value="Search" onclick="find();" /> 
    <p id="patient" /> 
</div> 

请注意以下几点:

  • 定义了一个 ID 为patients的空列表,用于在页面首次加载时显示患者记录列表
  • 搜索文本框的 ID 定义为patientId,允许用户通过 ID 进行搜索
  • Search按钮有一个onclick事件,该事件调用find()方法
  • 使用 IDpatient定义一个空段落,以显示调用find()方法的结果

接下来是对 jQuery 的引用,它可以是一个特定的(或最新的)版本,根据需要:

<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.3.min.js"></script> 

然后,我们开始主要的<script>来定义函数来处理 API 调用:

<script> 

在脚本的开始,让我们定义一个变量来保存 API 调用的 URI 路径:

var uri = 'api/Patient'; 

接下来,让我们确保在页面加载后立即填充患者列表:

$(document).ready(function () { 
    $.getJSON(uri) 
        .done(function (data) { 
            $.each(data, function (key, patient) { 
                $('<li>', { 
                    text: formatPatientInfo(patient) 
                }).appendTo($('#patients')); 
            }); 
        }); 
}); 

前面的代码引用了一个名为formatPatientInfo()的函数,用于格式化 API 调用返回的患者信息。 该方法定义如下:

function formatPatientInfo(patient) { 
    if (patient && patient.lastName) 
        return patient.lastName + 
            ', ' + patient.firstName + 
            ': ' + patient.socialSecurityNumber; 
    else 
        return 'No patient information found.'; 
} 

为了启用搜索功能,定义一个find()方法如下:

function find() { 
    var id = $('#patientId').val(); 
    $.getJSON(uri + '/' + id) 
        .done(function (data) { 
            $('#patient').text(formatPatientInfo(data)); 
        }) 
        .fail(function (jqXHR, textStatus, err) { 
            $('#patient').text('Error: ' + err); 
        }); 
} 

这里定义了一个id变量,以从提供给最终用户的搜索框中获取数字 ID 值。 然后,这个 ID 被附加到前面定义的 URI 的末尾,形成一个用于调用getJSON()的 URL。

一旦 API 调用被触发,JavaScript 分别用.done.fail块处理成功和失败的调用。 .done块更新。

此时,我们可以关闭<script><body><html>标签:

</script> 
</body> 
</html> 

要验证您可以在您的 web 应用中显示静态文件,执行以下步骤:

  1. 验证Main()包含。 Program.cs中的UseContentRoot(Directory.GetCurrentDirectory())
  2. 添加对Microsoft.AspNetCore.StaticFiles(1.0.0)的引用。
  3. app.UseStaticFiles()加到Startup.cs中。

以下截图显示了用户搜索 ID 为1的患者时显示的结果:

Client-side JavaScript

JavaScript 框架

当您开始向代码中添加越来越多的 JavaScript 时,您的 web 应用将变得笨拙且难以维护。 要利用提供的 JavaScript 框架,您可以从许多 JavaScript 框架中进行选择。

以下是四种流行的框架:

在本章中,我们将专注于 AngularJS 和 KnockoutJS。

使用 AngularJS 的单页应用

官方的 ASP.NET 文档中提到,AngularJS 是构建 spa 风格的 ASP 的一个选项。 网络应用。 本章中的例子来自微软的文档:http://docs.asp.net/en/latest/client-side/angular.html

开始学习 AngularJS

AngularJS 是一个开源的 JavaScript 框架,由谷歌官方维护。 在撰写本文时,最新的稳定版本是 1.5 版。 到 2016 年 7 月为止,2.0 版本正在经历几个发布候选(RC)阶段。

AngularJS 使用了 jQuery 的一个子集jqLite,它允许它在多个浏览器上操作 HTML 页面的 DOM。 如果你想在 AngularJS 应用中使用 jQuery,你必须确保 jQuery 是在angular.js文件之前加载的。

在 Visual Studio 2015 中将 AngularJS 设置到你的 web 应用中最简单的方法是在一个新的 web 应用中更新你的bower.json文件。 启动 Visual Studio 2015,点击File|New|Project,然后使用新的ASP 创建一个新的Web 应用.NET 模板(不是空的)。

这将自动为您创建一个bower.json配置文件。 您可能必须单击解决方案资源管理器中的Show All Files图标才能查看bower.json文件。 只需编辑此 JSON 文件,在"dependencies"下添加"angular""angular-route"两个条目。 使用智能感知来帮助你决定使用哪个版本号,如下截图所示:

Getting started with AngularJS

鲍尔。 json 文件

angular-route模块允许您的 web 应用使用路由和深度链接服务,以及指令。 这些术语(以及更多)将在下一节中解释。

几乎在添加这两个条目之后,必需的 JavaScript 文件将被添加到/Dependencies/Bower/下的Bower文件夹中,如下截图所示。 您可以配置 Grunt 或 Gulp 始终将必要的文件复制到wwwrootweb 根目录中的lib子文件夹中。

Getting started with AngularJS

Angular(1.5.8)和 Angular 路由(1.5.8)

下面的截图显示了你的lib文件夹在放入必要的模块后应该是什么样子:

Getting started with AngularJS

在本章的最后一节中,我们将学习更多关于使用 Bower 管理客户端代码的包的知识。

AngularJS 的语法和特性

AngularJS 可以通过几个例子来解释它的语法和特性。 HTML 文件中的 AngularJS 语法可以通过以下方法轻松识别:

  • 出现在 HTML 标记中的指令,例如ng-appng-init
  • 双花括号中的表达式,例如{{1+2}}
  • 数据绑定ng-bind,如<span ng-bind="ssn"></span>
  • 枚举数据的重复器,例如<li ng-repeat="patient in patients">
  • 用于处理用户输入的事件处理程序,例如<button ng-click="handleClick()">
  • 上述所有内容都可以包含在被称为模板的 HTML 文件中。

在 JavaScript 代码文件中,你可能会认识到以下一些语法:

  • angular.module()定义的 App 模块
  • .factory()定义的模型工厂
  • .service()定义的服务
  • .controller()定义的控制器

这并不是一个详尽的列表,本章也不打算取代 AngularJS 文档或教程。 要了解更多关于 AngularJS 的信息,请阅读官方文档https://docs.angularjs.org

用 AngularJS 构建一个 web 应用

一旦你在你的项目中建立了依赖关系,在你的 HTML 代码中添加一个指向 AngularJS 的脚本引用。 可以通过以下方式添加:

  • 在您的_Layout.cshtml文件中/Views/Shared
  • 在静态 HTML 页面中,刚好在关闭</body>标记之前

布局文件对于DevelopmentStaging/Production有不同的部分。 虽然您可以使用本地参考进行开发,但您应该使用来自内容交付网络(CDN)的共享 URL 进行登台和生产,如下所示:

<environment names="Development"> 
    <script src="~/lib/jquery/dist/jquery.js"></script> 
    <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script> 
</environment> 

对于登台和生产,AngularJS 常用的 CDN 服务器包括谷歌的 CDN 在 ajax.googleapis.com 或微软的 AJAX CDN 在 ajax.aspnetcdn.com。

静态 HTML 文件要简单得多,因为它只引用本地文件。 您还可以使用智能感知来键入angular.js文件的路径,因为当您键入时,Visual Studio 将在您的系统上定位该文件。 下面的代码做到了这一点:

    <script src="../lib/angular/angular.js"></script> 
</body> 
</html> 

以下静态 HTML 文件可以保存到项目的wwwrootweb 文件夹中,而其他 JavaScript 文件也可以保存在此:

Building a web application with AngularJS

下面的代码显示了一个名为ng-hello.html的简单 HTML 文件的内容:

<!DOCTYPE html> 
<html> 
<head> 
    <meta charset="utf-8" /> 
    <title>Hello, Angular!</title> 
</head> 
<body ng-app> 
    <h1>Hello, Angular!</h1> 

    Calculate (2016 - 1990) = {{2016-1990}} 
    <script src="../lib/angular/angular.js"></script> 
</body> 
</html> 

以下截图是ng-hello.html的输出:

Building a web application with AngularJS

注意,表达式{{2016-1990}}实际上是作为算术操作计算的,结果(26)显示在浏览器中。

下面的代码显示了在示例文件ng-binding.html中运行的数据绑定:

<!DOCTYPE html> 
<html> 
<head> 
    <meta charset="utf-8" /> 
    <title>Data Binding in Angular</title> 
</head> 
<body ng-app> 
    <h1>Data Binding in Angular</h1> 
    <h2>Patient Details</h2> 

    <ul ng-init="lastName='Smith'; firstName='John';socialSecurityNumber='123550001'"> 
        <li>Name: {{lastName}}, {{firstName}}</li> 
        <li>SSN: <span ng-bind="socialSecurityNumber"></span></li> 
    </ul> 

    <script src="../lib/angular/angular.js"></script> 
</body> 
</html> 

以下截图是ng-binding.html的输出:

Building a web application with AngularJS

请注意以下几点:

  • <body>标签中的ng-app指令指明了 Angular 应用的根元素
  • <ul>标记中的ng-init指令以<ul>块的范围初始化表达式
  • 第一个<li>元素中的双花括号计算在<ul>中初始化的变量。
  • 第二个<li>元素中的ng-bind指令是另一种绑定变量的方式
  • 两种数据绑定方式都是有效的

下面的代码显示了示例文件ng-repeater.html中的中继器:

<!DOCTYPE html> 
<html> 
<head> 
    <meta charset="utf-8" /> 
    <title>Angular Repeaters</title> 
</head> 
<body ng-app> 
    <h1>Angular Repeaters</h1> 

    <h2>Patient Records</h2> 
    <div ng-init="patients=['Smith, John','Doe, Jane', 'Smith, Lisa']"> 
        <ul> 
            <li ng-repeat="patient in patients"> 
                {{patient}} 
            </li> 
        </ul> 
    </div> 

    <script src="../lib/angular/angular.js"></script> 
</body> 
</html> 

以下截图是ng-repeater.html的输出:

Building a web application with AngularJS

在此,请注意以下几点:

  • ng-init指令初始化一个简单的患者名称数组,每个名称存储在一个字符串中
  • ng-repeat指令枚举数组中的每个患者
  • 双花括号出现在<li>元素中,并在 DOM 中生成的最终 HTML 中重复

下面的代码展示了一个使用我们在前一章中创建的 Web API 控制器的基本示例,在示例文件ng-rest.html中:

<!DOCTYPE html> 
<html> 
<head> 
    <meta charset="utf-8" /> 
    <title>RESTful Endpoints from Angular</title> 
</head> 
<body ng-app="patientRecordsApp"> 
    <h1>RESTful Endpoints from Angular</h1> 

    <div ng-controller="patientController"> 
        <ul> 
            <li ng-repeat="patient in patients"> 
                {{patient.lastName}}, {{patient.firstName}} 
            </li> 
        </ul> 
    </div> 

    <script src="../lib/angular/angular.js"></script> 
    <script src="../js/ng-rest.js"></script> 
</body> 
</html> 

它在客户端使用模型视图控制器模式,让控制器更新模型数据,然后在 HTML 中显示这些数据。 在下面的代码中,你会注意到一个名为ng-rest.js的 JavaScript 文件的引用:

(function () { 
    'use strict'; 
    var prApp = angular.module('patientRecordsApp', []); 
})(); 

(function () { 
    'use strict'; 

    var pService = 'patientFactory'; 

    angular.module('patientRecordsApp').factory(pService, 
        ['$http', patientFactory]); 

    function patientFactory($http) { 

        function getPatientsFromApi() 
        { 
            // NOTE: the port number should be changed as necessary  
            return $http.get('http://localhost:50915/api/Patient'); 
        } 

        var patientService = { 
            getPatientsFromApi: getPatientsFromApi 
        }; 
        return patientService; 
    } 
})(); 

(function () { 
    'use strict'; 

    var pController = 'patientController'; 

    angular.module('patientRecordsApp').controller(pController, 
        ['$scope', 'patientFactory', patientController]); 

    function patientController($scope, patientFactory) { 
        $scope.patients = []; 

        patientFactory.getPatientsFromApi().success(function (patientData) { 
            $scope.patients = patientData; 
            console.log("Data obtained successfully."); 
        }).error(function (error) { 
            console.log("An error has occured."); 
        }); 
    } 
})(); 

在前面的代码中,getPatientsFromApi()方法负责调用服务器端代码中的 API 方法,在 web 浏览器中显示如下结果:

Building a web application with AngularJS

要让之前的 Angular 样例正常工作,首先运行之前的 Web API 项目,并在执行 API 调用之前引用 API URL 的特定端口号。 如果你愿意,你也可以在你的 Angular 项目中添加一个新的服务器端 API 控制器。

模型-视图- viewmodel (MVVM)与 KnockoutJS

官方的 ASP.NET 文档提到 KnockoutJS 是构建模型-视图- viewmodel(MVVM)web 应用的一个选项; 见http://docs.asp.net/en/latest/client-side/knockout.html

了解 KnockoutJS

KnockoutJS 是一个流行的 JavaScript 框架,您可以单独使用它,也可以与 jQuery 和其他 JavaScript 库一起使用。 它在客户端使用 MVVM 模式,促进 HTML 元素和 JavaScript 变量之间的数据绑定。

与 AngularJS 类似,在 Visual Studio 2015 中将 KnockoutJS 设置到你的 web 应用中最简单的方法就是在一个新的 web 应用中更新你的bower.json文件。 再次启动 Visual Studio 2015 中,点击文件||项目,,然后创建一个新的 Web 应用使用新的【显示】ASP.NET 模板(不是空的)。

与前面一样,这将自动创建一个可以编辑的bower.json配置文件。 在"dependencies"下面为knockout"添加一个条目。 你可以使用智能感知来帮助你决定使用哪个版本号。 再次,您可能必须单击解决方案资源管理器中的显示所有文件以查看bower文件。 bower文件如下截图所示:

Getting started with KnockoutJS

鲍尔。 json 文件"Knockout": "3.4.0"

几乎在你添加条目之后,必要的 JavaScript 依赖会被添加到你的Bower文件夹下的/Dependencies/Bower/,如下截图所示:

Getting started with KnockoutJS

击倒(3.4.0)

下面的截图显示了 Knockout 模块放入你的lib文件夹后的样子:

Getting started with KnockoutJS

KnockoutJS 语法和特性

KnockoutJS 语法可以通过以下方式理解:

  • 在 HTML 标记中使用data-bind属性设置数据绑定
  • 使用.applyBindings()将绑定与数据模型关联起来
  • 对对象使用observable()自动更新
  • 对集合使用observableArray()自动更新
  • 为一个或多个可观察对象使用computed()的自定义绑定

要了解 KnockoutJS 的更多信息,请阅读官方文档http://knockoutjs.com/documentation/introduction.html

用 KnockoutJS 构建一个 web 应用

一旦在项目中设置了依赖项,就可以在 HTML 代码中添加对 KnockoutJS 的脚本引用。

下面的代码展示了将<span>元素绑定到示例文件ko-hello.html中的简单数据值的基本示例:

<!DOCTYPE html> 
<html> 
<head> 
    <meta charset="utf-8" /> 
    <title>Hello, Knockout!</title> 
    <script type="text/javascript" src="../lib/knockout/dist/knockout.js"></script> 
</head> 
<body> 
    <h1>Hello, Knockout!</h1> 

    <h2>Patient Details</h2> 

    Name: <span data-bind="text: patientName"></span> 

    <script type="text/javascript"> 
        var patientViewModel = { 
            patientName: 'John Smith' 
        }; 
        ko.applyBindings(patientViewModel); 
    </script> 

</body> 
</html> 

以下截图显示了ko-hello.html的输出:

Building a web application with KnockoutJS

你会注意到以下几点:

  • 一个<script>标签标识了 KnockoutJS 的位置
  • <span>标记使用data-bind属性作为文本值来显示patientName
  • 视图模型定义为一个属性patientName
  • 通过传递数据模型来调用ko.applyBindings()来建立连接

为了直接进入一个 Web API 示例,下面的代码调用一个 Web API 方法来获取示例文件ko-rest.html中的患者记录列表:

<!DOCTYPE html> 
<html> 
<head> 
    <meta charset="utf-8" /> 
    <title>Web API from KnockoutJS</title> 
    <script type="text/javascript" src="../lib/jquery/dist/jquery.js"></script> 
    <script type="text/javascript" src="../lib/knockout/dist/knockout.js"></script> 
</head> 
<body> 
    <h1>Web API from KnockoutJS</h1> 

    <h2>Patient Details</h2> 

    <ul data-bind="foreach: Patients"> 
        <li> 
            <span data-bind="text: lastName"></span>,  
            <span data-bind="text: firstName"></span> 
        </li> 
    </ul> 

    <script type="text/javascript"> 
    var patientViewModel = function () { 
        var self = this; 
        self.Patients = ko.observableArray(); 
        LoadPatientData(); 

        // NOTE: the port number below should be changed as necessary  
        function LoadPatientData() { 
            $.ajax({ 
                type: "GET", 
                url: "http://localhost:50915/api/Patient", 
                contentType: "application/json; charset=utf-8", 
                dataType: "json", 
                success: function (data) { 
                    self.Patients(data);  
                }, 
                error: function (error) { 
                    console.log("An error has occurred."); 
                } 
            }); 
        } 
    }; 
    ko.applyBindings(new patientViewModel()); 
    </script> 

</body> 
</html> 

以下截图显示了ko-rest.html的输出:

Building a web application with KnockoutJS

在前面的例子中,你会注意到以下几点:

  • 一个<script>标记标识 jQuery 的位置
  • 第二个<script>标签标识了 KnockoutJS 的位置
  • <ul>标签使用data-bind属性来枚举患者列表
  • 每个<li>包含<span>标签与lastNamefirstName绑定
  • 一个基本的ViewModel被定义为容纳一个observableArray()患者
  • 一个名为LoadPatientData的函数调用 Web API
  • 最后,使用ViewModel调用ko.ApplyBindings()进行绑定

同样,如果是在不同的项目中,您必须确保 Web API 已经启动并运行。

使用 Bower、Grunt 和 Gulp 的任务运行器、捆绑和缩小

通过 Visual Studio 2015,微软引入了与客户端包管理器和任务运行器更紧密的集成。 为了充分利用这些工具,最好了解它们做什么以及何时使用它们。

为什么我们需要任务自动化?

你可以在没有智能感知的基本文本编辑器中逐行编写代码,但你很快就会渴望 Visual Studio 或富 IDE 来完成更多的工作。 这类似于手动执行某些任务,当您可以使用自动化工具使您的生活更轻松时。

使用 Bower 作为包管理器

要处理客户端依赖关系,您需要使用与开发环境配合良好的包管理器。 Bower 就是这样一个工具。

您将不再使用NuGet来安装诸如 jQuery 这样的客户端包,而是使用 Bower 来获取 JavaScript 库和 CSS 框架。 不必等待 NuGet 上出现最新版本的软件包,您可以使用 Bower 获得这些软件包,世界各地的开发人员已经在使用它。

如果您使用 starter 模板创建一个新的 web 项目,那么您将已经设置了一个 Bower 配置文件。 要从头开始设置它,创建一个新的空 web 项目,并为其创建一个新的配置文件,并遵循以下步骤:

  1. 在 Visual Studio 2015 中,点击File|New|Project
  2. 选择ASP.NET Core Web Application,并输入项目名称和位置。
  3. 为 ASP 选择模板。 净的核心。
  4. 右键单击 web 项目,然后点击添加|新项目
  5. 在客户端类别下,添加新的 Bower 配置文件,通常命名为bower.json

**在bower.json中,添加一个新的 bootstrap 依赖,如下所示:

{ 
  "name": "asp.net", 
  "private": true, 
  "dependencies": { 
    "bootstrap": "3.3.7" 
  } 
} 

使用智能感知来决定你想使用哪个版本的引导。 几乎立刻,wwwroot内的lib子文件夹就会被填充上引导(你添加的依赖项)和jquery(bootstrap 所依赖的)所需的文件,如下截图所示:

Using Bower as your package manager

该位置是基于.bowerrc文件中的条目配置的,该文件位于bower.json旁边,但通常隐藏在解决方案资源管理器中。 以下代码来自.bowerrc:

{ 
    "directory": "wwwroot/lib" 
} 

有关使用 Bower 作为 ASP 的客户端包管理器的更多信息.NET Core 项目,查看官方文档http://docs.asp.net/en/latest/client-side/bower.html

使用 Gulp 和 Grunt 作为任务运行器

要在你的项目中设置 Gulp 和 Grunt,在项目级别添加一个新项目,类型为NPM 配置文件,名称为package.json。 然后,更新devDependencies部分,包括以下对 Gulp 和 Grunt 的引用:

{ 
  "version": "1.0.0", 
  "name": "asp.net", 
  "private": true, 
  "devDependencies": { 
    "gulp": "3.9.1", 
    "gulp-less": "3.1.0", 
    "grunt": "1.0.1", 
    "grunt-contrib-clean": "1.0.0" 
  } 
} 

在解决方案资源管理器中,验证这些依赖项现在在依赖项内的NPM文件夹中可见。 然后,添加一个名为styles.lessLESS文件到项目根目录的less子文件夹中,使用下面的 LESS 代码:

@light-gray: #C0C0C0; 
@darker-gray: @light-gray - #222; 

#banner { 
  color: @darker-gray; 
} 

这段 LESS 代码将自动为 ID 值为banner的 HTML 元素生成较深的灰色阴影,例如<div id="banner">

向项目中添加两个新项目,类型为Gulp 配置文件Grunt 配置文件,通常分别命名为gulpfile.jsgruntfile.js

将以下代码添加到gulpfile.js中,以监视 LESS 文件并在发生更改时编译 CSS 输出。 第一行中的指令还指示 Visual Studio 基于特定条件运行每个任务,比如在构建之后或在项目打开时:

/// <binding AfterBuild='stylemaker' ProjectOpened='lesswatcher' /> 
var gulp = require('gulp'); 
var gulpless = require('gulp-less') 

gulp.task('stylemaker', function () { 
    gulp.src("./less/styles.less") 
        .pipe(gulpless({ compress: true })) 
        .pipe(gulp.dest("./wwwroot/css")); 
}); 

gulp.task('lesswatcher', function () { 
    gulp.watch("./less/*.less", ["stylemaker"]); 
}); 

接下来,将以下代码添加到gruntfile.js中,以确保 CSS 输出文件夹被清理:

module.exports = function (grunt) { 
    grunt.initConfig({ 
        clean: ["wwwroot/css/*"], 
    }); 
    grunt.loadNpmTasks("grunt-contrib-clean"); 
}; 

可以通过任务运行器窗口运行上述任务,也可以通过配置绑定使任务根据特定条件运行。 如果任务运行程序管理器窗口不可见,在 Visual Studio 顶部菜单中单击View|Other Windows|任务运行程序管理器任务运行器管理器窗口如下截图:

Using Gulp and Grunt as task runners

有关使用 Grunt 和 Gulp 作为 ASP 的任务运行器的更多信息.NET Core 项目,查看以下网址的官方文档:

总结

在本章中,你学习了如何在 JavaScript 中使用 Web API,之后你学习了 AngularJS 和 KnockoutJS 的基础知识。 您还学习了客户端工具,这些工具在构建 ASP 时帮助自动化一些常见任务.NET web 应用在 Visual Studio 2015。

在下一章中,您将学习如何构建数据库驱动的 web 应用,而不需要编写 SQL 代码。 相反,您将使用Entity Framework,这是一个对象-关系映射器,允许您编写代码来表示数据库实体。**

六、在代码中使用实体框架与数据库交互

第三章理解 MVC中,我们在构建功能性 ASP 的同时提到了实体框架(EF).NET Core MVC web 应用。 在本章中,我们将深入了解 EF Core 1.0,并介绍在开发过程中你需要了解的关于 EF Core 的知识,也就是 EF7。

值得注意的是,EF Core 可以与不止 ASP 一起使用.NET Core 和关系数据库。 事实上,它也被设计用于与环球 Windows 平台(UWP)应用在 Windows 上 10 和 Windows 桌面应用(Windows Presentation Foundation (WPF)或【显示】WinForms)。 除了 SQL Server, EF Core 还可以与Azure Table StoragePostgreSQLSQLite,甚至NoSQL数据库一起使用。****

****为了本书的目的,我们将主要关注 ASP.NET Core、SQL Server 和新的内存提供程序。

.NET 中的对象关系映射

如果您以前使用过 EF 或任何对象关系映射器(ORM),那么您可以略读本节并继续下一节。 但是,如果您是 ORM 框架的新手,那么本节是为您准备的。

像 EF 这样的 ORM 使您可以很容易地在应用代码中与数据库进行交互。 下面的截图说明了 ORM:

Object-relational mapping in .NET

为什么使用 ORM?

在没有任何 ORM 的情况下构建一个功能齐全的 web 应用是完全可行的。 你可以直接使用 ADO.NET 或原始 SQL 从应用代码与数据库对话。 您可以编写 SQL 代码来创建数据库对象和关系。 您可以通过大量使用存储过程和函数来将所有数据库逻辑保存在数据库中,或者您可以使用像 EF 这样的 ORM 并利用Code First方法。

虽然数据库管理员可能会嘲笑 ORM 的使用,但开发人员应该有信心,他们将获得更多的权力和控制如何创建、操纵和维护数据库实体。 以下是你可以考虑的利弊清单。

使用 ORM 的优点:

  • 公共代码库:您可以在代码中定义所有实体,而不是在应用代码库之外断开数据库创建代码
  • 编译时的好处:如果你在模型代码中犯了某些错误,编译器会捕获它们
  • 更容易的更新:如果你需要更新/重命名模型中的任何东西,你的 IDE 会让它变得轻而易举
  • 无忧约束:您可以使用属性和流畅的代码轻松设置键、关系和其他约束
  • 可管理的维护:在迁移的帮助下,您和您的团队可以根据需要升级/降级您的数据库,并跟踪修订。

使用 ORM 的缺点:

  • 学习曲线:如果你以前没有使用过 ORM,你不可能一天之内就把它添加到现有的应用中。 在更新的项目中开始使用它,在更新现有项目时花点时间。
  • 复杂查询:如果你或你的团队已经精通 SQL 查询,你可能有困难重建复杂查询的结果使用综合语言查询(LINQ)语法。 随着时间的推移,您可能会变得更容易,或者您可能会认为 orm 不适合特定的应用。
  • 性能(有争议):当第一次引入 orm 时,您可能已经考虑了在应用代码和数据库之间添加另一层对性能的影响。 但对于现代 ORM 框架(如 EF Core),性能的改进应该使其值得付出努力,而无需太过担心。

现在我们已经了解了优点和缺点,让我们关注一下为什么您会选择 EF 作为您的 web 应用的 ORM。

为什么是实体框架?

如果你以前使用过 orm,你可能使用过NHibernate或更早版本的 EF。 您可能已经在 Java 应用中使用了Hibernate。 不管你的背景如何,有很多理由说明 EF 对你的 ASP 来说是一个不错的选择.NET Core 应用。

首先,新的 EF 与新的 ASP.NET 不像以前的其他版本。 早期版本的 EF 运行时和工具是与。net 框架一起发布的,但是最近的版本将运行时分离出来,从而通过 NuGet 实现带外发布。 运行时的最新版本可以通过 NuGet 获得,而主要版本与 Visual Studio 版本同步。

实体框架的演变

随着时间的推移,EF 已经有了很大的进步。 代码优先方法和 EF 迁移的引入让许多开发人员注意到这一点。 与此同时,还有很多开发人员还没有与 EF 合作过。

多年来,EF 团队一直在不断收集开发人员对其产品的反馈。 因此,该团队已经将一个成熟的产品整合在一起,并且随着时间的推移,这个产品会变得越来越好。

如果您有兴趣提供反馈或查看来自其他开发人员的输入,请访问https://data.uservoice.com

出现的一些新特性包括批处理更新、阴影属性、内存提供程序以及处理断开连接数据的改进方法。 我们将在本章中更详细地讨论新特性,通过一个示例项目来说明这些特性是如何结合在一起的。

5 .ef .NET Framework vs . EF Core 1.0

如果你一直使用 EF 6 到现在,你应该花时间了解 EF Core 有什么变化。 EF 的基本概念和用法应该是大家熟悉的,但是这里有一些新的添加(和一些删除),这将影响你学习 EF Core 的学习曲线。

如果您一直在进行代码优先开发,那么您应该已经熟悉了在应用代码中构建自己的模型。 如果您已经使用了 Migrations,那么您还应该熟悉如何通过触发迁移来生成/更新数据库。 但是,如果您已经习惯了可视化数据建模工具和 EDMX 文件,那么您可能需要忘掉一些东西。 EDMX 文件是实体数据模型文件,可以使用早期版本的 EF 可视化地表示数据模型。

EF Core 有什么不同

首先,值得注意的是,不再支持将 EDMX 文件作为创建数据模型的方法。 这并不意味着没有方法可视化地建模您的数据库,或者从数据库优先的方法开始。 相反,如果您愿意,您仍然可以从一个现有的数据库开始,并使用外部工具来生成您的模型类。

也就是说,我们将专注于 EF Core 的代码优先方法。 这很重要,因为它将帮助您更好地控制模型类,这将使代码合并更容易。

EF 以前的版本依赖于ObjectContext作为在代码中将数据库实体作为对象的一种方法。 即使在 EF 4.1 中使用了DbContext API,下面仍然继续依赖于原来的ObjectContext。 在 EF Core 中,ObjectContext将离开我们,但DbContext保留。 代表了一系列设计模式的组合,这些设计模式允许您从应用代码中轻松地使用数据库。

虽然 EF6 可以通过 NuGet 获得,但 EF Core 将其依赖管理提升到与 ASP 保持一致的另一个级别。 净的核心。 您可以选择只使用您需要的部分,包括针对 Core、Commands、Relational(包括迁移)和 SQL Server 的单独包。 如果您包含一个特定的包作为引用,Visual Studio 2015 将根据需要自动包含额外的依赖项。

EF Core 入门

如果你在 Visual Studio 2015 中使用标准的 Web 应用模板创建一个新的 Web 应用项目,你应该已经设置了 EF Core。 您可能还记得第 3 章、了解 MVC中的患者记录 web 应用,在其中我们建立了一个具有数据库连接的基本 web 项目。

如果您从一个空项目开始,您应该在您的project.json配置文件中键入 EF 引用。 在我的患者记录应用中,我有以下参考:

"Microsoft.EntityFrameworkCore.SqlServer": "1.0.0" 

在“解决方案资源管理器”面板中,您可以展开每个引用,以识别自动作为依赖项拉入的相关引用,如下面的截图所示:

Getting started with EF Core

根据添加引用的时间,版本号可能有所不同。 虽然你可以在配置文件中输入任何当前或以前的版本,但你很可能会让 Visual Studio 的智能感知功能用弹出的工具提示建议来指导你。

Startup.cs文件中,应该在ConfigureServices()方法中调用AddEntityFrameworkStores(),然后调用添加特定的数据库提供程序和要使用的数据库上下文。 要使用 EF Core,首先要确保以下 using 语句出现在你的Startup.cs文件中:

using Microsoft.EntityFrameworkCore; 

在我的患者记录申请中,ConfigureServices()方法如下:

public void ConfigureServices(IServiceCollection services) 
{ 
    // Add framework services. 
    services.AddDbContext<ApplicationDbContext>(options => 
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); 

    services.AddIdentity<ApplicationUser, IdentityRole>() 
        .AddEntityFrameworkStores<ApplicationDbContext>() 
        .AddDefaultTokenProviders(); 

    // rest of method removed for brevity 
} 

前面的代码包含以下术语和方法调用:

  • Microsoft.EntityFrameworkCore:此命名空间提供对 EF Core 的访问
  • AddDbContext<>():这增加了一个db上下文,称为ApplicationDbContext
  • UseSqlServer():配置 SQL Server 数据库连接

数据库上下文位于其自己的类文件ApplicationDbContext.cs中,位于您的Data文件夹中。 在这里您将添加一个或多个DbSet条目来表示您的实体。

数据库连接字符串可以作为文本值存在于appsettings.json文件中,该文件位于项目位置的根目录中。 这个连接字符串还可以作为特定环境的环境变量存在,无论它是开发机器、内部服务器还是云环境。

设置好引用和配置文件之后,现在就可以在代码中使用 EF 了。

还有什么新鲜事吗?

在我们进入剩下的代码之前,让我们看看 EF Core 还有什么新的地方:

  • Migrations History:在 EF 的以前版本中,每次新的迁移(即数据库修订)都会在数据库中的一个特殊表中生成一个新条目。 EF Core 中仍然存在这样一个表,它被称为__EFMigrationsHistory,但是它被简化了,除了产品版本之外,只包含一个字母数字迁移 ID。
  • 快照:迁移历史表用于包含每次迁移的快照,但它已被移动到Data文件夹中的Migrations子文件夹(如ApplicationDbContextModelSnapshot.cs)中的代码文件中。 实际的文件名可能会根据数据库上下文的名称而有所不同。
  • 批量更新:EF Core 根据应用代码生成 SQL 语句来运行数据库命令。 在设置数据库上下文时,可以配置最大批处理大小。
  • 阴影属性:您可以使用阴影属性在数据库上下文中从OnModelCreating()方法中动态扩展您的模型。 这些更改将在您的数据库中进行,但不会影响实际的模型文件。
  • 内存提供程序:与其直接与实际的数据库交互,你可以选择使用内存提供程序。 这在设置和运行单元测试时也很有用。
  • 断开连接的数据:有一些改进的方法可以处理断开连接的数据,这应该可以让开发人员更容易地在断开连接的数据场景中添加和更新数据。

代码优先的数据库设计方法和关系

那么到底什么是代码优先的方法呢? 正如它听起来的那样。 可以在代码中将数据库对象建模为实体类。 要建立这些对象之间的关系,可以定义一个类以包含其他类作为成员变量。 如果您已经有一个现有的数据库,您可以在您的代码中创建一个实体模型来表示一些(或全部)数据库对象。

从我们从第 3 章了解 MVC的病人记录例子中,我们已经有了一个代表人的模型类。 在这一章中,我们将增加一个RobotDoctor班到我们的项目中,为一个未来医院与机器人医生建立一个电脑化系统。 然后我们将在代码中建立HumansRobotDoctors之间的关系,使每个机器人医生可以分配给数据库中的一个或多个人类患者。

更新模型

Models文件夹的示例项目从第 3 章,理解 MVC【显示】,让我们添加一个新的模型类来表示一个RobotDoctor``Human然后更新现有的类来添加字段识别每一个人类与机器人医生的关系。 我们还必须更新上下文文件ApplicationDbContext.cs,为每一组实体使用单独的DbSet

要添加RobotDoctor类,请执行以下步骤:

  1. 在“解决方案资源管理器”面板中,右键单击Models文件夹。

  2. 在上下文菜单中点击添加|新项目

  3. 将新类命名为RobotDoctor.cs

  4. RobotDoctor类添加以下代码:

    using System.ComponentModel.DataAnnotations; 
    
    namespace PatientRecords.Models 
    { 
        public class RobotDoctor 
        { 
            [Display(Name = "Robot Doctor ID")] 
            public int RobotDoctorId { get; set; } 
    
            [Display(Name = "Model Number")] 
            public int ModelNumber { get; set; } 
    
            [Display(Name = "Preferred Name")] 
            public string PreferredName { get; set; } 
        }  
    } 
    
    

将使用RobotDoctorId整数字段作为主键。 其余字段将分别存储和显示型号和首选名称。 Display属性将用作字段的友好文本标签。

要更新 Human 类,请遵循以下步骤:

  1. Models文件夹中打开Human.cs类文件。
  2. 在类的底部添加以下字段:
[Display(Name = "Robot Doctor")] 
public int RobotDoctorId { get; set; } 
public RobotDoctor RobotDoctor { get; set; } 

Human类中的RobotDoctorId整数字段将作为外键引用RobotDoctor类中相应的字段。 对象将允许将每个Human分配给特定的RobotDoctorDisplay属性将作为一个友好的文本标签用于字段。

要更新数据库上下文,请遵循以下步骤:

  1. Data文件夹中打开ApplicationDbContext.cs类文件。
  2. 在班级底部为RobotDoctors增加一个新的DbSet
  3. 通过将单词Human复数化为Humans来更新HumansDbSet,如下代码所示:

命名为RobotDoctorsDbSet将代表存储在数据库中的一组机器人医生。 命名为HumansDbSet将表示存储在数据库中的Humans集合。 虽然没有必要将其复数化,但这一微小的变化应该有助于阐明其目的。

更新控制器

当我们将ApplicationDbContext类中命名为HumanDbSet更新为Humans时,我们的HumanController仍然保留对旧名称的引用。 有两种方法可以解决这个问题:你可以在控制器文件中手动重命名HumanHumans的所有引用,或者你可以使用 Visual Studio 内置的重命名功能。

使用重命名功能,您可以点击DbSet名为Human在您的代码(重命名之前Human``Humans),然后用一套快速跟踪击键:**Ctrl + R+【显示】。 这将允许您在引用新值的任何地方输入新值。 你将有机会通过选中相应的复选框来预览你的更改,如下图所示:

*Updating the controllers

在我们更新其余的控制器方法之前,请确保以下 using 语句位于HumanController类的顶部:

using System.Threading.Tasks; 
using System.Collections.Generic; 
using System; 

以下命名空间对于我们将在控制器中执行的任务是有用的:

  • 命名空间将允许我们使用异步方法。
  • Collections.Generic命名空间允许我们使用IEnumerable列表。
  • 命名空间System将允许我们使用String方法。

为了确保在Humans列表中包含每个HumanRobotDoctor数据,更新HumanController类的Index()方法以包含以下代码:

public async Task<IActionResult> Index() 
{ 
    var humans = _context.Humans.Include(h => h.RobotDoctor); 
    return View(humans); 
} 

在前面的代码中,对数据库上下文中名为HumansDbSet的调用和RobotDoctor数据由Include()方法拉出。 然后,该方法将人员集返回到相应的视图。 然后,视图负责遍历一个列表Humans以显示给用户。

为了确保我们在其Details页面中包含特定HumanRobotDoctor数据,更新Details()方法以包含以下代码:

public async Task<ActionResult> Details(int? id) 
{ 
    Human human = await _context.Humans 
        .Include(h => h.RobotDoctor) 
        .SingleOrDefaultAsync(h => h.ID == id); 
    if (human == null) 
    { 
        return NotFound(); 
    } 
    return View(human); 
} 

注意,命名为HumansDbSet后面是对Include()的调用,以确保RobotDoctor数据也包含在内。 在此过程中,我们还通过包括以下内容来利用异步方法调用:

  • 方法定义中的async关键字
  • Task<ActionResult>返回类型而不是ActionResult
  • SingleOrDefaultAsync()而不是Single()
  • 方法调用中的await关键字

接下来,让我们更新Create()方法的HttpGet版本,以便在显示一个创建新Humans的入口表单之前获得RobotDoctors列表。 由于HttpGet是控制器方法的默认谓词,因此该方法不需要一个属性来指示其HttpVerb。 用以下代码替换Create():

public IActionResult Create() 
{ 
    ViewBag.RobotDoctors = GetListOfRobotDoctors(); 
    return View(); 
} 

private IEnumerable<SelectListItem> GetListOfRobotDoctors(int selected = -1) 
{ 
    var tmp = _context.RobotDoctors.ToList();   

    // Create authors list for <select> dropdown 
    return tmp 
        .OrderBy(rb => rb.ModelNumber) 
        .Select(rb => new SelectListItem 
        { 
            Text = String.Format("{0}: {1}", rb.ModelNumber, rb.PreferredName), 
            Value = rb.RobotDoctorId.ToString(), 
            Selected = rb.RobotDoctorId == selected 
        }); 
} 

在前面的代码中,Create()方法之后添加了新的私有方法来完成获取数据的工作。 然后,Create()方法返回默认视图Create.cshtml,以显示一个输入表单。

接下来,我们还需要更新Create()方法的HttpPost版本。 该方法将在方法定义之上特别具有HttpPost属性。 用下面的代码更新这个Create()方法:

[HttpPost] 
[ValidateAntiForgeryToken] 
public async Task<ActionResult> Create([Bind("SocialSecurityNumber", "DateOfBirth", "FirstName", "LastName", "RobotDoctorId")] Human human) 
{ 
    if (ModelState.IsValid) 
    { 
        _context.Humans.Add(human); 
        await _context.SaveChangesAsync(); 
        return RedirectToAction("Index"); 
    } 
    return View(human); 
} 

另一件要注意的事情是Create()方法参数列表中的Bind属性。 该属性帮助我们准确地确定希望将哪个模型属性绑定到相应的表单字段。

最后,让我们用以下代码更新Edit()方法,从HttpGet版本开始。 与Create()方法一样,HttpGet版本不需要在其上面有任何属性来指示控制器方法使用的 HttpVerb:

public async Task<IActionResult> Edit(int? id) 
{ 
    if (id == null) 
    { 
        return NotFound(); 
    } 

    Human human = _context.Humans.Single(m => m.ID == id); 
    if (human == null) 
    { 
        return NotFound(); 
    } 

    ViewBag.RobotDoctors = GetListOfRobotDoctors(); 
    return View(human); 
} 

对这个Edit()方法的主要更改是添加了一个名为RobotDoctors的动态ViewBag属性,与Create()方法中所看到的类似。 它使用相同的私有方法来获取RobotDoctors列表。 它将Human模型返回到视图Edit.cshtml,视图显示一个条目表单来编辑现有的Human条目。

最后,让我们更新Edit()方法的HttpPost版本。 用以下代码替换此方法,以确保Human模型更新为已传递的数据:

[HttpPost] 
[ValidateAntiForgeryToken] 
public async Task<IActionResult> Edit(int id, [Bind("SocialSecurityNumber", "DateOfBirth", "FirstName", "LastName", "RobotDoctorId")] Human human) 
{ 
    if (id != human.ID) 
    { 
        return NotFound(); 
    } 

    if (ModelState.IsValid) 
    { 
        human.ID = id; 
        _context.Humans.Attach(human); 
        _context.Entry(human).State = EntityState.Modified; 
        await _context.SaveChangesAsync(); 
        return RedirectToAction("Index"); 
    } 
    return View(human); 

} 

为了通知 EF Core 修改的状态,将编辑条目的State属性设置为EntityState.Modified。 如果存在任何验证错误,则通过传入更新的Human模型,将用户定向到相同的Edit.cshtml视图。 如果没有验证错误,则将用户重定向到Index.cshtml视图,以显示Human条目的当前列表。

Delete()方法不需要任何额外的更改,因为实体代码生成负责正确删除每个实体。 当使用适当的控制器方法删除选定的Human时,它将显示一个由Delete()方法的HttpGet版本准备的确认屏幕。 一旦确认,Delete()方法的HttpPost版本将在调用SaveChanges()以相应更新数据库之前负责实际的删除。 在删除操作之后,用户被重定向到Index方法以查看Human条目列表。

更新视图

要完成编码更改,必须更新Human控制器的Views。 这些文件包括在Views文件夹中的Human子文件夹中的以下.cshtml文件:

  • 指数
  • 编辑
  • 细节
  • 创建

Index.cshtml文件中,添加以下表头<th>块,显示机器人医生的首选名称的标签,就在 SSN 标签之后:

<th> 
    @Html.DisplayNameFor(model => model.RobotDoctor.PreferredName) 
</th> 

foreach内循环遍历项目Model,<td>添加一个表数据块显示名字,SSN 之后字段,用于存储一个独特的社会安全号码。 这应该正好在编辑细节删除链接之前:

<td> 
    @Html.DisplayFor(modelItem => item.RobotDoctor.PreferredName) 
</td> 

Edit.cshtml文件中,更新<form>标记以包含额外的标记帮助器属性:

<form  
    asp-controller="Human"  
    asp-action="Edit"  
    method="post"  
    asp-route-id="@Model.ID"> 

这些属性的用途有很多:

  • asp-controllerasp-action:这表示将表单提交到的控制器名称和方法
  • method:表示提交表单的方法,通常为POST
  • asp-route-id:表示要与提交关联的 ID 值

在表单验证摘要的下方,删除隐藏的 ID 字段:

<input type="hidden" asp-for="ID" /> 

相反,用下面的代码替换它:

<div class="form-group"> 
    <label  
        asp-for="RobotDoctorId"  
        class="col-md-2 control-label"></label> 
    <div class="col-md-10"> 
        <select  
                asp-for="RobotDoctorId"  
                asp-items="@ViewBag.RobotDoctors"></select> 
    </div> 
</div> 

前面的代码显示了RobotDoctorId字段的标签,后面是RobotDoctors的下拉框。 您可能还记得,正在多个控制器方法中设置ViewBag.RobotDoctors的值。

Details.cshtml文件中,在<dt>/<dd>对块中添加以下描述列表条目,以显示每个机器人医生的编号、型号和首选名称的标签和字段,就在 SSN 标签的右侧:

<dt> 
    @Html.DisplayNameFor(model => model.RobotDoctor.RobotDoctorId) 
</dt> 
<dd> 
    @Html.DisplayFor(model => model.RobotDoctor.RobotDoctorId) 
</dd> 
<dt> 
    @Html.DisplayNameFor(model => model.RobotDoctor.ModelNumber) 
</dt> 
<dd> 
    @Html.DisplayFor(model => model.RobotDoctor.ModelNumber) 
</dd> 
<dt> 
    @Html.DisplayNameFor(model => model.RobotDoctor.PreferredName) 
</dt> 
<dd> 
    @Html.DisplayFor(model => model.RobotDoctor.PreferredName) 
</dd> 

前面的条目应该在关闭</dl>列表之前添加。

Edit视图类似,您必须使用新标签和下拉菜单更新Create视图,以显示RobotDoctors列表。 在表单中的验证摘要下面,添加以下表单组:

<div class="form-group"> 
    <label  
        asp-for="RobotDoctorId"  
        class="col-md-2 control-label"></label> 
  <div class="col-md-10"> 
    <select  
        asp-for="RobotDoctorId"  
        asp-items="@ViewBag.RobotDoctors"></select> 
  </div> 
</div> 

再一次,在适当的控制器方法中设置了ViewBag.RobotDoctors的动态值。 现在已经对模型、控制器和视图进行了所有必要的更改,现在可以使用 EF Migrations 更新数据库了。

EF Code First 迁移,用于数据库版本控制和维护

在 EF 中,您可以使用迁移来促进数据库的创建、升级和降级。 您还可以使用自动生成的版本历史来跟踪更改,并与开发团队的其他成员保持同步。

我们之前已经在我们的示例项目中设置了迁移以便开始工作。 在本节中,我们将添加一个新的迁移,以反映我们在本章中所做的更改。

设置迁移

下面回顾一下我们之前如何为 EF 设置迁移。 再次强调,请记住您的 DNX 版本可能有所不同:

  1. 打开指向项目文件夹位置的命令提示符。
  2. 执行如下命令:
>dotnet restore
>dotnet build
>dotnet ef migrations add Initial
>dotnet ef database update

前面的命令不需要再次运行,因为您的初始迁移已经在第 3 章了解 MVC中创建。 相反,我们将运行额外的命令来创建一个新的迁移来表示对模型和数据库上下文的更改。

添加和删除迁移

当你添加一个新的迁移时,会发生以下几件事:

  • 在初始迁移文件下面的Migrations文件夹中将生成一个新的类文件。 文件名通常以日期/时间戳作为前缀,然后是迁移的名称,例如2016MMDDXXYY_RobotDoctors.cs.
  • 迁移文件的内容将包含一个带有迁移名称的类,例如RobotDoctors。 这个类将包含两个方法Up()Down(),分别帮助数据库的升级和降级。
  • 一个自动生成的快照文件将被更新,以反映数据库模型和关系的当前状态。 文件名通常以数据库上下文的名称作为前缀,例如ApplicationDbContextModelSnapshot.cs
  • 在运行 update 命令之后,将使用适当的更改更新物理数据库。 如果由于某种原因无法成功处理更改,将返回一个或多个错误消息。

要添加新的迁移,请在项目文件夹位置的命令提示符中运行以下命令:

>dotnet restore
>dotnet build
>dotnet ef migrations add RobotDoctors
>dotnet ef database update

这将生成一个新的迁移,如前所述。 如果您得到任何数据库冲突错误,您可能必须首先删除Humans表中的记录,然后重新运行数据库更新命令。 现在可以检查数据库以检查新的更改。 为了快速找到数据库,使用SQL Server Object Explorer面板钻到你的数据库并检查表和字段。

后退一步,您可以使用remove命令删除之前的迁移。 完整的命令列表如下:

  • add:这将添加一个新的迁移
  • apply:这将把迁移应用到数据库
  • list:这将显示迁移列表
  • script:这将生成一个 SQL 脚本列表,而不需要实际更新数据库
  • remove:这将删除之前的迁移

值得注意的是,脚本命令类似于 EF 以前版本中的脚本参数。 不再将此特性隐藏在参数中(如果忘记包含它,可能会意外覆盖数据库),现在有单独的命令来更新数据库或只生成 SQL 脚本,而不更改任何内容。

运行应用

现在已经使用新的迁移完成了所有必要的数据库更改,现在可以运行应用了。 在 Visual Studio 2015 中构建解决方案并运行应用。 这将从Home控制器启动默认的 web 浏览器,主Index页面,如下截图所示:

Running your application

在顶部菜单中,点击Humans链接,可查看Human控制器的Index页面。 这将显示一个空列表,因为此时我们还没有创建任何新的Humans。 如果您在进入此步骤之前已经创建了自己的条目,请在继续之前删除您的条目。

下图为Human控制器Index界面:

Running your application

为了确保我们有一些示例数据,打开RobotDoctors表,并使用 Visual Studio 中的SQL Server 对象管理器手动添加一些条目到数据库中。 下面的截图显示了RobotDoctorIdModelNumberPreferredName的一些示例值:

| ***RobotDoctorId (auto)*** | ***ModelNumber*** | ***PreferredName*** | | 1 | 90210 | X 博士 | | 2 | 12345 | 芝加哥博士 |

我们可以写一些额外的代码来添加种子数据到你的数据库中,但我们将把这一步保存到第 7 章依赖注入和健壮 Web 应用的单元测试中。 最好使用一个测试框架来添加种子数据,而不是将其添加到应用代码中。

回到您的 web 浏览器,单击左上角区域中的Create New链接,查看HumanControllerCreate视图。 选择一个机器人医生,然后完成并提交表单,创建一个新的人类条目。 如果有任何验证错误,将提示您在提交完成之前更正它们。 一旦提交,你应该被重定向到Index页。 Create视图如下截图所示:

Running your application

在下面的截图中,在Index页面上,你应该看到标签为EditDetails,andDelete的链接。 让我们试试这些链接来编辑条目,查看条目的详细信息,或删除现有条目:

Running your application

如果您单击Edit链接,您现在应该看到先前添加条目的可编辑详细信息,如下面的截图所示。 更改一些值并提交表单。 与创建新条目类似,您将被提示修复任何验证错误,或者在提交时返回Index页面:

Running your application

如果您单击Details链接,您应该看到先前添加条目的不可编辑详细信息,如下面的截图所示。 您可以选择再次返回Index页面或单击Edit查看可编辑视图:

Running your application

如果您点击Delete链接,您将看到一个确认页面,询问您是否要删除该条目,如下面的截图所示。 点击删除按钮继续或返回Index页面:

Running your application

总结

在这一章中,我们讨论了 EF Core,它是一个用于 web 应用的现代 ORM 工具。 出于本书的目的,我们只讨论了 EF Core 与 ASP 的关系。 净的核心。 关于 EF Core 的更多信息,您可以查看官方文档https://ef.readthedocs.org

在下一章中,您将涵盖依赖注入反转控制单元测试。 这将为您提供良好的应用设计基础,以确保您拥有健壮的、可测试的应用,这些应用可以放心地轻松更新。*****

七、健壮 Web 应用的依赖注入和单元测试

控制反转(IoC)是一个容易被一些开发人员忽略的主题,他们可能永远都不需要这个高级概念。 在过去,开发人员可以选择推出自己的代码,或者使用众多 IoC 容器中的一个在代码中引入依赖注入(DI)。 ASP.NET Core,您可以选择使用内置的 DI 特性,或者使用您可能已经熟悉的现有 IoC 容器。

在本章中,我们将从介绍 IoC 以及 DI 如何帮助您构建更好的应用开始。 我们将在一个示例项目中实现 DI,然后学习单元测试的好处。 最后,我们将总结您作为 ASP 可用的各种 DI 选项.NET Core web 应用开发人员。

了解 IoC

IoC 是一种众所周知的模式,用于控制如何在软件组件中创建依赖对象。 使用 DI 允许我们在软件应用中实现 IoC。 让我们看一个实际的场景来说明这意味着什么。

如果您已经熟悉 DI 和 IoC,可以跳过这一节,直接进入下一节的代码。 如果您不熟悉这个主题,或者没有机会在实际项目中使用它,本节旨在帮助您更熟悉它。

DI 的利弊

对于 DI 是否适合自己,有经验的开发人员和初学者都有自己的看法。 因此,对于在项目中使用 DI 和 IoC,可能存在许多赞成和反对的意见。

以下是 DI 是一个好的选择的一些原因:

  • 有助于遵循依赖倒转原则(DIP)
  • 允许对象很容易地被替换
  • 促进使用策略设计模式(SDP)
  • 提高应用的可测试性
  • 支持软件组件的松散耦合

有关 DIP 和策略模式的更多信息,请看以下维基百科文章:https://en.wikipedia.org/wiki/Dependency_inversion_principle

https://en.wikipedia.org/wiki/Strategy_pattern

另一方面,由于以下原因,你可能不同意:

  • DI 为一些开发人员引入了一条学习曲线
  • 残障保险可能需要对现有项目进行重大改革
  • 项目时间表可能不允许 DI

让我们先看看他的缺点。 如果你不习惯,每一项技术都有一个学习曲线。 一旦你认识到这些好处,额外的努力就会变得值得。 如果您的项目需要进行大量的工作来将依赖注入改造到其中,那么您可能会很高兴保持体系结构不变,并在特定的项目中避免依赖注入。 因为 ASP.NET Core 是全新的,你可以从头开始,而且它也使得从依赖注入开始变得更容易。

至于优点的列表,其好处可能不会立即清楚,所以条款将在本章的其余部分进行更详细的解释。

扎实的原则和四人帮模式

您可能不熟悉SOLID原则和所谓的四人组(GoF)设计模式。 要实现依赖注入的好处,对这些模式和原则有一定的了解是很有帮助的。

SOLID 原则指的是组成 SOLID 这个词的下列原则。 这个缩略词可以帮助你记住以下列表:

  • (S)单一责任原则:每个类只负责一个主要功能。 这鼓励更好的类命名,并且不鼓励开发人员制作超出其需要的类。
  • (O)pen-Closed Principle:对象在扩展时是打开的,在修改时是关闭的。 这鼓励在不破坏现有代码的情况下为添加的功能创建子类。
  • (L)iskov 替换原则:对象应该被适当的对象替换,例如,共享相同父类或公共接口的其他对象。 这支持相关对象的松散耦合。
  • (I)接口隔离原则:与其过度使用一个通用接口,不如拥有更多适合特定用途的接口。 这鼓励您保持每个接口的轻量级。
  • (D)依赖倒置原理:对象应解耦或松散耦合。 这迫使类依赖于另一个对象的抽象定义,而不是具体的实现。

注意事项

关于 SOLID 原则的更多信息,可以在网上找到大量的参考材料、博客文章和文章。 下面的维基百科页面可能是一个很好的开始,其中包括对每个原则更详细的链接,在:https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)

GoF 设计模式参考了经典著作设计模式:可重用面向对象软件的元素中描述的模式,该书由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 编写。 他们经常被称为“四人帮”。

总共有 23 种设计模式,所以涵盖所有的设计模式远远超出了本章的范围,但值得一提的是策略模式,它与依赖注入的好处相关。 该模式定义了一种设计,它规定了可互换的对象,可以在遵循前面提到的 SOLID 原则的同时遵循该对象。

松耦合

为了演示松耦合,我们可以从一个具有负责复杂操作的操作方法的控制器开始。 理想情况下,我们的控制器方法应该尽可能精简。 如果我们使用 new 关键字在控制器方法中创建复杂对象,这些方法将负责创建这些对象。 换句话说,它们将与这些物体紧密结合。

下面是一个代码片段,展示了一个使用复杂对象的控制器方法:

public class MyController: Controller 
{ 
        public MyComplexObject ComplexObject { get;  set; }  

        public MyController() 
        { 
                ComplexObject = new MyComplexObject(); 
        } 

        public IActionResult MyActionMethod() 
        { 
                ComplexObject.GetStuffDone(); 
        } 
} 

在这里,我们可以看到对象必须用构造函数中的new关键字进行初始化。 这就创建了一个紧密耦合的依赖关系,如果我们需要换出一个不同的对象,就需要更新控制器代码。 为了避免这种依赖,我们可以传入一个已经在其他地方初始化的对象。 这将允许我们调用该对象上的方法,而不必担心创建它的实例并在之后处理它们。 下面是一个代码示例:

public class MyController: Controller 
{ 
        public MyComplexObject ComplexObject { get;  set; }  

        public MyController(MyComplexObject complexObject) 
        { 
                ComplexObject = complexObject; 
        } 

        public IActionResult MyActionMethod() 
        { 
                ComplexObject.GetStuffDone(); 
        } 
} 

在前面的代码中,对象已经通过构造函数传入,因此我们可以在控制器类的其他地方调用它的方法。 这是一个好的开始,但是我们可以通过将方法参数从一个类改为一个抽象接口来进一步改进:

public class MyController: Controller 
{ 
        public IMyComplexObject ComplexObject { get;  set; }  

        public MyController(IMyComplexObject complexObject) 
        { 
                ComplexObject = complexObject; 
        } 

        public IActionResult MyActionMethod() 
        { 
                ComplexObject.GetStuffDone(); 
        } 
} 

现在,构造函数可以接受实现预期接口的任何具体类。 但是这个对象在传入之前是如何初始化的呢? 这就是 IoC 容器的作用所在。 首先,我们来看一个由 ASP 提供的最简单的内置 DI 容器的例子。 净的核心。

在 ASP 中实现 DI.NET Core

在我们继续之前,您可能会惊讶地发现,在前面章节中我们已经在示例项目中使用了 DI。 看看你的 webproject文件夹中的Startup.cs文件。 你应该在ConfigureServices()方法的末尾看到以下代码:

services.AddTransient<IEmailSender, AuthMessageSender>(); 
services.AddTransient<ISmsSender, AuthMessageSender>(); 

这段代码添加了AuthMessageSender服务类的两个实例,它同时实现了IEmailSenderISmsSender接口。 这个类及其接口的代码可以在项目的Services子文件夹中找到,如下截图所示:

Implementing DI in ASP.NET Core

AddTransient的调用确保正确地创建和销毁对象,这样您就不必担心实例化和处理。 有多种方法可以表明您想要什么样的生命周期。 生命周期管理是在应用中使用 DI 不可或缺的一部分。

使用我们的患者记录项目第 6 章使用实体框架在代码中与您的数据库交互,让我们添加一个名为ApptService的新服务来帮助我们为虚构的医院预约:

  1. 在解决方案资源管理器中右键单击Services文件夹。
  2. 从弹出菜单中选择Add|Class
  3. 命名您的类ApptService.cs:

Implementing DI in ASP.NET Core

接下来,添加一个方法来模拟约会的调度。 为了简单起见,下面的示例代码返回 true,表示约会计划成功:

public class ApptService 
{ 
    public bool ScheduleAppt() 
    { 
        bool isSuccess = false; 
        // scheduling code goes here 
        isSuccess = true; 
        return isSuccess; 
    } 
} 

接下来,从这个服务类中提取一个接口。 你可以创建另一个类文件并命名为IApptService,但让 Visual Studio 2015 做额外的工作会更容易:

  1. ApptService.cs中,右键单击类的名称。
  2. 选择Quick Actions… 弹出菜单中的
  3. 在下一个弹出菜单中选择提取界面
  4. 在出现的对话框中,保留默认值并单击OK:

Implementing DI in ASP.NET Core

如果 Visual Studio 没有自动将新界面添加到您的Services文件夹中,您可能必须在解决方案资源管理器中的项目根文件夹中找到它。 如果IApptService.cs文件不在Services文件夹中,你应该手动拖动它到Services文件夹中:

Implementing DI in ASP.NET Core

您现在应该在您的Services文件夹中有一个新的接口和服务类。 现在,我们已经准备好实例化服务类并使用它。 在设置对象的 DI 时,我们需要为对象选择一个生命周期。 我们将通过在控制器类中注入它来利用它。

生命周期管理

为了在 ASP 中管理依赖的生命周期.NET,这里有一个你可以选择的生命周期列表:

  • Transient:每次需要该对象时,都会创建一个新的实例
  • 作用域:将为每个 web 请求创建一个新实例
  • Singleton:一个新实例只在应用启动时创建一次
  • 实例(Singleton 的特殊情况):使用AddSingleton()创建自己的实例

在每种情况下,对象只在不再需要之后才会被处理。 要使用一个或多个生存期设置,通常需要将代码添加到Startup.cs文件的ConfigureServices()方法中。 这将在本章后面详细解释。

构造函数注入和动作注入

当您通过构造函数注入一个依赖项时,这个方法被称为构造函数注入。 如果以动作方式注射,则称为动作注射。 您可以在应用中使用这两种方法。

首先,让我们添加一个新的控制器来管理医院的活动。 在Controllers子文件夹中创建一个新的HospitalController类:

  1. 在解决方案资源管理器中右键单击Controllers子文件夹。
  2. 在弹出菜单中,单击添加|新建项目
  3. 添加一个新的,命名为HospitalController.cs

下面的截图显示了创建HospitalController.cs的过程:

Constructor injection versus action injection

要使用构造函数注入,让我们添加一个对控制器构造函数的引用。 对于此选项,将以下代码添加到HospitalController类的主体:

public IApptService ApptService { get; set; } 
public HospitalController(IApptService apptService) 
{ 
    ApptService = apptService; 
} 

为了使用前面提到的服务类,请确保在类的顶部添加了正确的 using 语句:

using PatientRecords.Services; 

要使用动作注入,注入是在控制器的动作方法的参数部分执行的:

public IActionResult ActionMethod([FromServices] IApptService apptService) 
{ 
    ViewData["Message"] = "Scheduled: " + apptService.ScheduleAppt(); 

    return View(); 
} 

如果你碰巧读过较老的博客文章或过时的文档,了解一下早期的预发布版本的 ASP.NET Core 允许setter 注入在属性级别注入一个依赖项。

同样,确保在类定义上面包含正确的 using 语句:

using Microsoft.AspNetCore.Mvc; 
using PatientRecords.Services; 

第一个 using 语句确保我们将能够使用[FromServices]属性(以及其他属性)。 第二个 using 语句确保我们可以使用IApptService接口类。

最后,让我们添加一个控制器操作方法来调用我们的服务类。 在我们的HospitalController类的构造函数下面添加以下代码:

public string ProcessAppointment() 
{ 
    bool isSuccess = ApptService.ScheduleAppt(); 
    if (isSuccess) 
        return "Success!"; 
    else 
        return "Failed..."; 
} 

为简单起见,前面的代码返回一个字符串值,而不是实际的视图。 但是,如果我们现在运行应用并调用新的控制器方法,我们将得到一条错误消息。 错误信息如下所示:

An unhandled exception occurred while processing the request.
InvalidOperationException: Unable to resolve service for type 'PatientRecords.Services.IApptService' while attempting to activate 'PatientRecords.Controllers.HospitalController'.

出现这个错误是因为我们仍然没有告诉 DI 引擎如何创建我们的服务对象。 要解决这个问题,我们需要更新我们的Startup.cs文件,并使用ConfigureServices()方法底部的以下代码行,如本项目的示例代码所示:

services.AddTransient<IApptService, ApptService>(); 

您可以将前面一行添加到示例应用中对services.AddTransient的现有调用下面。 确保为IApptService添加了一个接口文件,该文件将由ApptService类实现。 您的应用现在已经准备好进行一些快速测试了。

验证预期行为

运行应用,然后通过在 web 浏览器中输入控制器名称和方法名称来手动调用控制器方法。

http://localhost:12345/Hospital/ProcessAppointment 

您的端口号可能有所不同,但 URL 的其余部分应该与前面的 URL 类似。 您现在应该看到一个成功! 消息,如下截图所示。 您可能还记得,我们的服务方法被硬编码为总是返回一个成功的结果。 如果你想测试一个不成功的结果,你可以编辑你的服务类中的方法来返回 false 而不是 true:

Verifying the expected behavior

恭喜你! 现在,您已经成功地在 ASP 中设置了 DI。 带有内置 IoC 特性的 NET Core。

为了测试注入的服务对象的剩余生命周期选项,我们可以在Startup.cs类中的ConfigureServices()方法中替换对AddTransient()的调用。 尝试使用以下选项将一行代码替换为另一行代码。 而不是使用以下多个选项:

// Option 1: Transient 
services.AddTransient<IApptService, ApptService>(); 
// Option 2: Scoped 
//services.AddScoped<IApptService, ApptService>(); 
// Option 3: Singleton 
//services.AddSingleton<IApptService, ApptService>(); 
// Option 4: Instance (Singleton, with explicit instance) 
//services.AddSingleton<IApptService>(new ApptService()); 

注意,每个选项显示非常相似的语法,并且它们都使用了IApptServiceApptService。 然而,对AddSingleton()的最后一次调用与其他的不同。 使用此选项,我们将负责自己创建实例,因此在本例中需要使用new关键字。 根据应用的需求,您可以使用上述选项之一来设置服务对象的生命周期。

ASP 中的 DI 选项.NET Core

当涉及到 DI 时,您有几个选择。 当涉及到 ASP 中的 DI 时.NET Core,您已经知道可以从内置 IoC 容器中进行选择。

但是你还有其他的选择吗?你应该如何决定什么时候进行选择呢?

内置 IoC

如果你刚刚开始使用 ASP.NET Core,我强烈建议使用最简单的 DI 选择。 使用现成的东西。 如果您是 DI 和 IoC 容器的新手,那么您肯定应该坚持使用默认的 IoC 容器。

如果你已经非常熟悉其他的选择,你可以从你知道的开始。 例如,如果您在过去的项目中广泛使用了Autofac作为 IoC 容器,那么对于新的 ASP,您可能希望坚持使用 Autofac。 网的核心项目。

Autofac

Autofac 已经被许多 ASP 使用。 asp.net 开发人员,并可用于 asp.net.NET Core 早于其正式发布。 在本节中,我们将看看如何在项目中使用 Autofac。

创建一个新的标准 web 项目,并在应用的project.json文件中依赖项部分添加以下对 Autofac 的引用:

"Autofac": "4.0.0-rc3-316", 
"Autofac.Extensions.DependencyInjection": "4.0.0-rc3-309" 

对主 Autofac 包及其特定于 di 的包的两次引用将使您开始学习。 与配置文件中的其他引用一样,您的版本号可能有所不同。

接下来,更新Startup.cs文件中的ConfigureServices()方法,根据需要初始化 Autofac。 您可能已经注意到,该方法在新创建的 web 项目中返回 void。 为了使用 Autofac,您应该将返回类型更改为IServiceProvider

更新你的ConfigureServices()方法以返回IServiceProvider并添加以下代码块:

public IServiceProvider ConfigureServices(IServiceCollection services) 
{ 
    // other code in method 

    // Prepare Autofac 
    var containerBuilder = new ContainerBuilder(); 
    containerBuilder.RegisterModule<DefaultModule>(); 
    containerBuilder.Populate(services); 
    var container = containerBuilder.Build(); 
    return container.Resolve<IServiceProvider>(); 
} 

为了让前面的代码正常工作,请确保在你的Startup.cs类顶部添加了正确的 using 语句:

using Autofac; 
using Autofac.Extensions.DependencyInjection; 

第一个 using 语句允许您在代码中使用 Autofac,正如您所期望的那样。 第二个 using 语句引入了额外的扩展方法,如Populate方法。

那么所有这些代码的作用是什么呢? Autofac 在五行代码中设置:

  1. 首先,创建一个新的ContainerBuilder对象。 这是 Autofac 的 DI 容器,其中包含注册服务模块的方法。
  2. 接下来,我们将注册一个模块,该模块将包含需要创建的服务对象的指令。 您可能注意到正在注册一个名为DefaultModule的未定义模块,但是我们还没有创建这个模块。 我们很快就会处理的。
  3. 通过调用ContainerBuilderPopulate()方法,它传入服务对象,该对象已经在方法的前面设置好了。
  4. 在返回之前,对ContainerBuilderBuild()方法的调用将使用刚刚注册的组件创建一个新容器。
  5. 最后,我们可以返回服务提供者来开始工作。

现在,让我们来创建前面的代码试图注册的模块:

  1. 在解决方案资源管理器中右键单击 web 项目。
  2. 在弹出菜单中点击添加|新建文件夹
  3. 为方便起见,将此文件夹命名为DependencyInjection
  4. 右键单击刚刚创建的新文件夹。
  5. 点击添加|创建一个新类。
  6. 命名你的班级DefaultModule.cs

下面的截图显示了创建DefaultModule.cs的过程:

Autofac

用以下代码替换这个新类的内容:

public class DefaultModule: Module 
{ 
    protected override void Load(ContainerBuilder builder) 
    { 
        builder.RegisterType<SomeClass>().As<ISomeInterface>(); 
    } 
} 

注意,我们是从Module 导出DefaultModule并覆盖其Load()方法。 为了让这段代码正常工作,我们必须在类的顶部添加以下 using 语句:

using Autofac; 
using Autofac.Extensions.DependencyInjection; 

在对RegisterType()的调用中,有一个占位符类SomeClass和一个占位符接口ISomeInterface。 您应该用一个类和一个接口替换它们,您希望 Autofac 解析这些类和接口以便在代码中使用。 实际上,您可以使用本章前面使用的例子中的IApptServiceApptService。 只需确保包含一个 using 语句,其中包含服务类的名称空间。

其他选择

如果您想探索其他替代方案,请查看以下可用于。net 的 IoC 容器。 确保您阅读了每个提供者的文档,以确保他们的包适合于 ASP 的最新版本.NET Core 和 MVC:

你也可以在你的project.json文件中使用智能感知来通过 NuGet 确定最新的稳定/Beta 版本。

编写单元测试

除了 DI 之外,编写单元测试来构建健壮的软件也很重要。 通过自动化测试,您可以最小化代码中出现 bug 的机会,并在添加新特性时产生信心。

关于单元测试已经写了很多文章和一些书籍,所以本节绝不是关于单元测试的全面参考。 相反,它的目的是向您介绍代码中自动化测试的可能性。

设置测试项目

ASP.NET 开发人员多年来一直在使用各种单元测试框架。 测试框架列表包括nUnitxUnit.net,以及微软自己的MSTest。 所有这些产品都具有相同的基本功能和一些高级功能。 为了扩展功能,开发人员可以使用模仿框架,如Moq(发音为 Mock- you 或 Mock)。

自从引入 ASP。 通过各种 Beta 版本,xUnit.net 已经可以使用了。 虽然 MSTest 在早期的 beta 测试中并不可用,但 ASP.NET 团队在 ASP 的 RC2 版本中宣布了 MSTest。 净的核心。 在本章中,我们将介绍 xUnit.net 作为我们的单元测试示例。

提示

如果您有兴趣比较 xUnit.net 和它的其他选项的语法,可以查看 GitHub 上的https://xunit.github.io/docs/comparisons.html比较图表。

要为项目设置 xUnit.net,我们应该在解决方案中创建一个新项目,仅用于测试代码。 尽管这在技术上不是必需的,但是将测试代码从应用代码中分离出来是一个很好的实践。 测试项目将有一个对应用项目的参考,但是应用项目不应该有测试项目的任何知识。

你可以为你的测试项目创建一个新文件夹:

  1. 在解决方案资源管理器中右键单击您的解决方案。
  2. 在弹出菜单中单击添加|新建解决方案文件夹
  3. 将新文件夹命名为test,如下截图所示:

Setting up a test project

要创建一个测试项目,请遵循以下步骤:

  1. 右键单击您的test文件夹。
  2. 在弹出菜单中单击添加|新建项目
  3. 选择类库(.NET Core)为项目类型。
  4. 输入PatientRecords.Tests作为项目名称,如下截图所示:

Setting up a test project

请注意,测试项目的名称不必包含应用项目的名称。 然而,在命名测试项目时,通常以应用项目名开始,然后以.Test.Tests结束。

要在我们的测试项目中包含适当的引用,请在测试项目中编辑您的project.json文件。 确保您已经从测试项目而不是应用项目中打开了这个配置文件,以便进行以下更改。

project.json文件的"dependencies"部分,添加对xunitxunit.runner.dnx和你的 web 项目的引用:

"dependencies": { 
  "xunit": "2.2.0-beta2-build3300", 
  "dotnet-test-xunit": "2.2.0-preview2-build1029", 
  "PatientRecords": "1.0.0-*" 
}, 

第一个xunit引用允许您在测试项目中使用 xunit,而运行器引用允许您从命令行或在 Visual Studio 中运行单元测试。 最后,对PatientRecords的引用是对您的 web 应用项目的引用,并且可能根据您如何命名项目而有所不同。 同样,你的版本号可能会有所不同,所以你应该在输入时使用智能感知弹出窗口来决定哪个 Beta/Stable 版本可能适合你的项目。

就在项目版本号下面,将xunit标识为您的测试运行器:

"testRunner": "xunit", 

新项目中应该已经有一个占位符类。 要创建第一个测试类,可以重命名占位符类名及其文件名。 您还可以删除占位符类并创建一个新类。

要创建一个新的测试类,请遵循以下步骤:

  1. 在解决方案资源管理器中右键单击您的测试项目。
  2. 在弹出的菜单中点击添加|Class
  3. 命名测试类HospitalServiceTests.cs,如下截图所示:

Setting up a test project

为了编写第一个单元测试,您需要添加一个 using 语句来使用 xUnit.net。 将下面的 using 语句添加到测试类的顶部:

using Xunit; 

接下来,在测试类中添加以下占位符方法:

[Fact] 
public void VerifySuccess() 
{ 
    bool isSuccess = false; 
    isSuccess = true; 
    Assert.True(isSuccess); 
} 

在前面的代码中,[Fact]属性将方法标识为不带任何参数的测试方法。 public关键字确保测试可以从程序集外部调用,由测试运行程序和持续集成系统调用。 对Assert.True()的调用将验证isSuccess变量是否被设置为 true。 为了简单起见,我们对其值进行了硬编码。 现在可以运行这个示例测试了。

运行单元测试

我们可以通过 Visual Studio 2015 中的 with 或命令行来运行单元测试。 要从 Visual Studio 中运行测试,请确保测试资源管理器面板打开且可见。 要从命令行运行测试,必须打开命令提示窗口并在项目的文件夹中运行命令。

要使用 Visual Studio 2015 中的测试资源管理器,请遵循以下步骤:

  1. 在顶部菜单中,单击Test|Windows|Test Explorer
  2. 验证 Test Explorer 面板在 Visual Studio 中是否可见。
  3. 在顶部菜单中,单击Build|Build Solution
  4. 验证单元测试是否出现在测试资源管理器面板中。
  5. 单击 Test Explorer 面板中的Run All
  6. 验证您的测试是否成功通过,如下面的截图所示:

Running unit tests

要使用命令提示符运行测试,请执行以下步骤:

  1. 打开一个命令提示窗口。
  2. 将当前目录更改为指向测试项目文件夹。
  3. 输入以下命令运行示例测试:
>dotnet test

此命令将触发测试系统发现单元测试,并运行所有可用的测试。 运行测试之后,您应该看到一个测试执行摘要,其中标识了测试的数量、成功的数量和失败的数量,如下面的截图所示:

Running unit tests

如果您想在单元测试方法中包含参数,您可以同时使用[Theory][InlineData]属性来替换[Fact]属性。 在属性中传递的参数应该与测试方法签名中的参数匹配。

要包含参数,请在测试类中添加以下方法:

[Theory] 
[InlineData(7, 5)] 
public void VerifySuccessWithParams(int n1, int n2) 
{ 
    Assert.True(n1 > n2); 
} 

在这里,[Theory]属性定义了接受一些测试数据作为参数的测试方法。 属性接受一个或多个匹配测试方法参数的参数。 Assert语句可以采用多种形式,因此您可以通过在单词Assert后键入一个句点,并观察可以调用该语句的可能方法列表来发现它的各种特性。 在本例中,我们正在验证数字n1大于n2

最后,让我们看看如何调用应用的方法来测试应用代码。 在测试类中添加以下 using 语句:

using PatientRecords.Services; 

接下来,将以下方法添加到测试类中:

[Fact] 
public void VerifyApptService() 
{ 
    ApptService apptService = new ApptService(); 
    var isSuccess = apptService.ScheduleAppt(); 
    Assert.True(isSuccess); 
} 

这个测试方法负责创建ApptService类的一个新实例,然后调用ScheduleAppt()方法。 根据应用代码返回的内容,测试要么通过要么失败。

无论您的测试方法是[Fact]还是[Theory],您可能会出于某些原因暂时跳过测试。 这可能是因为测试失败了,您需要一些额外的时间来排除故障。 为了跳过一个测试方法,你可以使用Skip参数,并提供一个原因:

[Fact(Skip = "skip this for now")] 
[Theory(Skip = "skip this too")] 

下次运行所有测试时,使用此参数标记的任何测试方法都将在该测试运行中跳过。 请记住,这种做法不应该用于长时间隐藏您的测试方法。 如果某些测试在任何一段时间内都被跳过,您应该找到一种方法来修复这些测试(如果必要的话)。 如果测试已经过时或不相关,您应该考虑删除它们,而不是每次都跳过它们。

超越基础

在结束本节之前,重要的是要提到,自动化测试不仅仅是本章所涵盖的基本单元测试。 在本节的前面,简要提到了一些替代框架,比如 nUnit 和 MSTest。 如果您有时间学习更多内容,那么在您决定哪个测试框架适合您的项目之前,请查看其他可用的测试框架。

如果您还选择使用 Moq 模拟框架,您可以将它与现有的测试框架(如 xUnit.net)一起使用。 在前面的测试示例中,您可以用抽象接口替换具体类,这将允许您用模拟对象替换接口。 Mocking 框架超出了本书的范围,但是你可以在 GitHub 的产品页面https://github.com/Moq了解更多关于 Moq 的内容。

至少,您应该了解可以在测试项目中创建的模拟对象的种类:mockfakestub:

  • 模拟是用预期的结果进行预编程的,并不实际连接到真实的数据库,但可以用于行为验证
  • pseudo 是工作示例,但还不能用于生产(例如,内存中的数据库)
  • 存根是提供固定答案的占位符,对于特定的有限场景非常有用

如果您对这些术语完全清楚,您应该参考 Martin Fowler 在http://martinfowler.com/articles/mocksArentStubs.html上关于 mock、Fakes 和 stub 的经典文章。

总结

在本章中,我们讨论了 IoC、DI 和单元测试。 我们讨论了在 ASP 中实现 DI 的多种方法。 然后学习了在 asp.net 中进行单元测试的基础知识。 净的核心。

如果您想了解更多关于 DI 和 IoC 容器的信息,可以从软件工程师和公众演说家 Martin Fowler 在http://www.martinfowler.com/articles/injection.html上发表的一篇关于这个主题的经典文章开始。

在下一章中,我们将讨论 ASP 中的身份验证、授权和安全性.NET Core web 应用。

八、身份验证、授权和安全

在构建安全的 web 应用时,身份验证、授权和安全性都是需要注意的重要主题。 有很多内容需要讨论,所以我们将关注哪些内容对 ASP 来说是重要的和相关的.NET Core web 应用。

但首先,让我们来定义这三个术语:

  • 认证:你可以使用认证来限制谁可以使用你的应用,而不是允许任何网站访问者访问你的 web 应用。 这对于任何需要在允许与每个用户进行交互之前识别每个用户的应用都很有用。
  • 授权:一旦进入您的应用,您可以使用授权来限制应用的特定部分。 这对于允许某些用户执行其他用户无法访问的一些任务很有用,例如管理任务、数据编辑等等。
  • Security:在这里,Security 是指可能影响 web 应用的安全漏洞。 这包括反请求伪造跨站点脚本,以及SQL 注入

在 ASP 中启用认证功能。 净

你可能会很高兴听到 ASP.NET Core 在其 web 模板中包含了内置的身份验证方法。 事实上,创建前面章节中提到的示例项目将生成一个包含身份验证的 web 应用。 如果您开始一个全新的项目,本节还将帮助您确定需要做什么来包含身份验证。

高层概述

下面是对如何在新的 ASP 中启用身份验证的高级概述.NET Core 应用使用 web 模板:

  1. 用一个网页模板创建一个新项目。
  2. 选择身份验证方法。
  3. 验证包和引用。
  4. 验证Startup.cs类中的代码。
  5. 根据需要添加额外的标识选项。
  6. 根据需要添加外部提供程序。

如果你想从一个空项目开始,你可以执行以下步骤:

  1. 创建一个新的空 web 项目。
  2. 不选择身份验证方法。
  3. 添加用于身份验证的包和引用。
  4. Startup.cs类中添加代码。
  5. 根据需要添加额外的标识选项。
  6. 根据需要添加外部提供程序。

出于显而易见的原因,从一个网页模板开始要容易得多。 注意前面两个列表中的不同之处。 如果你决定从一个空的 web 模板开始,你将不得不做更多的工作来启动和运行认证。

当您选择一个项目类型时,Visual Studio 会在右侧面板中显示一个对话框,其中包含一个标记为Change Authentication的按钮,如下图所示:

High-level overview

如果单击Change Authentication按钮,Visual Studio 将显示另一个对话框,其中显示以下选项:

  • 无身份验证:这是不言自明的。 您的项目不会自动包含任何身份验证,因此应该适用于不需要任何身份验证的 web 应用。 如果您选择稍后添加它,则必须执行额外的步骤来手动添加它。
  • 个人用户帐户:这对于初学者来说非常有用。 所有用户数据都存储在应用数据库中,可以使用 ASP 进行访问.NET Core 框架。 注意:ASP。 asp.net 身份已经从Windows 身份基础转移到 ASP。 净的核心。
  • 工作和学校帐户:该选项包括使用Windows Server Active DirectoryAzure Active Directory的组织帐户,如企业或教育机构帐户。
  • Windows 身份验证:此选项非常适合将在内部网环境中使用的应用,其中 Windows 计算机上的凭据将用于验证 web 应用上的用户。

在本章中,我们将介绍使用个人用户帐户进行身份验证的过程。

认证配置

通过使用前面章节中的医院记录项目,您应该已经有了一个功能良好的 web 项目,并且默认选择了适当的验证方法。 请注意以下验证步骤,以确定在将来的 web 项目中使用空模板时需要什么。

在您的project.json文件中,验证您是否引用了Microsoft.AspNetCore.Identity.EntityFrameworkCore包,如下截图所示:

Authentication configuration

这个 ef 特定的Identity包有两个其他依赖项,您可以在解决方案资源管理器面板中的引用列表中看到这些依赖项。 依赖关系包括以下这些额外的包:

  • Microsoft.AspNetCore.Identity
  • Microsoft.EntityFrameworkCore.Relational

Relational包将Core包作为依赖项拉入EntityFramework,而 ASP.NET 身份包包括我们在项目中需要使用的身份验证。 实体框架将用于与数据库交互,其中我们的用户凭证将存储在一个名为AspNetUsers的表中。

要完成配置,Startup.cs类需要适当的代码来添加和使用身份验证。 正如您可能已经猜到的,这段代码将分别进入ConfigureServices()Configure()方法。

在您的Startup类的ConfigureServices()方法中,验证下面的代码是否能够使用 ASP 的 Identity 特性.NET Core:

services.AddIdentity<ApplicationUser, IdentityRole>() 
    .AddEntityFrameworkStores<ApplicationDbContext>() 
    .AddDefaultTokenProviders(); 

在您的Startup类的Configure()方法中,验证下面的代码是否能够在 ASP 中使用 Identity 特性.NET Core:

app.UseIdentity(); 

有几个配置标志允许我们为使用 Identity 特性定制额外的选项。 这些限制包括但不限于密码限制。 下面的代码展示了这是如何工作的。 例如,如果您想在注册时要求用户的密码中使用数字,您应该将RequireDigit设置为true:

services.AddIdentity<ApplicationUser, IdentityRole>( 
    options => 
    { 
        options.Password.RequireDigit = true; 
        options.Password.RequiredLength = 8; 
        options.Password.RequireLowercase = false; 
        options.Password.RequireNonAlphanumeric= true; 
        options.Password.RequireUppercase = false; 
    }) 
    .AddEntityFrameworkStores<ApplicationDbContext>() 
    .AddDefaultTokenProviders(); 

如果您想研究其他选项,可以在选项列表中的单词选项后输入句号。 这将允许你使用智能感知来发现其他对你有用的东西。 与密码相关的选项在您可能已经在 MVC 模型的密码字段中分配的任何属性之上提供了这些限制。

运行您的应用并单击顶部面板中的Register链接。 注册后,您将作为注册用户自动重定向到应用。 您可以注销,然后使用创建的凭据重新登录。

外部服务提供商

许多用户可能已经拥有希望重用的外部服务提供的帐户。 像 Twitter 和 Facebook 这样的社交媒体巨头提供身份验证服务,你可以很容易地将其集成到你的 ASP 中.NET Core 应用。

困难的部分实际上是在供应商的网站上设置一个应用,一旦你设置了它,就会变得更容易。

以下是建立外部服务提供者所需步骤的概述:

  1. 访问外部提供商的网站,在他们的系统上设置一个应用。
  2. 获取必要的 ID、密钥、秘密或令牌值。
  3. 在您的开发环境中安装Secret Manager工具。
  4. 使用前面获得的值分配秘密值。
  5. 更新您的代码,以便在开发期间使用用户秘密存储。
  6. 更新您的代码以使用第三方提供商,例如 Facebook。
  7. 通过 Facebook 登录到应用来验证身份验证。

在 Azure(云)环境中,您可以在应用设置下为您的 web 应用配置秘密值。我们将在第 9 章部署您的应用中介绍 Azure 部署。

要在外部提供商的系统中设置应用,请访问他们的特定网站:

每个供应商的说明和用户界面可能会随着时间的推移而改变,所以您应该始终访问每个特定的网站以获得最新的说明。 整个过程通常包括创建一个命名的应用,然后获取一组唯一标识你的应用的值。

让我们通过将 Facebook 认证整合到我们的应用中来探索一个特殊的过程:

  1. 请访问https://developers.facebook.com/
  2. 注册为一个新的开发人员。
  3. 创建一个新的应用。
  4. 如果被问到,选择网站平台。
  5. 创建一个带有您选择的显示名称的App Id
  6. 应用创建完成后,进入设置界面。
  7. 记录您的App IdApp Secret值。
  8. 点击添加平台按钮。
  9. 从选择列表中选择网站平台。
  10. 添加你网站的 URL。
  11. Advanced部分,添加您的 URL 作为Redirect URI

在开发过程中测试应用时,可以使用带有适当端口号的本地主机 URL。 要测试一个已部署的网站在一个域名服务器上,你应该使用完全限定域名。 请记住,前面的说明可能会发生变化,所以你应该参考 Facebook 开发者网站上的说明,以防发生变化。

验证如下:

  • 项目的userSecretsId值在您的project.json文件中定义
  • Microsoft.Extensions.Configuration.UserSecrets属于依赖项
  • SecretManager列在project.json文件的工具部分

使用管理权限打开命令提示符。 切换到您的项目目录,然后使用以下 DNU 命令测试 Secret Manager 工具:

>dotnet user-secrets -h

如果您刚刚将SecretManager添加到配置文件中,那么您可能必须在前面的命令之前运行dotnet restore。 验证完SecretManager后,使用以下两个额外命令来存储你从 Facebook 获得的应用 ID 和秘密值:

dotnet user-secrets set Authentication:Facebook:AppId <Value>
dotnet user-secrets set Authentication:Facebook:AppSecret <Value>

注意事项

注意,上面的占位符值必须用实际值替换,命令才能工作。 如果 Facebook 中的值发生了变化,您必须重新运行这两个命令,以确保最新的值存储在开发环境中。

验证你的Startup类中的构造函数中是否有以下一行代码:

builder.AddUserSecrets(); 

这行代码只能在IsDevelopment()返回true时运行,以确保此场景只存在于开发环境中。 为了验证您的环境是否为此正确配置,请确保托管的环境变量被设置为Development,如下面的截图所示。 你可以在你的网页项目的属性面板的Debug选项卡中检查。 你也可以在项目的Properties文件夹下的launchSettings.json文件中看到它:

External service providers

最后,更新Startup.cs中的Configure()方法以使用 Facebook 身份验证。 在调用UseIdentity()之后添加以下代码:

app.UseFacebookAuthentication(new FacebookOptions() 
{ 
    AppId = Configuration["Authentication:Facebook:AppId"], 
    AppSecret = Configuration["Authentication:Facebook:AppSecret"] 
}); 

下次运行应用并尝试登录时,应该会在其他服务列表中看到 Facebook 身份验证选项。 如果你想知道这个 Facebook 按钮是如何显示的,看看代码中Account控制器的Login.cshtml视图,如下截图所示:

External service providers

Login.cshtml视图中(在Views文件夹的Account子文件夹下),有一个由@{}分隔符包围的代码块,用于检查外部身份验证提供程序。 如果找到任何提供程序,将在foreach循环中为每个提供程序显示一个按钮。 该服务器端代码的简化版本如下:

var loginProviders = SignInManager.GetExternalAuthenticationSchemes().ToList(); 
if (loginProviders.Count == 0) 
{ 
    <p>None configured</p> 
} 
else 
{ 
    <form  
        asp-controller="Account"  
        asp-action="ExternalLogin"  
        asp-route-returnurl="@ViewData["ReturnUrl"]"  
        method="post" class="form-horizontal" role="form"> 

    @foreach (var provider in loginProviders) 
    { 
        <button  
            type="submit"  
            class="btn btn-default"  
            name="provider"  
            value="@provider.AuthenticationScheme"  
            > 
        @provider.AuthenticationScheme 
        </button> 
    } 
    </form> 
} 

登录后,您的身份验证应该由 Facebook 处理,您可能会被提示登录 Facebook。 如果您已经在当前窗口登录到 Facebook,您当前的会话将被使用。 在这两种情况下,您都应该被重定向回您的应用,并且您的用户名将是您可能用于登录 Facebook 的登录名(例如,您的电子邮件地址)。

默认情况下,你的 Facebook 帐户将是 Facebook 应用的管理员。当你登录到你的 web 应用时,你将被要求将你的 Facebook 帐户与 web 应用的用户系统中的一个新帐户相关联。 您可以通过在关联屏幕中输入新的电子邮件地址来注册一个新帐户。 如果你想在测试期间添加额外的用户,你必须首先进入 Facebook 开发者网站上的角色标签页,在那里你可以添加额外的管理员、开发人员和测试人员。

为应用特性使用授权

正如在本章的介绍中所解释的,授权可以用于将已经通过身份验证的用户排除在应用的特定部分之外。 在我们的医院记录应用中,我们可以限制某些功能,以便医生可以访问它们,而护士或患者则不能。

高层概述

下面是对如何在 ASP 中实现基本授权技术的高级概述.NET Core 应用:

  1. 在控制器代码中使用Authorization命名空间。
  2. 在控制器类级别授予授权。
  3. 在控制器操作方法级别授予授权。
  4. 在控制器类级别授予匿名访问。
  5. 在控制器操作方法级别授予匿名访问。

尽管第一步是使用授权所必需的,但其余建议不必按照任何特定的顺序执行。 实际上,您可以对一个控制器类或一个方法进行授权,或者两者都授权,或者根本不授权。 在所有情况下,如果您授予匿名访问,它将优先于任何其他授权。 例如,同时具有授权和匿名访问集的类将向任何用户授予匿名访问权。

实现授权的最简单方法是使用基于角色的授权基于声明的授权。 我们将在本节中介绍这两种技术。 有关更复杂的授权技术的更完整列表,请参阅以下网址的官方文档:

http://docs.asp.net/en/latest/security/authorization

基本授权

要配置授权,最快的方法是简单地将Authorize属性添加到特定的控制器类。 下面的代码展示了如何将其应用到HumanController:

[Authorize] 
public class HumanController : Controller 
{ 
} 

为了使用Authorize属性,请确保在类定义上面有正确的 using 语句:

using Microsoft.AspNetCore.Authorization; 

从 Visual Studio 运行应用,并单击顶部工具栏中的Humans链接来访问HumanController。 由于控制器在类级别上受到了限制,如果您尝试在不首先登录的情况下访问HumanController,那么您将被重定向到Login页面。

要尝试匿名访问,将[AllowAnonymous]属性添加到HumanController类的Index()操作方法:

[AllowAnonymous] 
public async Task<IActionResult> Index() 

再次运行应用,然后再次单击Humans链接。 即使控制器已经在类级别限制,Index()的方法会让你访问Index视图,而无需登录,但试图访问【编辑 T7】/【显示】删除将提示您登录的链接。 即使在操作方法上有[Authorize]属性和[AllowAnonymous]属性时,这种覆盖行为也会工作。

角色与主张

除了基本的身份验证之外,我们还可以实现基于角色的授权和基于声明的授权,以实现更精确的访问控制。 这些类型的授权依赖于以下 ASP。 网相关的表:

  • AspNetUsers:带有用户 ID、电子邮件、密码等的用户表
  • AspNetRoles:角色主列表
  • AspNetUserRoles:映射到特定角色的用户
  • AspNetUserClaims:映射到特定索赔的用户
  • AspNetRoleClaims:映射到特定索赔的角色(以及用户)

要用一些示例数据填充数据库,请执行以下步骤创建新用户、角色和声明。 为了测试而使用假的电子邮件地址是可以的。 要与数据库交互,可以使用 Visual Studio 中的SQL Server 对象资源管理器面板来扩展患者记录数据库的内容。

您可以右键单击任何表,选择View Data查看(并编辑)其数据:

  1. 从 Visual Studio 中运行应用。
  2. 在浏览器中,单击右上方菜单中的Register
  3. 在浏览器中注册几个用户(例如,医生、护士、病人)。
  4. 在数据库中,验证AspNetUsers表中的用户。
  5. AspNetRoles表中添加几个角色,并使用顺序的 ID 值。
  6. AspNetUserRoles表中添加一些记录,将用户映射到角色。
  7. AspNetUserClaims表添加一些记录,将用户映射到索赔。
  8. 暂时将AspNetRoleClaims表空着。

如果您需要帮助将数据添加到AspNetRoles表中,以下是一些示例值:

| **Id** | **concurrent stamp** | **名称** | **NormalizedName** | | 1 | 零 | 管理员 | 管理员 | | 2 | 零 | 医生 | 医生 | | 3 | 零 | 护士 | 护士 |

如果您需要帮助将数据添加到AspNetUserRoles表中,以下是一些示例值:

| **用户名** | **角色** | | cb09fda1-1458-4da1-bee8-3e831d68ca8c | 3 | | e8104b13-72e6-497f-baac-0619548c1e70 | 2 |

不应该将前面的UserId值复制到AspNetUserRoles中。 相反,您应该检查自己的AspNetUsers表来复制分配给每个用户UserId字段的global Unique ID(GUID)值。 这些是由内置的注册工具自动生成的。 下面是AspNetUsers表的一个示例:

| **用户名** | **Email** | **…** | | cb09fda1-1458-4da1-bee8-3e831d68ca8c | RobotNurse1@fakedomain.com | | | e8104b13-72e6-497f-baac-0619548c1e70 | RobotDoctor1@fakedomain.com | |

最后,如果您需要帮助将数据添加到AspNetUserClaims表中,下面是一些示例值。 在该表中,ID 值是按顺序自动生成的:

| **Id** | | **ClaimValue** | **用户名** | | 1 | DoctorCred | 123456789 | e8104b13-72e6-497f-baac-0619548c1e70 | | 2 | NurseCred | 987654321 | cb09fda1-1458-4da1-bee8-3e831d68ca8c |

现在你的数据已经全部设置好了,用如下所示的属性装饰你的动作方法:

[Authorize(Roles = "Doctors,Nurses")] 
public async Task<IActionResult> Index() 

[Authorize(Roles = "Doctors")] 
public IActionResult Create 

上述代码确保以下情况:

  • 医生和护士都可以进入主要的Index视图
  • 只有医生能够创建一个新的条目

本文将介绍基于角色的授权的设置和使用。 要使用基于 claims 的 Authorization,您必须更新启动配置,并在控制器代码中使用其他属性和参数。

Startup.cs文件的ConfigureServices()方法中,在AddMvc()调用之后添加一个对AddAuthorization()的调用,如下所示:

services.AddAuthorization(options => 
{ 
    options.AddPolicy("DoctorsOnly",  
        policy => policy.RequireClaim("DoctorCred")); 

}); 

这将向您的应用添加一个名为DoctorsOnly的新保单,并要求索赔DoctorCred。 您可能还记得,DoctorCred就是我们在本节前面添加到AspNetUserClaims表中的ClaimType。 要使用这个声明,只需向Details()方法添加以下属性,如下所示:

[Authorize(Policy = "DoctorsOnly")] 
public async Task<IActionResult> Details(int? id) 

这确保了只有具有DoctorCred声明的用户才能访问人类患者列表的详细信息。 为了更进一步,重新定义索赔以包含特定的索赔编号。 这个值可以是一个或多个逗号分隔值的集合:

services.AddAuthorization(options => 
{ 
    options.AddPolicy("DoctorsOnly",  
        policy => policy.RequireClaim("DoctorCred", "123456789")); 

}); 

运行应用并以前面创建的各种用户身份登录。 验证您正在观察基于每个用户的授权特权所期望的行为。 如果没有适当的权限,您可能会看到一个Access Denied错误页面。

保护您的数据

ASP.NET Core 包含了一个新的数据保护系统,可以在 web 应用和控制台应用中使用。 你可能还记得 ASP 的模板列表.NET Core 包含一个控制台应用,在前面的章节中我们不需要使用它,如下截图所示。 然而,这种项目类型对于说明框架特性是一个很好的选择,例如在 ASP 中使用数据保护.NET Core:

Protecting your data

元素在以前的 ASP 版本中使用了<machinekey>。 而新的数据保护堆栈将取代它。 为了使事情更简单,新系统鼓励以最小的配置工作来使用它。 同时,还有一些可扩展性 api 允许根据需要进行更多定制。

ASP 中的数据保护.NET Core

当用户与 web 应用通信时,有许多方法来持久化和传输数据。 有些方法比其他方法更持久,而安全性和加密可能差异很大。 提供给经过身份验证的用户的令牌在以后使用时需要受到信任。 这就是数据保护的作用所在。

ASP.NET 团队在构建新的数据保护系统时确定了一些要求:真实性、机密性和隔离性。 通过确保我们可以担保受保护数据的完整性,同时保护数据不受不可信客户的影响,可以满足所有这些要求。 请注意,这并不能防止恶意应用滥用 API 来访问不应该访问的受保护数据。

实施数据保护

使用新的Data Protection API设置项目是相当简单的。 这是有意为之,并允许开发人员快速启动和运行。 请记住,受保护的数据不应该是您希望无限期存储的数据。 这在技术上是可行的,但不推荐使用。

要开始,使用以下步骤创建一个新的控制台应用:

  1. 在 Visual Studio 2015 中点击File|New|Project
  2. Installed模板列表中,选择Visual c#|. net Core|Console Application(控制台应用).NET Core)
  3. 输入DataProtectionApp作为项目名称,然后单击OK
  4. 打开project.json文件进行编辑。
  5. 将以下引用添加到依赖列表中:
      "Microsoft.AspNetCore.DataProtection":"1.0.0", 
      "Microsoft.Extensions.DependencyInjection": "1.0.0" 

当您输入版本号时,您很可能会使用可用的最稳定的版本。 在DataProtection名称空间中,您可以访问数据保护提供者及其保护和取消数据保护的方法。 命名空间将允许您使用 DI 轻松地设置数据保护。 你要用一根所谓的用途线把它们绑在一起。

编辑Program.cs文件的Main()方法,包含以下代码:

var serviceCollection = new ServiceCollection(); 
serviceCollection.AddDataProtection(); 
var services = serviceCollection.BuildServiceProvider(); 

var instance = ActivatorUtilities.CreateInstance<DataProtector>(services); 
instance.ProtectAndRelease(); 

要使用ServiceCollectionActivatorUtilities类,请确保将下面的 using 语句添加到Program类的顶部。

using Microsoft.Extensions.DependencyInjection;  

前面的代码创建了我们接下来将创建的DataProtector类的实例。 它将有一个公共的ProtectAndRelease()方法。 您可能已经猜到了,该方法将负责保护一些输入数据,然后将其作为未保护数据发布。 在实际场景中,您可能不会使用相同的方法取消对数据的保护。

要创建DataProtector类,请遵循以下步骤:

  1. 在“解决方案资源管理器”面板中右键单击项目。
  2. 从弹出的菜单中选择添加|Class
  3. 命名类DataProtector.cs,点击添加,如下图所示:

Implementing data protection

DataProtector类中添加以下实例变量和构造函数:

IDataProtector _protector;         
public DataProtector(IDataProtectionProvider provider) 
{ 
    _protector = provider.CreateProtector("Company.Project.v1"); 
} 

为了使用IDataProtectorIDataProtectionProvider接口,请确保在DataProtector类的顶部添加以下命名空间:

using Microsoft.AspNetCore.DataProtection; 

构造函数负责创建新的数据保护提供程序对象,该对象又用于创建新的保护程序。 Company.Project.v1目的字符串用于创建用于此示例的保护器。 这个字符串可以是您想要的任何内容,但建议您对您的应用使其独一无二。 在同一个应用的其他地方,创建一个具有相同目的字符串的保护程序将允许该保护程序取消对先前被第一个保护程序保护的数据的保护。

最后,添加以下方法来保护和取消对数据的保护。 方法可以采用字符串值或字节数组作为输入参数:

public void ProtectAndRelease() 
{ 
    Console.Write("Enter input: "); 
    string userToken = Console.ReadLine(); 

    string protectedToken = _protector.Protect(userToken); 
    Console.WriteLine($"Protected token: {protectedToken}"); 

    string unprotectedToken = _protector.Unprotect(protectedToken); 
    Console.WriteLine($"Unprotected result: {unprotectedToken}"); 

    Console.ReadKey(); 
} 

现在可以运行应用了。 从 Visual Studio 中运行应用并输入一些示例文本,例如文本字符串形式的用户标记。 您应该立即看到来自ProtectAndRelease()方法的输出,它显示了您输入的文本的受保护版本和未受保护版本:

Implementing data protection

您可能想要将这些数据保护代码添加到您的 ASP.NET Core 控制器在您的 web 项目。 相反,您应该考虑抽象服务类来处理应用的特定部分,以保持控制器的精简。

这一切是如何运作的

为了理解所有这些是如何工作的,让我们回到目的字符串。 它的用途可以是字符串、字符串列表,甚至是方法调用的菊花链。 你可以使用这里显示的任何一种格式:

CreateProtector("Company.Project.v1"); 
CreateProtector(new List<string> { "Comp", "Proj", "v1" }); 
CreateProtector("Comp.Proj").CreateProtector("v1"); 

只要两个提供程序对象被认为是等效的,一个提供程序就可以取消被另一个提供程序保护的数据的保护。 记住以下几点:

  • 如果目的字符串以相同的顺序包含相同的字符串,则认为它们是等效的
  • 如果提供程序对象是用等效的字符串值创建的,则认为它们是等效的
  • 如果保护者对象是由等效的提供者对象创建的,则认为它们是等效的
  • 在内部,系统在目的链中使用一个对应用唯一的标识符

以下是一些你应该注意的注意事项:

  • 如果在构建目的字符串时使用了任何用户输入,则向该字符串追加一些内容,以防止用户控制目的字符串的整体外观。
  • 你应该在它发生的地方抓住一个CryptographicException。 如果使用不同的数据保护程序来尝试取消保护操作,或者负载被篡改,则会抛出此类异常。
  • 这种数据保护方法不能保护恶意应用代码本身。 如果相同的目的字符串在不应该重用的地方被重用,这可能会导致在您不需要它们的地方产生等效的提供者对象。

这并不是您需要了解的关于 ASP 中数据保护的全部内容。 净的核心。 欲了解更多信息,请浏览以下网址获取官方文件:

https://docs.asp.net/en/latest/security/data-protection

实现 web 应用安全

在网络向世界普及后不久,网络应用开始到处涌现。 伴随着 web 应用而来的漏洞可能会被恶意用户利用。 幸运的是,安全专家和框架开发人员不断提供持续的建议和更好的安全措施。

多年来,一些常见的安全漏洞包括:

  • SQL 注入:SQL 通过 HTML 表单字段注入恶意 SQL 脚本,这些字段的值用于构建 SQL 的文本字符串。 通过使用LINQ对具有 ORM(如 EF Core)的实体,可以避免 SQL 注入的风险。 如果您发现自己正在使用参数化查询,请确保在查询参数中使用任何用户输入(通过 html 编码值)之前对它们进行消毒。
  • 敏感数据暴露:关于服务器、文件系统、数据库和操作系统的信息可能在生产环境中不必要地暴露出来,特别是在出现错误的情况下。 最好避免向最终用户透露过多的信息。 相反,如果发生错误,可以利用内置的日志功能将详细信息存储在其他地方,同时向用户显示数字错误代码和友好的消息。
  • 跨站脚本(XSS):恶意脚本代码通常通过QueryString参数注入到 HTML 页面的主体中。 幸运的是,有多种方法来抵御 XSS 攻击。

本节的其余部分将探讨一些技术,用于对抗 XSS、防止伪造和支持跨源请求。

跨站点脚本

为了防止来自恶意用户的跨站点脚本攻击,您可以将AntiXSS库与以前的 ASP.NET 版本一起使用。 然而,AntiXSS 在 2015 年底就已经被认为使用寿命结束了。 因此,AntiXSS 库不兼容。net Core 1.0。

来自 AntiXSS 库最新版本的Sanitizer对象包含一个方法,可以将可能不安全的 HTML 代码转换为更安全的替代代码。 作为替换,你可以让剃刀引擎在 ASP.NET MVC 自动编码来自不可信源的变量值。 最终的目标是确保应用的用户不会将恶意客户端脚本注入到 HTML 主体中。

更多关于 ASP 中 HTML 编码的详细信息.NET MVC web 应用,查看 asp.net 的 XSS 部分的 HTML 编码部分。 网络文档:

https://docs.asp.net/en/latest/security/cross-site-scripting.html

防伪

[ValidateAntiForgeryToken]属性为您的 web 应用提供了内置的防伪支持。 它生成一个只包含 http 的 cookie,该 cookie 的值也写入 HTML 表单。 如果在 HTTPPOST操作发生后,值不匹配,这将发出一个红色信号,表明可能发生了跨站点请求伪造(CSRF); 也就是说,提交的表单与服务器最初提供的表单不同。

你可能已经注意到这个[ValidateAntiForgeryToken]属性已经被应用到 web 项目模板提供的AccountController中的几个动作方法:

[HttpPost] 
[ValidateAntiForgeryToken] 
public async Task<IActionResult> MethodName(...) 
{ 
        // ... 
} 

如果你需要为特定的表单禁用这个特性,你可以在你想要的视图中将标签帮助器asp-anti-forgery属性设置为 false:

<form asp-controller="controllerName"  
      asp-antiforgery="false"asp-anti-forgery="false" 
      asp-action="methodName"> 
</form> 

跨源请求

由于所谓的同源策略,web 浏览器通常会阻止 web 页面向不同域的页面发出 AJAX 请求。 然而,您的 web 应用可能需要接受来自不同域的 AJAX 请求。 幸运的是,万维网联盟(W3C)组织定义了一个名为跨源资源共享(CORS)的标准,该标准支持跨源请求。

要判断两个 URL 是否同源,需要比较每个 URL 的域名、子域名、端口号和方案(HTTP/HTTPS)。 如果它们是不同的,那么 CORS 就是一个有用的工具。 在 ASP 中启用 CORS.NET Core,遵循这里列出的简单步骤:

  1. 在您的project.json文件中添加对 CORS 包的引用。
  2. 更新ConfigureServices()方法添加Cors
  3. Configure()方法更新为Cors
  4. 在控制器代码中使用属性自定义 CORS 行为。

要添加对项目的引用,请在project.json文件的依赖项部分添加以下包:

"Microsoft.AspNetCore.Cors": "1.0.0" 

要设置您的配置,请将以下代码添加到Startup类的ConfigureServices()方法中的AddCors()中。 您可以将其直接添加到AddMvc():

services.AddCors(options => 
{ 
    options.AddPolicy("AllowSpecificOrigin", 
        builder => 
        { 
            builder.WithOrigins("http://fakedomain.com"); 
        }); 
}); 

前面的代码将 CORS 功能添加到应用中,同时显式地指定了命名的AllowSpecificOrigin策略。 它允许来自 fakedomain.com 的跨源请求通过您的网站进行处理。

要在控制器类级别启用 CORS,请使用[EnableCors]属性:

[EnableCors("AllowSpecificOrigin")] 
public class MyController: Controller 

要在控制器方法级别启用 CORS,使用相同的属性:

[EnableCors("AllowSpecificOrigin")] 
public IActionResult ActionMethod() 

在这两种情况下,添加正确的 using 语句来包含 CORS 命名空间:

using Microsoft.AspNetCore.Cors; 

方法级别的设置优先于控制器级别的设置,控制器级别的设置优先于应用级别的设置。 这允许我们在选择的任何级别禁用 CORS,即使它已经在应用级别启用。 例如,如果只需要为特定的方法禁用 CORS,则可以将[DisableCors]属性添加到特定的方法。

要在全局级别上在应用中添加AllowSpecificOrigin策略,请将以下代码添加到ConfigureServices()方法中。 您可以在调用AddMvc()之后添加以下代码:

services.Configure<MvcOptions>(options => 
{ 
    options.Filters.Add( 
        new CorsAuthorizationFilterFactory("AllowSpecificOrigin")); 
}); 

为了在您的Startup类中使用MvcOptionsCorsAuthorizationFilterFactory类,您必须在类的顶部添加以下名称空间:

using Microsoft.AspNetCore.Mvc; 
using Microsoft.AspNetCore.Mvc.Cors.Internal; 

总结

在本章中,我们讨论了提供访问应用的一些方法以及限制访问它的其他方法。 您可以使用身份验证来验证用户的身份,同时也可以使用授权来支持访问应用的特定部分。 您可以使用新的数据保护系统保护少量数据,同时您可以采取几个不同的操作来保护您的应用免受众所周知的安全风险。

在下一章和最后一章中,我们将介绍部署过程。 如果 web 应用只存在于您的开发环境中,那么它就不是一个好的应用。 通过将应用部署到公共网站,您可以接触到全球用户。 通过部署到云,您可以扩展您的网站以满足公众需求。

九、部署应用

有很多方法可以让您的 web 应用走向世界。 但是除了部署 web 项目,还有其他的考虑因素。 通常,您还会部署数据库,但不希望在生产环境中使用测试数据。 您应该维护单独的配置设置,以便您的项目能够指向每个环境中的正确数据库。

Visual Studio 和 ASP.NET Core 帮助您使用简单的技术和约定在不同的环境中维护项目文件和设置。 在微软 Azure 云平台的帮助下,您可以在几秒钟内轻松部署您的网站,并可根据需求进行缩放。

部署选项

本章将着重于部署你的 ASP.NET Core 应用到互联网信息服务(IIS)(微软的 web 服务器产品)和 Azure(微软的云服务)。 如果您计划部署到 Windows 以外的操作系统,请参考您特定操作系统的文档。 在继续部署之前,您应该熟悉环境配置设置。

环境配置

第 8 章身份验证、授权和安全中,我们快速了解了定义宿主环境的环境变量,例如,开发、登台或生产环境。 您可能还记得,在您的 web 项目的Properties面板的Debug选项卡中出现了一个环境变量列表。

打开launchSettings.json文件查看配置文件中的相同变量:

"environmentVariables": { 
  "ASPNETCORE_ENVIRONMENT": "Development" 
} 

您可能需要展开“解决方案资源管理器”面板中的Properties节点来找到launchSettings.json文件:

Environment configuration

Startup.cs文件的Configure()方法接受一个名为envIHostingEnvironment参数。 这可以用来检查环境类型,例如:

if (env.IsDevelopment()) { /* do something */ } 

有几个有用的属性和方法,包括以下:

  • env.EnvironmentName:用于获取或设置环境名称的读写属性
  • env.IsDevelopment():如果EnvironmentNameDevelopment则返回true
  • env.IsStaging():如果EnvironmentNameStaging则返回true
  • env.IsProduction():如果EnvironmentNameProduction则返回true
  • env.IsEnvironment(string environmentName):如果传入的参数environmentName匹配当前的EnvironmentName则返回true

在 Windows 上,env.EnvironmentName的值由ASPNETCORE_ENVIRONMENT变量设置。 在 Linux 或 OS X 上,环境设置会有所不同,因此请参考针对非 windows 部署的 ASP . net 文档,因为文档仍在发展中。 建议的值只是一种约定,但建议作为一个起点。

如果需要根据环境在 MVC 视图中显示不同的元素,可以使用环境标记助手,如下面的代码片段所示。 可以在一个逗号分隔的列表中使用多个环境名:

<environment names="Development"> 
    <p>For Development Only</p> 
</environment> 
<environment names="Staging,Production"> 
    <p>For Staging and Production Only</p> 
</environment> 

要在测试应用时模拟这些不同的环境,您可以在项目的PropertiesDebug选项卡中为每个环境创建不同的概要文件。 每次添加新的概要文件时,都可以添加相应的环境变量并分配适当的值。 相关配置文件将自动更新。 当您将应用部署到其他环境(如登台和生产环境)时,只需为特定的构建配置选择适当的概要文件。

为了帮助您的应用找到您的数据库,数据库连接字符串可以存储在您的appSettings.json文件中,位于您的 web 项目的根位置:

"ConnectionStrings": { 
  "DefaultConnection": "<full connection string goes here>" 
} 

在服务器环境中,您可以直接在服务器上定义环境变量。 在 Azure 托管的环境中,您可以通过Azure Portal设置环境变量。

部署你的 web 应用

部署 web 应用有几种方法。您可以简单地在解决方案资源管理器中右键单击 web 项目并选择Publish选项,如下面的截图所示。 这将为您呈现以下发布目标:

  • Microsoft Azure App Service:您可以部署到 Azure 上现有的 web 应用(以前称为Azure Website),也可以通过 Visual Studio 创建新的 web 应用。
  • Import:您可以导入Publish Profile来预填充标识目标位置的字段。 这可以来自主机提供商、网络管理员或开发团队。 它也可以从 Azure web 应用导出。
  • File System (under Custom):这个选项非常基本,但是提供了将已部署的应用直接导出到文件系统的选项。 您可以将导出的文件复制到您选择的目标目的地,或者压缩它们以将它们传输到其他地方。 Custom部分还提供了其他选项,如Web DeployWeb Deploy PackageFTP。 选择一个适合您需要的选项,并在必要时完成字段。
  • 其他托管选项:您可以访问网络托管选项的公共图库,您可以通过以下网址从托管合作伙伴中选择http://hosting.asp.net/hosting

Deploying your web app

部署数据库

SQL Server是许多 ASP 的常用选择.NET 开发人员,它与实体框架工作得很好。 将数据库部署到登台和生产环境有许多不同的方法。 对于一些开发人员,这个责任可能会移交给数据库管理员或系统管理员。

以下是一些你可以考虑的选择:

  • 编写 SQL 脚本在服务器上生成数据库对象
  • 使用实体框架迁移生成 SQL 脚本在服务器上运行
  • 使用实体框架迁移来生成数据库

您可能最终使用这些选项的组合,但最好的选项是您和您的团队能够达成一致的。 在某些情况下,您可能必须与数据库管理员、客户或 IT 经理讨论数据库创建方法。 您可能认为最好的选择是生成 SQL 脚本而不立即运行它们。 这将允许您在实际影响数据库之前检查脚本并在必要时进行修改。

根据您的场景,您可能必须通过代码或脚本自动插入种子数据。 理想情况下,应用代码应该准备好处理缺少数据的情况。

部署到 IIS

微软的 IIS web 服务器甚至早于活动服务器页面的第一个发布。 IIS 支持多种 web 框架和语言,包括 ASP。 净与 c#。 您可以在自己的 Windows 服务器或宿主环境中运行 IIS。

要部署到 IIS,您可以选择以下任一选项:

  • 将 web 应用文件直接复制到服务器上的 web 应用根目录
  • 使用 Visual Studio 前面提到的发布功能
  • 使用持续集成系统来自动化 web 部署

IIS 管理器界面如下所示:

Deploying to IIS

设置 IIS

在部署到 IIS 之前,必须确保在服务器上正确安装和配置了 IIS。 通常,一个网站将运行在端口80上用于 HTTP 访问,端口443用于 HTTPS 访问。 要启用安全套接字层(SSL),您还需要安装一个证书。

如果你不熟悉设置 IIS,请参考以下网址的官方教程:

http://www.iis.net/learn

设置 IIS 包括以下步骤:

  1. 在 Windows 的服务器管理器中启用Web 服务器(IIS)角色。
  2. 在 IIS 管理器中,设置一个 web 应用以在所需的端口上运行。
  3. 将应用池设置为No Managed Code.NET Core 管理自己的运行时。

您可能需要咨询您的管理员来在您的工作环境中设置 IIS。 如需更多信息,请在以下网址查阅官方文件:

https://docs.asp.net/en/latest/publishing/iis.html

使用文件系统

一旦 web 服务器被设置为运行 ASP.NET Core 应用,您可以从您的 web 应用直接复制文件到 web 服务器上的 web 根位置。 在 Visual Studio 中,您可以使用发布功能将文件导出到可以通过文件系统访问的文件夹。

要发布到文件系统,请遵循以下步骤:

  1. 在解决方案资源管理器中右键单击项目,然后单击发布
  2. Custom下选择File System选项。
  3. 命名你的出版简介。
  4. 选择目标位置,然后单击Next
  5. 预览连接信息,然后单击Next
  6. 根据需要更新设置和配置,然后单击Next
  7. 预览要发布的文件,然后单击发布

设置屏幕包括一个复选框,可以启用该复选框来删除目标位置中已经存在的其他文件。 如果文件列表在部署之间发生变化,这将非常有用。 请注意,无论您是部署到 Azure 还是部署到文件系统目的地,这个选项都存在,如下面的截图所示:

Using the filesystem

导入发布配置文件

为了导入发布配置文件,您将需要一个自己生成的发布配置文件,该文件来自团队成员或从网络主机获取。 然后您可以在发布对话框中选择导入选项。 如果你不确定如何生成自己的发布概要文件,按照说明在下一节中创建自己的 web 应用,然后浏览到 web 应用的叶片在 Azure 门户并单击发布概要文件按钮在 web 应用的工具栏。

要使用导入的发布配置文件发布,请遵循以下步骤:

  1. 在解决方案资源管理器中右键单击项目,然后单击发布
  2. 选择Import选项。
  3. 浏览到发布概要文件。
  4. 预览连接信息,然后单击Next
  5. 根据需要更新设置和配置,然后单击Next
  6. 预览要发布的文件,然后单击发布

部署到微软云平台 Azure

Azure 已成为全球许多开发人员的流行云平台,无论应用使用的是微软框架还是开源替代方案。 ASP。 对于 NET 开发者来说,Azure 是一个无需动脑筋的东西。 在免费计划下,你可以在 Azure 上创建最多 10 个免费网站,它为网络应用提供了一个野外生存的游乐场。

要开始使用 Azure,您可以在http://azure.com注册免费试用。

创建 web 应用

一旦你注册了 Azure,你就可以使用 Azure 门户创建一个 web 应用。 这个过程只需要几秒钟。 一旦启动,web 应用应该在一分钟内启动并运行。

要在 Azure 中创建一个 web 应用,请遵循以下步骤:

  1. 登录 Azure 门户网站http://portal.azure.com
  2. 点击左上方有加号的按钮。
  3. 从选项列表中选择Web + Mobile
  4. 从选项列表中选择Web App
  5. 填写所需信息,然后单击创建

允许 Azure 几秒钟来生成你的新 web 应用。一旦你的应用被创建,你应该被重定向到仪表板。 如果你把 web 应用仪表板,您应该看到一个瓷砖上更新你的状态你的 web 应用。一旦完成,你应该被重定向到设置叶片您刚才创建的 web 应用。

以下截图显示了创建Web App的过程:

Creating a web app

请注意,Azure UI 将随着时间的推移而不断发展,因此本章中的说明可能不能准确反映您在屏幕上看到的内容。 然而,基本的指令流可以用来近似地描述您需要做的事情。 有关最新的说明,请参阅以下网址所载的官方文件:

https://azure.microsoft.com/documentation

创建虚拟机

如果您需要完全控制一个虚拟机(VM),您也可以在 Azure 上创建一个 VM 来部署您的应用。 需要注意的是,一个虚拟机的成本通常比一个简单的 web 应用要高。它也需要花费更多的精力来创建和配置机器和 web 服务器。

要在 Azure 中创建虚拟机,请遵循以下步骤:

  1. 如果您还没有登录 Azure,请登录。
  2. 点击左上方有加号的按钮。
  3. 从选项列表中选择Virtual Machines
  4. 选择一个特色项目,例如:Windows Server 2012 R2 Datacenter
  5. 选择一个部署模型(如果可用),然后单击创建

尽管您可以选择经典部署模型,但建议您选择资源管理器选项作为您的部署模型。 这将允许您轻松地管理 Azure 中的相关资源以及 VM。

在出现的下一个刀片中,您必须输入关于 VM 的其他信息:

  • 基础:输入机器名和管理凭据,然后选择订阅、资源组和位置。 如果您还没有任何资源组,您可以在选择它之前创建一个新的资源组。
  • 大小:从多个预先配置的机器大小中选择一个。 您的选择将决定内核数量、RAM 数量、磁盘空间数量、自动伸缩能力和负载平衡特性。 请记住,创建具有更多特性和功能的 vm 的成本会更高。
  • 设置:选择存储帐户、虚拟网络、子网、公网 IP、默认安全组、诊断存储帐户(如果启用诊断)和可用性设置。 可用性集允许您将多个机器添加到同一个集合中,以避免所有机器同时关闭,这可以通过将 vm 放置在不同的物理服务器/机架上实现。

允许 Azure 大约 8 到 10 分钟来生成新的 VM。 虚拟机创建完成后,应该被重定向到仪表板,在那里应该会看到一个更新 VM 状态的平铺。 完成后,您应该被重定向到您刚刚创建的 VM 的设置刀片服务器。

在虚拟机的主刀片中,图标工具栏应该有一个Connect图标,允许您下载.rdp文件。 可以在 Windows(或其他操作系统)的远程桌面中打开该文件,直接与 VM 交互,就像您在 VM 面前一样。

如果要直接通过远程桌面连接到虚拟机,请使用以下格式的凭证:

  • 登录名:<machinename\username>
  • 登录密码:<password>

虚拟机创建过程如下所示:

Creating a virtual machine

一旦您访问了计算机,您将不得不手动设置 IIS 并在 Azure 中配置 VM 以启用所需的端口。 Azure 中的这些额外设置可以通过虚拟机关联的网络安全组访问。 如果无法找到,可单击左侧菜单中的所有资源,过滤所有资源列表,选择与虚拟机同名的网络安全组。 在此组的设置刀片中,您应该能够管理组的属性和入站/出站安全规则。 默认情况下,RDP 端口应该为您启用。

部署到 Azure

为了部署到 Azure,你可以手动部署你的 web 项目,或者通过持续集成设置自动部署。 Visual Studio 2015 使得将 web 应用部署到 Azure 中的 web 应用变得非常容易。

要从 Visual Studio 部署到 Azure,请遵循以下步骤:

  1. 在解决方案资源管理器中右键单击项目,然后单击发布
  2. 选择Microsoft Azure App Service选项。
  3. 如果您还没有登录 Azure,请登录。
  4. 选择一个Web App或新建一个 Web App。
  5. 如适用,请选择具体的“部署槽位”。
  6. 预览连接信息(如果适用),然后单击Next
  7. 根据需要更新设置和配置,然后单击Next
  8. 预览要发布的文件,点击发布,如下图所示:

Deploying to Azure

对于小型应用,这个过程通常需要几分钟,但发布时间可能会根据文件的数量和大小而有所不同。 一旦部署,你的网页浏览器应该弹出网站加载从你的网页应用在 Azure 上的 URL。 网站第一次加载时,应用第一次在 web 服务器上启动应该需要几秒钟。 随后的加载几乎是瞬间完成的。

如果你的网站在一段时间内没有访客,它就被认为是闲置的。 如果发生这种情况,web 应用将被 Azure 卸载,直到下一个访问者请求该站点。 第一个到达空闲站点的访问者将触发该站点再次加载,这将花费几秒钟。 虽然这有助于节省资源,但您可能希望始终保持 web 应用处于加载状态。 在 Azure 中的 web 应用的应用设置刀片中,您可以启用一个名为Always On的功能,以确保它总是加载。

持续集成

无论你从事一个项目单独或与一个团队的开发人员,你将最有可能使用 Git 源代码控制系统,如,Subversion,TFS。 为了帮助自动化部署过程,通过持续集成(CI)系统直接从源代码控制部署 web 应用是非常有效的。

**在实现 CI 时,有几个不同的选项可以考虑:

  • Team Foundation Server【T1
  • Visual Studio Team Services,微软的托管选项
  • 来自 JetBrains 的TeamCity【T1

要开始使用 TeamCity,你可以从这里下载:

https://www.jetbrains.com/teamcity

要开始使用 TFS,你可以从这里下载:

https://visualstudio.com/products/tfs-overview-vs.aspx

要开始使用 VSTS,你可以在这里注册:

https://visualstudio.com/products/visual-studio-team-services-vs.aspx

准备 CI

并不是团队中的每个人都准备好了进行 CI。 事实上,一些团队成员甚至项目经理可能会反对它。 讨论您的 CI 决策的利弊,并确保您有一个成功的策略来为您的项目实现 CI,这对您来说是很重要的。

考虑以下问题:

  • 我们没有时间讨论 CI:如果您听到此消息,那么您应该考虑一下在 IT 管理员休假的午夜将应用部署到登台服务器时所损失的时间。 从长远来看,自动化部署将使您节省更多时间。
  • We can’t afford to do CI:如果你听到这个抱怨,你应该考虑没有 CI 系统的成本。 除了执行部署之外,CI 系统还允许您运行单元测试。 您可能要花费大量的时间(甚至数天/周)来排除在修复 bug 或添加新特性时引入的复杂问题。 CI 系统将有助于及早发现这些问题,并在长期内节省时间。
  • We won't get buy in from our clients or upper management:如果你面临这种类型的恐惧,你应该说服你的团队与你的客户谈论 CI 的好处。 拥有一个合适的 CI 系统意味着关键的涉众将能够在任何时候访问 web 应用的实时版本,甚至是在开发迭代期间。 这将支持一个敏捷开发周期,它将鼓励早期反馈。

设置连续部署

使用 Azure 设置连续部署(CD)相当简单。 您可以从 Azure 门户的选项列表中进行选择。 一旦 CD 设置好,每次签入源代码控制系统都会触发到 Azure 的 web 部署。

要开始,完成以下步骤:

  1. 如果您还没有登录 Azure,请登录。
  2. 仪表板中选择您的 web 应用。
  3. 浏览您的 web 应用的设置。
  4. 设置刀片服务器中,查找部署选项来配置部署源。
  5. 在源列表中,选择一个源。

下面是一个你可能会在资料列表中看到的例子:

  • Visual Studio 在线
  • OneDrive
  • 本地 Git 存储库
  • GitHub
  • Bitbucket 都
  • Dropbox
  • 外部存储库(如 Git, Mercurial 等),如下截图所示:

Setting up Continuous Deployment

对于您所选择的选项,填写连接到相应服务的所需信息以建立连接。 一旦建立连接,您的凭据将用于未来的部署。 Azure 允许您为登台和生产环境设置部署槽,这些环境可以根据需要进行交换。 这将帮助您加快将最新的稳定构建引入生产环境的时间。

如果您有兴趣了解更多关于使用Visual Studio Team Services在云中运行单元测试的信息,请参阅官方文档:

https://www.visualstudio.com/docs/test/developer-testing/developer-testing

CI 的缺陷

现在您已经设置了 CD 和 CI,那么会出现什么问题呢? 多年的经验告诉许多开发人员要小心陷阱。 只要你知道将会发生什么,你就会准备好应对 CI 旅程中的挑战。

以下是你可能会遇到的挑战:

  • 学习曲线:如果您的团队是 CI 的新手,那么您的团队成员将会有一个学习曲线。 这是没有办法的。 尽一切努力让你的团队跟上进度。 放心吧,好处很快就会超过最初的障碍。
  • 前期成本:当你越过学习曲线时,你将面临一些前期成本,包括工作时间和可能的服务器费用。 再一次提醒自己,从长远来看,提前支付的额外费用会帮你省钱。
  • 误报:一旦一切都设置好了,一些单元测试可能会导致一些误报。 这可能是由编写糟糕的测试或一些不需要解决的模糊运行时场景引起的。
  • 错误的保证:您也可能有编写得很差的测试,导致错误的保证。 一个糟糕的测试可能表明一切正常,但忽略了等待某个用户在未来发现它的错误条件。
  • 长时间运行的部署:如果你有很多代码和大量的测试,你可能有长时间运行的部署。 关键在于重构代码库以减少不必要的代码,并优化单元测试以使其快速有效地运行。

一旦完成了初始设置并优化了代码和测试,您就可以顺利地进行部署过程了。 当您继续对代码和基础设施进行更改时,您可能偶尔需要与您的团队一起重新访问您的持续部署策略。 这将帮助你的团队提出任何需要解决的问题。

总结

在本章中,我们介绍了各种部署选项,并重点介绍了部署 ASP.NET web 应用到 Azure。 最后,我们简要介绍了 CI 和 CD。

展望未来,你应该继续关注微软的公告,以了解更多关于 ASP 的信息.NET Core 和 Azure。 有一些网站、博客和在线视频可能对你有用。

以下是你可能会觉得有用的在线资源列表:

posted @ 2025-10-21 10:43  绝不原创的飞龙  阅读(6)  评论(0)    收藏  举报