ASP-NET-Core-MVC-编程学习指南-全-

ASP.NET Core MVC 编程学习指南(全)

原文:zh.annas-archive.org/md5/76c90d44dc67d34f6be132448b19d491

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

本书旨在帮助您学习 ASP.NET Core MVC 的基础知识,并将这些知识应用于使用 ASP.NET Core 构建应用程序。本书还旨在为想要学习 ASP.NET MVC 的初学者提供坚实的指南。具体来说,本书将涵盖以下主题:

  • ASP.NET Core MVC 的基本和目标

  • ASP.NET Core 的哲学(关注点分离、约定优于配置)

  • ASP.NET Core MVC 的组件——控制器、模型和视图

  • 使用 Entity Framework 与数据库交互

  • 在客户端和服务器端验证用户的输入

  • 使用 Bootstrap 为应用程序提供一次改头换面的机会

  • 利用 ASP.NET Core MVC 提供的不同部署选项

本书涵盖的内容

第一章,ASP.NET Core 简介,涵盖了 ASP.NET MVC 的基础知识以及它在 ASP.NET 生态系统中的位置。本章解释了网络开发的基本知识,包括客户端组件和服务器端组件,以及程序员在每一层可以做什么和不能做什么。

第二章,设置环境,向读者展示了如何设置开发环境,包括 Visual Studio 和 ASP.NET Core 的安装。还讨论了设置开发环境的硬件和软件要求,并介绍了 ASP.NET MVC 应用程序的结构。

第三章,控制器,解释了构成控制器和动作方法的内容及其角色和责任。在本章中,将创建一个简单的控制器及其动作方法,并从整体 ASP.NET MVC 应用程序的角度向读者解释动作方法和控制器的作用。

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

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

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

第七章,路由,解释了路由模块如何通过示例从接收到的请求中选择适当的控制器,并展示了路由的各种选项和功能。本章还将指导您根据业务逻辑或 SEO 目的为 ASP.NET MVC 应用程序构建自定义路由。

第八章,使用 Bootstrap 美化 ASP.NET 应用程序,教授如何使用 Bootstrap,一个响应式前端框架,来美化你的应用程序。你将指导创建 HTML 表单控件。

第九章,ASP.NET Core 应用程序的部署,解释了 project.json 库如何处理 ASP.NET Core 应用程序的所有依赖项以及版本。它还解释了 K 运行时(ASP.NET Core 应用程序中的最新选项),这样 ASP.NET MVC 应用程序就可以在非 Windows 环境中部署。

第十章,使用 Web API 构建 Web 服务,解释了基于 HTTP 的服务和如何使用 Web API 来实现它们。它还将介绍 Fiddler,并使用它来组合 HTTP 请求。

第十一章,提高 ASP.NET Core 应用程序的性能,解释了分析应用程序性能的方法以及提高性能的各种层级的措施。

第十二章,ASP.NET Core Identity,解释了应用程序的安全方面以及如何使用 Entity Framework 实现应用程序的安全身份。

你需要这本书什么

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

这本书适合谁

这本书是为想要学习如何使用 ASP.NET Core 构建 Web 应用程序的开发者而写的,是为想要使用 Microsoft 技术构建 Web 应用程序的职业开发者而写的,以及为在 Ruby on Rails 或其他 Web 框架中工作的开发者而写的,他们想学习如何使用 ASP.NET Core MVC。

不需要了解 ASP.NET 平台或 .NET 平台。即使你不需要有 C# 的经验,对任何现代编程语言的基本结构(循环、条件、类和对象)的理解也会有所帮助。

读累了记得休息一会哦~

公众号:古德猫宁李

  • 电子书搜索下载

  • 书单分享

  • 书友学习交流

网站:沉金书屋 https://www.chenjin5.com

  • 电子书搜索下载

  • 电子书打包资源分享

  • 学习资源分享

规范

在这本书中,你会发现许多文本样式,用于区分不同类型的信息。以下是一些这些样式的示例及其含义的解释。

文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 标签以如下方式显示:“我们需要在project.json框架中将Kestrel HTTP Server包作为依赖项添加。”

代码块设置如下:

public static void Main(string[] args)
{

var host = new WebHostBuilder()

.UseKestrel()

}

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


vi project.json

新术语重要词汇以粗体显示。屏幕上看到的单词,例如在菜单或对话框中,在文本中显示如下:“本书中的快捷键基于Mac OS X 10.5+方案。”

注意

警告或重要注意事项以如下方式显示在框中。

技巧

技巧和窍门看起来像这样。

读者反馈

我们始终欢迎读者的反馈。请告诉我们您对这本书的看法——您喜欢或不喜欢什么。读者的反馈对我们很重要,因为它帮助我们开发出您真正能从中获得最大价值的标题。要发送一般反馈,请简单地发送电子邮件至 feedback@packtpub.com,并在邮件主题中提及书的标题。如果您在某个主题领域有专业知识,并且您对撰写或为书籍做出贡献感兴趣,请参阅我们的作者指南www.packtpub.com/authors

客户支持

现在您是 Packt 图书的骄傲拥有者,我们有一些事情可以帮助您从购买中获得最大价值。

下载示例代码

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

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

  1. 使用您的电子邮件地址和密码登录或注册我们的网站。

  2. 将鼠标指针悬停在顶部的支持标签上。

  3. 点击代码下载 & 错误修正

  4. 搜索框中输入书的名称。

  5. 选择您想要下载代码文件的书籍。

  6. 从下拉菜单中选择您购买这本书的地方。

  7. 点击代码下载

文件下载完成后,请确保您使用最新版本解压缩或提取文件夹:

  • 适用于 Windows 的 WinRAR / 7-Zip

  • 适用于 Mac 的 Zipeg / iZip / UnRarX

  • 适用于 Linux 的 7-Zip / PeaZip

本书代码包也托管在 GitHub 上github.com/PacktPublishing/learning-asp-dot-net-core-programming。我们还有其他来自我们丰富图书和视频目录的代码包,可在github.com/PacktPublishing/找到。查看它们吧!

下载本书的颜色图像

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

勘误

尽管我们已经尽一切努力确保内容的准确性,但错误仍然可能发生。如果您在我们的某本书中发现错误——可能是文本或代码中的错误——如果您能向我们报告这一点,我们将不胜感激。通过这样做,您可以节省其他读者的挫败感,并帮助我们改进本书的后续版本。如果您发现任何勘误,请通过访问 www.packtpub.com/submit-errata ,选择您的书籍,点击勘误提交表单链接,并输入您的勘误详情来报告。一旦您的勘误得到验证,您的提交将被接受,勘误将被上传到我们的网站或添加到该标题的勘误部分下的现有勘误列表中。

要查看之前提交的勘误表,请访问 www.packtpub.com/books/content/support 并在搜索字段中输入书籍名称。所需信息将在勘误部分显示。

海盗行为

在互联网上对版权材料的盗版是一个跨所有媒体的持续问题。在 Packt,我们非常重视保护我们的版权和许可证。如果您在互联网上发现任何形式的我们作品的非法副本,请立即提供位置地址或网站名称,以便我们可以追究补救措施。

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

我们感谢您在保护我们的作者和我们为您提供有价值内容的能力方面的帮助。

问题

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

第一章。ASP.NET Core 简介

ASP.NET Core,微软推出的 ASP.NET MVC 的最新版本,是一个服务器端 Web 应用程序开发框架,它可以帮助你有效地构建 Web 应用程序。这个框架运行在 ASP.NET 5 平台之上,使得你的应用程序能够在包括 Linux 和 Mac OS X 在内的多种平台上运行。这为开发者提供了大量的机会,在这个时代成为一名.NET 开发者是非常令人兴奋的。

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

  • 关于 Web 应用程序的基本概念——HTTP、客户端和服务器端

  • ASP.NET 的三个编程模型——ASP.NET Web Forms、ASP.NET Web Pages 和 ASP.NET MVC

  • ASP.NET MVC 的哲学

  • ASP.NET Core 和 ASP.NET 5 的功能

在讨论 ASP.NET Core 及其功能之前,让我们先了解 Web 应用程序开发的基础知识。我坚信这样一个原则:如果你想在某个领域成为专家,你需要非常精通基础知识。这将有助于调试问题和解决问题。

话虽如此,我们将讨论以下关键基础知识:

  • 关于 Web 应用程序的工作原理以及一些关于 HTTP 的知识

  • 客户端和服务器端

  • HTTP 方法

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

Web 应用程序是如何工作的

所有 Web 应用程序,无论它们是使用 ASP.NET MVC、Ruby on Rails 还是任何其他新技术构建的,都基于 HTTP 协议。一些应用程序使用 HTTPS(HTTP 的安全版本),在数据通过线路传输之前进行加密。但 HTTPS 仍然使用 HTTP。

那么什么是 HTTP 协议呢?

HTTP代表超文本传输协议,是一种为分布式超媒体系统设计的应用协议。"超文本"在超文本传输协议中指的是使用超链接在文档之间进行导航的结构化文本。HTTP 标准由互联网工程任务组IETF)和万维网联盟W3C)制定。当前版本的 HTTP 是 HTTP/2,于 2015 年标准化。它被大多数网络浏览器支持,如 Internet Explorer、Chrome 和 Firefox。

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

请求-响应模式

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

回到请求-响应模式,当你从服务器请求一个资源时,服务器会响应你,提供所请求的资源。资源可以是任何东西——一个网页、文本文件、一张图片,或者另一种数据格式。

请求-响应模式

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

HTTP 的无状态特性

当你再次请求相同的资源时,服务器会再次响应你,提供所请求的资源,而无需知道之前已经请求并提供了相同的资源。HTTP 协议本身并不了解之前接收和提供过的任何请求的状态知识。有几种机制可以维护状态,但 HTTP 协议本身并不维护状态。我们将在稍后解释维护状态的机制。

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

  1. 你输入以下 URL:en.wikipedia.org/wiki/ASP.NET_MVC。这是一个关于 ASP.NET MVC 的维基百科网页。

  2. 从前面的 URL,浏览器向维基百科服务器发起请求。

  3. 维基百科的 Web 服务器为你提供了 ASP.NET MVC 网页。

  4. 浏览器接收该网页并将其呈现出来。

  5. 你再次通过输入相同的 URL(en.wikipedia.org/wiki/ASP.NET_MVC)并按Enter键来请求相同的页面。

  6. 浏览器再次向维基百科服务器发起请求。

  7. 维基百科为你提供了相同的 ASP.NET MVC 网页,而它并不知道之前已经从同一资源请求过相同的资源。

注意

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

客户端和服务器端

理解 Web 应用的客户端和服务器端以及各自可以做什么是必要的。就 Web 应用而言,你的客户端是浏览器,你的服务器可以是 Web 服务器/应用服务器。

浏览器端是你在浏览器中看到的所有事情。这是你的 JavaScript 代码运行和 HTML 元素所在的地方。

服务器端是在你电脑另一端的服务器上发生的事情。你从浏览器发起的请求必须通过线路(可能跨越网络)执行一些服务器端代码,并返回适当的响应。你的浏览器对服务器端技术或服务器端代码所使用的语言一无所知。服务器端也是你的 C#代码所在的地方。

让我们讨论一些事实来澄清问题:

  • 事实 1:所有浏览器只能理解 HTML、CSS 和 JavaScript,无论浏览器供应商如何。

    • 你可能在使用 Internet Explorer、Firefox、Chrome 或任何其他浏览器。然而,你的浏览器只能理解 HTML、CSS 和 JavaScript 的这一事实是正确的。它不能理解 C#。它也不能理解 Java。更不用说 Ruby。只有 HTML、CSS 和 JavaScript。这就是为什么你可以访问使用任何技术构建的网络应用,并且这些应用可以通过相同的浏览器访问。客户端和服务器端
  • 事实 2:任何网络开发框架的目的是将你的服务器端代码转换为 HTML、CSS 和 JavaScript。

    • 这与前面的观点相关。由于浏览器只能理解 HTML、CSS 和 JavaScript,所有网络开发技术都应该将你的服务器端代码转换为 HTML、CSS 和 JavaScript,以便你的浏览器可以理解。这是任何网络开发框架的主要目的。无论你使用 ASP.NET MVC、ASP.NET Web Forms、Ruby on Rails 还是 J2EE 来构建你的网络应用,这都是正确的。每个网络开发框架可能都有独特的概念/实现,关于如何生成 HTML、CSS 和 JavaScript,并且可能以不同的方式处理功能,如安全性和性能。但每个框架都必须生成 HTML,因为这是你的浏览器所能理解的。

HTTP 方法

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

HTTP 中可用的方法有 GETHEADPOSTPUTDELETETRACEOPTIONSCONNECTPATCH。在大多数网络应用中,GETPOST 方法被广泛使用。在本节中,我们将讨论这些方法。稍后,我们将根据需要讨论其他 HTTP 方法。

GET 方法

GET 是 HTTP 协议中的一种方法,用于从服务器获取资源。使用 GET 方法的请求应仅检索数据,不应有任何副作用。这意味着如果你反复发送相同的 GET 请求,你应该得到相同的数据,并且服务器状态不应因这个 GET 请求而发生变化。

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

在前面的GET请求中,我们传递了几个参数。tech是第一个参数,其值为mvc6,而db是第二个参数,其值为sql。假设您的网站接受这些参数及其值,并在向用户展示这些博客文章之前,在数据库中搜索关于mvc6sql的博客文章。

GET 方法

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

此外,大多数浏览器对 URL 中的字符数有限制,因此,当使用GET请求时,我们不能发送大量数据。

POST 方法

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

数据在请求体中传递。这有以下影响:

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

  • 由于数据不是通过请求 URL 发送的,因此它不会占用 URL 空间,因此它没有 URL 长度限制的问题。POST 方法

既然我们已经涵盖了基础知识,我们现在可以继续讨论 ASP.NET。

什么是 ASP.NET?

ASP.NET 是一个服务器端 Web 应用程序开发框架,允许开发者构建 Web 应用程序、网站和 Web 服务。它最初由微软在 2002 年初推出,在这 14 年中,它经历了许多变化。

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

  • ASP.NET Web Forms

  • ASP.NET Web Pages

  • ASP.NET MVC

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

ASP.NET Web Forms

从历史上看,当 ASP.NET 首次推出时,ASP.NET Web Forms 是程序员在 ASP.NET 中开发 Web 应用程序的唯一编程模型。

ASP.NET Web Forms 模型抽象了 Web,即使 Web 本质上是无状态的,也能保持状态。

它还支持服务器端的事件驱动编程模型。这有助于桌面应用程序开发者平滑过渡到 Web 应用程序开发。

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

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

这是在 ASP.NET MVC 出现之前的主要编程模型,并且此编程模型仍在被用于维护使用此模型编写的生产应用程序。

ASP.NET Web Pages

ASP.NET Web Pages 主要针对小型 Web 应用程序,其中数据处理逻辑直接写在网页上。

ASP.NET MVC

ASP.NET MVC 是 ASP.NET 中对 MVC 模式的实现。ASP.NET Web Forms 的缺点,如对 HTML 生成的有限控制,在 ASP.NET MVC 中得到解决。由于大多数现代应用程序都由客户端 JavaScript 库/框架(如jQueryKnockoutJSAngularJS)控制,因此对生成的 HTML 拥有完全控制至关重要。

让我们简要谈谈模型-视图-控制器(Model-View-Controller)模式及其如何有利于 Web 应用程序开发。

模型-视图-控制器(MVC)模式:这是一种软件架构模式,有助于定义每个组件的责任以及它们如何协同工作以实现整体目标。此模式主要用于构建用户界面,并适用于许多领域,包括开发桌面应用程序和 Web 应用程序。但我将从 Web 开发的角度来解释 MVC 模式。

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

  • 模型:此组件代表您的领域数据。请注意,这并不是您的数据库。此模型组件可以与您的数据库通信,但模型仅代表您的领域数据。例如,如果您正在构建电子商务 Web 应用程序,模型组件可能包含如 Product(产品)、Supplier(供应商)和 Inventory(库存)等类。

  • 视图:此组件负责向用户展示什么内容。通常,此组件会包含您的 HTML 和 CSS 文件。这还可能包括控制您的 Web 应用程序外观的布局信息。

  • 控制器:正如其名所示,控制器负责与不同组件交互。它接收请求(通过路由模块),与模型通信,并向用户发送适当的视图。ASP.NET MVC

这种责任分离为 Web 应用程序开发带来了极大的灵活性,允许每个区域独立管理。

ASP.NET MVC 的特点

ASP.NET MVC 是一个有观点的应用程序开发框架,它倾向于以某种独特的方式处理某些功能。让我们讨论 ASP.NET MVC 的每个功能,以及它们带来的好处。

约定优于配置:在 ASP.NET MVC 中,完全采用了“约定优于配置”的原则。这是一种哲学,你可以在不进行任何配置的情况下确定它的工作方式。

这是一个设计方法,在开发应用程序时显著减少了决策的数量,从而使其更加简单。

如果你使用过任何技术构建过任何应用程序,你可能使用过某种类型的 XML 文件,其中你必须配置其中的所有内容。即使是更简单直接的事情,我们也可能需要在那里进行配置。

ASP.NET MVC 完全采用了“约定优于配置”。这是一种哲学,你可以在不进行任何配置的情况下确定它的工作方式。

让我给你一个简单的例子。所有控制器代码都位于 Controller 文件夹中,而视图为每个控制器都有一个单独的文件夹。每当有请求到来时,ASP.NET MVC 就知道在哪里找到控制器及其关联的视图,而无需任何配置。这种方法减少了配置并缩短了调试时间。

关注点分离

如前所述,ASP.NET MVC 有三个主要组件——模型、控制器和视图。这清楚地分离了责任,以便 UI 设计师或 UI 开发者可以工作在视图上,而后端开发者可以工作在模型上,为应用程序构建数据域或与数据库通信。由于每个组件的职责都明确且分离,工作可以并行进行。

对生成的 HTML 的控制

如果你有过构建 ASP.NET Web Forms 应用程序的经验,你可能使用过 ASP 控件,例如 asp:textbox。尽管这些控件有很多好处,但它们也有自己的缺点。当使用这些控件时,开发者无法完全控制生成的 HTML。当然,你可以在 ASP 控件中设置一些属性,这些属性反过来会设置你生成的 HTML 中的某些属性。但无法实现完全控制。ASP.NET MVC HTML 辅助工具和 ASP.NET Core 中的标签辅助工具提供了对生成的 HTML 的更好控制。

更好的单元测试支持

由于每个组件都是分离和分区的,因此创建单元测试用例变得更容易实现:

  • ASP.NET Core 中统一的 MVC 和 Web API 控制器:在 ASP.NET MVC 的早期版本中,不同的控制器用于 MVC (System.Web.MVC.Controller) 和 Web API (System.Web.Http.ApiController)。在 ASP.NET Core 中,只有一个基控制器支持创建 MVC 控制器和 Web API 控制器。在路由方面,所有控制器使用相同的路由。当然,你可以根据需要使用基于约定的路由或基于属性的路由。

  • 关于 Web API 的说明:Web API 是微软用于通过 HTTP 协议构建 Web 服务的 Microsoft 技术。HTTP 不仅限于服务网页。Web API 可以用于构建 API 服务和数据。这种方法的优点是,使用 Web API 构建的服务可以被各种客户端消费,例如浏览器、移动应用程序和桌面应用程序。

以下为 ASP.NET MVC 早期版本(直到 ASP.NET MVC 5)的代码:

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

ASP.NET 5

ASP.NET 5 是微软最新推出的框架,用于使用 .NET 构建现代基于云的应用程序。它是一个跨平台框架,这意味着您可以在任何平台上运行基于 ASP.NET 5 构建的应用程序,例如 Linux 或 Mac OS X,当然也包括 Microsoft Windows。ASP.NET 5 是开源的,完整的源代码可在 GitHub 上找到,网址为 github.com/aspnet/home

ASP.NET MVC 的最新版本,即 ASP.NET Core,运行在 ASP.NET 5 平台上。

ASP.NET 5 的特性

  • 跨平台支持:在 ASP.NET 5 上构建的应用程序可以在安装了 ASP.NET 5 的任何平台上运行。这意味着您在 ASP.NET 5 上构建的应用程序可以在 Apple OS X 和 Linux 机器上运行。在后续章节中,我们将解释如何在 Linux 机器上部署 ASP.NET Core。

  • 更好的客户端开发支持:ASP.NET 5 设计得可以无缝地与一系列客户端框架协同工作,例如 AngularJsKnockoutBootstrapReact.js

概述

在本章中,我们学习了网络开发的基础知识,包括服务器端和客户端的构成。我们还讨论了 ASP.NET Core 和 ASP.NET 5 的特性。

第二章. 设置环境

在任何开发项目中,设置正确类型的开发环境至关重要,这样你就可以专注于开发解决方案,而不是解决环境问题或配置问题。对于 .NET 来说,Visual Studio 是构建 .NET 网络应用程序的默认标准 IDE集成开发环境)。

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

  • IDE 的用途

  • Visual Studio 的不同版本

  • Visual Studio Community 2015 的安装

  • 创建您的第一个 ASP.NET MVC 5 项目和项目结构

IDE 的用途

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

当你开发网络应用程序时,你可能需要以下东西以提高生产力:

  • 代码编辑器:这是你输入代码的文本编辑器。你的代码编辑器应该能够识别不同的结构,例如你的编程语言的 if 条件、for 循环。在 Visual Studio 中,所有关键字都会以蓝色突出显示。

  • Intellisense:Intellisense 是一种上下文感知的代码补全功能,在大多数现代 IDE(包括 Visual Studio)中都可用。例如,当你在对象后面输入一个点时,这个 Intellisense 功能会列出该对象上所有可用的方法。这有助于开发者更快、更轻松地编写代码。

  • 构建/发布:如果你能够通过单次点击或单条命令构建或发布应用程序,那将非常有帮助。Visual Studio 提供了多种选项,可以一键构建单独的项目或构建整个解决方案。这使得应用程序的构建和部署更加容易。

  • 模板:根据应用程序的类型,你可能需要创建不同的文件夹和文件,以及样板代码。因此,如果你的 IDE 支持创建不同类型的模板,那将非常有帮助。Visual Studio 生成不同类型的模板,包括 ASP.NET Web Forms、MVC 和 Web API 的代码,以便你快速开始。

  • 易于添加项目:你的 IDE 应该允许你轻松地添加不同类型的项。例如,你应该能够无问题地添加一个 XML 文件。如果 XML 文件的结构有任何问题,它应该能够突出显示问题并提供帮助信息,以帮助你解决问题。

读累了记得休息一会哦~

公众号:古德猫宁李

  • 电子书搜索下载

  • 书单分享

  • 书友学习交流

网站沉金书屋 https://www.chenjin5.com

  • 电子书搜索下载

  • 电子书打包资源分享

  • 学习资源分享

Visual Studio 的产品

可用的 Visual Studio 2015 版本有多种,以满足开发者和组织机构的不同需求。主要来说,Visual Studio 2015 有以下四种版本:

  • Visual Studio Community

  • Visual Studio Professional

  • Visual Studio Enterprise

  • Visual Studio Test Professional

系统要求

Visual Studio 可以安装在运行 Windows 7 Service Pack 1 操作系统及更高版本的计算机上。您可以从以下 URL 获取完整的系统要求列表:

Visual Studio 2015 系统要求

Visual Studio Community 2015

这是一个功能齐全的 IDE,可用于构建桌面应用程序、Web 应用程序和云服务。对于个人用户,它是免费提供的。

您可以从以下 URL 下载 Visual Studio Community:

Visual Studio Community

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

Visual Studio Professional

如其名所示,Visual Studio Professional 面向专业开发者,包含如 Code Lens 提高团队生产力的功能。它还具备增强团队协作的功能。

Visual Studio Enterprise

Visual Studio Enterprise 是 Visual Studio 的完整版本,包含用于协作的完整功能集,包括团队基础服务器、建模和测试。

Visual Studio Test Professional

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

安装 Visual Studio Community

按照以下步骤安装 Visual Studio Community 2015:

  1. 访问以下链接下载 Visual Studio Community 2015:

    Visual Studio Community

    安装 Visual Studio Community

  2. 点击 下载 Community 2015 按钮。将文件保存在您可以轻松检索的文件夹中:安装 Visual Studio Community

  3. 运行下载的可执行文件:安装 Visual Studio Community

  4. 点击 运行 按钮,将出现以下屏幕:安装 Visual Studio Community

安装有两种类型——默认安装和自定义安装。默认安装安装最常用的功能,这将覆盖大多数开发者的使用场景。自定义安装可以帮助您自定义要安装的组件:

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

  2. 根据您的内存和处理器速度,安装过程可能需要 1 到 2 小时。安装 Visual Studio Community

  3. 一旦所有组件都安装完成,你将看到以下 设置完成 界面:安装 Visual Studio Community

安装 ASP.NET 5

当我们安装 Visual Studio Community 2015 版本时,ASP.NET 5 将默认安装。由于 ASP.NET Core 应用程序运行在 ASP.NET 5 之上,我们需要安装 ASP.NET 5。安装 ASP.NET 5 有几种方法:

  • get.asp.net/ 获取 ASP.NET 5 安装 ASP.NET 5

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

这个选项稍微简单一些,因为你不需要搜索和安装。

以下为详细步骤:

  1. 通过选择 文件 | 新建 | 项目 或使用快捷键 Ctrl + Shift + N 创建一个新的项目:安装 ASP.NET 5

  2. 选择 ASP.NET Web 应用程序 并输入项目名称,然后点击 确定安装 ASP.NET 5

  3. 将出现以下窗口以选择模板。选择以下截图所示的 获取 ASP.NET 5 RC 选项:安装 ASP.NET 5

  4. 当你在前面的屏幕上点击 确定 时,将出现以下窗口:安装 ASP.NET 5

  5. 当你在前面的对话框中点击 运行保存 按钮时,将出现以下屏幕,要求进行 ASP.NET 5 设置。选择复选框,我同意许可条款和条件 并点击 安装 按钮:安装 ASP.NET 5

  6. 安装 ASP.NET 5 可能需要几个小时。一旦完成,你将看到以下界面:安装 ASP.NET 5

在安装 ASP.NET 5 RC1 更新 1 的过程中,可能会要求你关闭 Visual Studio。如果要求,请照做。

ASP.NET 5 应用程序中的项目结构

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

ASP.NET 5 应用程序中的项目结构

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

ASP.NET 5 应用程序中的项目结构

基于文件的项目

无论何时你在文件系统中添加文件或文件夹(在 ASP.NET 5 项目文件夹内),更改将自动反映在你的应用程序中。

支持 .NET 和 .NET Core 全功能

你可能已经注意到了前面项目中的几个引用:DNX 4.5.1DNX Core 5.0DNX 4.5.1 提供了完整 .NET 的功能,而 DNX Core 5.0 仅支持核心功能,如果你要在跨平台(如 Apple OS X、Linux)上部署应用程序,则会使用这些功能。在下一章中,将解释在 Linux 机器上开发和部署 ASP.NET Core 应用程序的过程。

Project.json 包

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

控制器

这个文件夹包含了你所有的控制器文件。控制器负责处理请求、传递模型和生成视图。

模型

所有代表领域数据的类都将存在于这个文件夹中。

视图

视图是包含前端组件的文件,并展示给应用程序的最终用户。这个文件夹包含了你所有的 Razor 视图文件。

迁移

任何数据库相关的迁移都将在这个文件夹中可用。数据库迁移是包含通过 Entity Framework(一个 ORM 框架)执行的任何数据库更改历史的 C# 文件。这将在稍后的章节中详细解释。

wwwroot 文件夹

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

其他文件

appsettings.json 文件是配置文件,你可以在这里配置应用程序级别的设置。BowernpmNode 包管理器)和 gulpfile.js 是 ASP.NET 5 应用程序支持的客户端技术。

概述

在本章中,你学习了 Visual Studio 的功能。提供了安装 Visual Studio Community 版本的逐步说明,该版本免费提供给个人开发者。我们还讨论了 ASP.NET 5 应用程序的新项目结构以及与之前版本的差异。

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

第三章。控制器

如第一章所述,所有 Web 应用程序都从服务器接收请求并生成响应,该响应被发送回最终用户。在 ASP.NET MVC 中,控制器负责接收请求并根据输入数据生成输出。

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

  • 控制器在 ASP.NET MVC 应用程序中的作用

  • 路由简介和概念

  • 创建您的第一个 ASP.NET 5 应用程序

  • 在您的应用程序中安装 ASP.NET Core NuGet

  • 创建您的第一个控制器和action方法,该方法返回一个简单的Hello World

  • 添加视图并做出允许控制器使用该视图的更改

  • 添加模型并将模型数据传递给视图

控制器在 ASP.NET MVC 应用程序中的作用

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

控制器在 ASP.NET MVC 应用程序中的作用

当用户访问 ASP.NET Core 应用程序时,以下是在高层次上发生的事件序列:

  1. 用户在浏览器中输入 URL。

  2. 根据 URL 模式,路由引擎选择适当的控制器。

  3. 控制器通过其操作方法与模型通信,以获取任何相关数据。操作方法是在controller类中的方法。

  4. 控制器随后将数据传递给视图,以便以可查看的格式呈现,通常为 HTML 元素。

  5. 视图最终交付给用户,用户将在其浏览器中查看。

在讨论控制器之前,让我们讨论路由概念的基本原理,因为路由引擎仅在运行时选择适当的controlleraction方法。

路由简介

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

按照惯例,ASP.NET MVC 遵循以下模式:控制器/操作/标识符

如果用户输入 URL http://yourwebsite.com/Hello/Greeting/1,路由引擎将选择Hello 控制器类中的Greeting 操作方法,并将Id值作为1传递。您可以给一些参数设置默认值,并使一些参数为可选。

以下是一个示例配置:

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

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

  • 使用路由模式controller/action/id

  • 如果在 URL 中没有提供controlleraction的值,则分别使用默认值HelloGreeting

  • Id参数设置为可选,这样 URL 就不需要包含此信息。如果 URL 包含此Id信息,则将使用它。否则,Id信息将不会传递给action方法。

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

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

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

当传递前面的 URL 时,以下action方法将被路由处理程序选中:

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

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

理由:由于 URL 中第一个参数包含Hello,因此将选择Hello控制器。并且由于 URL 中第二个参数包含Greeting2,因此将选择Greeting2操作方法。请注意,只有在 URL 中没有值的情况下,才会选择配置中提到的默认值。由于id参数是可选的,并且 URL 中没有提供id的值,因此不会将任何值传递给id参数。

当传递前面的 URL 时,以下操作方法Greeting2将被路由处理程序选中:

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

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

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

理由:由于第一个参数传递了Hello2,因此将选择Hello2控制器,并且由于第二个参数传递了Greeting2,因此选择了Greeting2作为操作方法。由于id参数是可选的,并且没有为id参数传递值,因此不会为id传递任何值。

当传递前面的 URL 时,以下action方法将被路由处理程序选中:

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

理由:Hello3是作为第一个参数提到的控制器,Greeting4是操作方法,而1是作为id传递的值。

当传递前面的 URL 时,以下action方法将被路由处理程序选中:

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

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

一旦请求到达控制器,控制器将通过与模型通信来创建响应,并且可能将数据传递给视图,然后视图将被渲染给最终用户。

创建 ASP.NET 5 应用程序

是时候动手实践了。让我们创建一个简单的 ASP.NET 5 应用程序。启动 Visual Studio 并按照以下步骤操作:

  1. 通过在 Visual Studio 中选择文件 | 新建项目来创建一个项目。第一个选项是创建 ASP.NET Web 应用程序的早期版本。第二个选项是使用.NET Core 框架创建 ASP.NET Core 应用程序。第三个选项是使用.NET 框架创建 ASP.NET Core 应用程序。第二个和第三个选项之间的区别在于,.NET 框架支持所有现有.NET 框架的功能,而.NET Core 只支持核心功能。使用.NET Core 库的优势在于它可以在任何平台上部署。创建 ASP.NET 5 应用程序

  2. 从 ASP.NET 5 模板列表中选择Empty模板。第二个选项是用于创建 Web API 应用程序(用于构建基于 HTTP 的服务),第三个选项是用于创建包含一些基本功能的 Web 应用程序,你可以直接运行它,无需编写任何代码。创建 ASP.NET 5 应用程序

  3. 一旦你在如图所示的窗口中点击OK(在选择了 Empty 模板选项之后),就会创建一个解决方案,如图所示:创建 ASP.NET 5 应用程序

  4. 当你运行应用程序(通过按F5键)而不做任何更改时,你的屏幕上会显示简单的Hello World!文本,如图所示:创建 ASP.NET 5 应用程序

在这个新创建的应用程序中,我们还没有编写任何代码。那么,你有没有想过它是如何显示文本Hello World!的?

答案在于Startup.cs文件,该文件包含一个名为Startup的类。这个类包含Main方法,它是 Web 应用程序的入口点。如果你使用过任何之前的 ASP.NET MVC 版本,或者甚至 ASP.NET Web Forms,情况就不会是这样了。

ASP.NET 5 运行时调用ConfigureServicesConfigure方法。例如,如果你想配置任何服务,你可以在这里添加。任何针对应用程序的定制配置都可以添加到这个Configure方法中:

public void ConfigureServices(IServiceCollection services) { 

} 

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

} 

Configure方法中只有几条语句。第一条语句告诉运行时使用IISPlatformHandler来处理所有传入的 HTTP 请求。现在我们先暂时放下asyncawaitcontext,这些我们稍后再讨论。本质上,第二条语句告诉运行时对于所有传入的请求,无论传入的 URL 是什么,都返回Hello World!

当你在浏览器中输入 URL http://localhost:50140/Hello 时,它仍然会返回相同的Hello World!

这就是为什么当我们运行应用程序时,我们得到了Hello World!

由于我们在创建 ASP.NET 5 应用程序时选择了 Empty 模板,因此没有任何组件被安装。即使选择 Empty 模板(如我们所做的那样),默认情况下也不会安装 MVC。

您可以通过打开 project.json 文件来确认,您可以在依赖项列表中看到没有提到 ASP.NET MVC:

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

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

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

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

  1. 右键单击项目,并选择 管理 NuGet 包 选项:在您的应用程序中安装 ASP.NET Core NuGet 包

  2. 选择 包含预发布 复选框,以便 NuGet 包管理器 列出所有预发布包。搜索 MVC,您将得到 Microsoft.AspNet.MVC 包,如以下结果所示,然后点击右侧的 安装 按钮:在您的应用程序中安装 ASP.NET Core NuGet 包

  3. 查看更改:在您的应用程序中安装 ASP.NET Core NuGet 包

  4. 一旦您点击 查看更改,将出现以下对话框,您需要接受许可条款:在您的应用程序中安装 ASP.NET Core NuGet 包

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

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

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

或者,您也可以通过更新 project.json 文件来添加 NuGet 包及其版本信息。NuGet 包管理器将自动下载并安装它们。

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

这需要对 Startup.cs 文件进行一些更改:

  1. 配置应用程序以添加 MVC 服务。这可以通过向 Startup 类的 ConfigureServices 方法中添加以下行来完成:

          services.AddMvc(); 
    
    
  2. 配置路由,以便根据输入的 URL 选取正确的控制器来处理传入的请求。以下代码片段需要更新在 Startup.cs 文件的 Configure 方法中:app.UseMvc(routes => {

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

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

在本章以及本书中的大多数章节中,我们将手动编写代码或选择 模板,而不是依赖脚手架模板。对于那些对术语 脚手架 感到陌生的人来说,脚手架是一个功能,它会为你生成所选项目(例如,控制器)所需的所有必要样板代码,而不是你需要编写一切。虽然我同意脚手架模板很有用并且可以节省生成样板代码的时间,但它们隐藏了许多初学者必须理解的具体细节。一旦你手动编写代码,你就会了解每个组件如何为整体画面做出贡献的所有复杂性。一旦你在基础知识上变得强大,你就可以使用脚手架模板来节省你在编写样板代码上的时间。

我们的第一个控制器

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

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

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

我们的第一个控制器

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

我们的第一个控制器

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

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

所有控制器,无论是 MVC 还是 Web API 控制器,都继承自 Controller 基类。在 ASP.NET MVC 的早期版本中,MVC 控制器会继承自 Controller 类,而 Web API 控制器会继承自 APIController 类。

在前面的 HomeController 类中,我们有一个名为 Index 的单个操作方法,它返回相应的视图。当你以当前状态运行应用程序时,你会得到一个 500 内部服务器错误。原因是 HomeControllerIndex 操作没有创建视图,而 ASP.NET Core 尝试搜索该视图。由于视图不可用,它返回一个 500 内部服务器错误

我们不创建并返回该视图,而是对这个操作方法进行简单的修改。让我们返回一个字符串,Hello World! 我正在学习 MVC 6!,并更改 IActionResult 的返回类型:

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

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

我们的第一个控制器

哇!我们已经将 ASP.NET Core 应用程序更改为渲染自定义内容而不是无聊的 Hello World。我们所做的可能看起来是一个微小的改进,但我们已经在我们的 ASP.NET Core 应用程序中使用了控制器和动作方法,这为 Web 应用程序开发带来了很多结构和灵活性。

我们的第一个控制器

以下是我们运行应用程序时发生的步骤序列:

  1. 应用程序在 URL http://localhost:50140 上运行,其中 50140 是 IIS Express 在我的本地系统上运行应用程序所选的端口号。这个数字可能不同。

  2. 由于我们没有传递任何参数,Controlleraction 方法的默认值将被选择。在我们的情况下,HomeController 将被选为 ControllerIndex 将被选为 HomeController 中的 action 方法。由于 ID 是可选值且未传递,此 ID 参数将被忽略。

  3. 在路由引擎选择 Controlleraction 方法之后,控制权传递给所选控制器的 action 方法。在我们的情况下,它将是 HomeControllerIndex 动作方法。

  4. Index 动作方法中,我们返回一个字符串,Hello World! I am learning ASP.Net MVC 6!。此文本从控制器返回,然后返回给用户。

IActionResult

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

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

在前面的示例中,我将 return 类型更改为字符串以使事情简单。现在,让我们通过保持 return 类型(IActionResult)不变来返回字符串:

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

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

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

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

  • ContentResult : 可以返回文本结果。

  • EmptyResult : 返回一个 null 结果。

  • FileResult : 返回二进制输出以写入响应。

  • HttpStatusCodeResult : 提供了一种返回方式。

  • JavaScriptResult : 返回可以在客户端执行的脚本。

  • JSonResult : 当你返回一个序列化的 JSON 对象时。

  • RedirectResult : 重定向到另一个 action 方法。

  • RedirectToRouteResult:表示通过使用指定的路由值字典执行重定向的结果。

添加视图

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

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

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

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

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

添加视图

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

添加视图

当你在前面的屏幕上点击添加按钮时,将创建一个名为Index2.cshtml的文件,内容如下:

添加视图

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

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

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

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

添加视图

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

添加视图

读累了记得休息一会哦~

公众号:古德猫宁李

  • 电子书搜索下载

  • 书单分享

  • 书友学习交流

网站:沉金书屋 https://www.chenjin5.com

  • 电子书搜索下载

  • 电子书打包资源分享

  • 学习资源分享

添加模型

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

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

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

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

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

添加以下代码片段。在 @ 符号之后的所有内容都被认为是 Razor 代码。在以下代码中,我们正在尝试访问传递给我们的视图的 Model 对象的属性。在我们的例子中,Model 代表我们在 action 方法中构建的 employee 对象。您可以使用 Model 关键字从视图中访问该对象:

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

当你运行应用程序并输入 URL http://localhost:50140/Home/Employee 时,你会看到以下输出:

添加模型

从控制器到视图传递数据

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

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

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

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

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

  return View(emp1); 
} 

也要在视图文件中进行相应的更改,以便我们可以显示 Company 名称和 Company location 值:

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

在进行前面的更改后运行应用程序:

从控制器到视图传递数据

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

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

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

尽管我在 Controller 中使用 ViewBag 存储了 Company 值,但我仍然使用 ViewData 访问相同的值。对于 Company Location 值也是如此,我们在 Controller 中使用 ViewData 存储了值,但我们使用 ViewBag 访问该值。

在进行前面的更改后运行应用程序,你会看到之前看到的结果。

过滤器

ASP.NET MVC 中的过滤器允许你在执行管道的特定阶段之前或之后运行代码。它们可以按控制器或按操作全局配置。

有不同类型的过滤器,每个过滤器在管道的不同阶段执行。例如,当 action 方法执行时,会执行动作过滤器。

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

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

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

当我第一次运行它时,它显示的时间与预期相符。但是当你刷新浏览器(这间接地再次触发了请求),时间没有更新,因为响应已经被应用程序缓存。在下面的屏幕截图中,尽管时间是 7:43,但应用程序仍然显示为 7:40:

过滤器

以下是在 ASP.NET Core 中可用的预定义过滤器类型。

授权过滤器

这些用于授权,主要目的是确定当前用户是否有权进行所提出的请求。

资源过滤器

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

动作过滤器

这些包装对单个 action 方法调用的调用,并且可以操作传递给 action 的参数以及从它返回的动作结果。

异常过滤器

这些用于管理 ASP.NET MVC 中的未处理异常。

结果过滤器

这些包装单个动作结果,并且只有在 action 方法成功执行时才会运行。

摘要

在本章中,我们从零开始构建了我们的第一个 ASP.NET 5 应用程序,并在我们的 ASP.NET 5 应用程序中安装了 ASP.NET Core。我们学习了控制器如何融入整体的 ASP.NET MVC 应用程序,并学习了如何使用 action 方法构建您的第一个控制器。我们还学习了如何在控制器中使用模型和视图。我们还讨论了使用 ViewBagViewData 将数据从控制器传递到视图的不同方法。我们还学习了 ASP.NET MVC 中的过滤器以及如何在 ASP.NET Core 中使用预定义的过滤器。

第四章:视图

视图是应用程序的实际输出,它被发送给用户。这是用户从屏幕访问你的应用程序时实际看到的内容。所有组件,无论是菜单、输入元素、对话框还是用户看到的一切,都只来自你的视图。如果你在访问应用程序时没有提供良好的用户体验,用户将不会关心你的应用程序有多好。因此,在构建 ASP.NET MVC 应用程序时,视图发挥着至关重要的作用。

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

  • 视图引擎和 Razor 视图引擎的目的

  • 在 Razor 视图引擎中编程和不同的编程结构

  • ASP.NET Core 中的布局及其功能

  • HTML 辅助器

  • 部分视图

  • 标签辅助器

视图引擎和 Razor 视图引擎

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

Razor 视图引擎

Razor 视图引擎是 ASP.NET Core 中的默认和推荐视图引擎,并且从现在开始,这可能是当你安装 ASP.NET MVC 时随附的唯一视图引擎。

你可以在你的 Razor 视图中混合 C# 代码和 HTML 代码,Razor 视图引擎足够智能,可以区分这两者并生成预期的输出。在某些情况下,我们可能需要向 Razor 视图提供额外的信息以生成适当的结果。Razor 代码块以 @ 符号开始,不需要关闭的 @ 符号。

在 Razor 视图引擎中编程

在 Razor 视图引擎中编程就像你在 C# 中编程一样。区别在于,在 Razor 视图引擎中,你的 C# 代码将与 HTML 混合以生成所需的 HTML 输出。

Razor 视图中的变量

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

注意

对于本章中的所有示例,我们只将展示视图的代码样本。

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

  1. 创建一个名为 Controllers 的文件夹和一个名为 HomeController 的控制器。

  2. 通过右键单击上下文菜单并选择 添加 | 新建项,然后从列表中选择 MVC Razor 视图,创建一个名为 Views 的文件夹、一个名为 Home 的子文件夹和一个名为 Index.cshtml 的视图文件。

HomeController.cs 文件将包含以下代码:

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

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

我们将专注于粗体的行。然后,我们将使用@ { … } 创建一个 Razor 代码块,并在其中声明一个变量。Razor 代码块以闭合的大括号结束。Value:片段被视为简单的 HTML 文本。由于我们希望使用 Razor 变量的值,我们将使用@i来指示 Razor 视图引擎,i不是一个普通的 HTML 文本;它是一个 Razor 构造,应相应地处理。完整的 HTML 代码如下:

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

 @{

 int i = 5; 

 } 

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

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

Razor 视图中的变量

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

以下截图是当你不使用@符号访问变量时将得到的结果:

Razor 视图中的变量

for 循环

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

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

以下是一些需要注意的点:

  • 由于 for 循环是 Razor 代码,我们应该用@符号包围循环,以指示随后的代码是 Razor 代码而不是普通 HTML。

  • 无论何时我们使用 HTML 元素或标签,Razor 视图引擎都会回退到 HTML 模式。如果你想在 HTML 标签内使用任何 Razor 表达式,你将需要再次包含@符号,以告诉 Razor 视图引擎随后的内容是 Razor 代码而不是 HTML 元素。这就是为什么我们在前面的表达式中再次使用@符号,即使在父级根级 Razor 代码内部。

视图的完整代码如下:

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

while 循环

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

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

 i++; 

   }
 }

foreach 循环

Razor 视图中的foreach循环与 C#中的foreach循环相同。在以下代码中,我们将初始化一个整数列表,遍历列表并将其作为列表项打印出来:

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

if 条件

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

这个text标签是为了指示 Razor 视图引擎,text标签之后的内容应被视为文本,而不是 Razor 表达式:

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

布局

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

考虑以下网页结构,其中 顶部部分 包含公司标志或横幅,而 侧边部分 包含到网站各个部分的链接。内容部分会因页面而异。

布局

如果我们在单个视图中编写完整的内容,我们可能需要在每个页面上重复 顶部部分侧边部分。如果我们想更改 侧边部分 中的任何内容,我们可能需要更改所有文件。这清楚地表明,单个视图文件并不是最佳解决方案。

在这种情况下,布局就派上用场了。布局定义了可以在所有网页中重用的网站结构。布局甚至不需要像顶部部分或侧边部分这样的内容;它可以包含一个简单的 HTML 结构,其中可以包含常见内容,而主体内容将由单个视图渲染。

让我们构建我们的第一个布局。为了使用布局,你需要以下三个东西:

  1. 通知布局文件的名称——这个信息应该在 _ViewStart.cshtml 中提供。按照惯例,所有共享文件的名称都将以一个下划线开头,并且此文件位于 Views 文件夹的底部。

  2. 创建布局文件——按照惯例,文件名为 _Layout.cshtml,它将位于 Shared 文件夹中。所有共享内容,例如部分视图,也将在此处可用。部分视图将在本章后面讨论。

  3. 创建内容视图文件——这个视图文件几乎与之前创建的视图文件相同,只有一个区别;只有页面特定的内容将在此文件中可用,这意味着你将不会在此处有任何 htmlheadtitle 标签。布局

在创建 _ViewStart.cshtml_Layout.cshtml 和页面特定的视图文件后,文件夹结构将如前所述。

创建 _ViewStart.cshtml

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

创建 _ViewStart.cshtml

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

@{
   Layout = "_Layout";
 }

创建 _Layout.cshtml

Views 文件夹中创建一个名为 Shared 的文件夹。然后,右键单击 Shared 文件夹,从 上下文 菜单中选择 添加新项,如图所示:

创建 _Layout.cshtml

当你点击 添加 按钮时,它将创建包含以下内容的 _Layout.cshtml

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

@RenderBody() 

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

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

添加页面特定的视图

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

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

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

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

现在,右键单击 Views 文件夹,选择 添加 | 新建项,选择 MVC 视图页面,并将文件保存为 Index2.cshtml

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

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

现在一切都已经设置好了。运行应用程序,并在浏览器中输入 URL http://localhost:50132/Home/Index2。请注意,当您从您的电脑运行应用程序时,本地主机后面的端口号可能会变化。

添加页面特定的视图

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

通过按 Ctrl + U(在 Windows 的 Chrome 浏览器中)查看源代码。您将看到以下 HTML 内容:

添加页面特定的视图

顶部内容(htmlheadbodydiv 开放标签)和底部内容(htmlheadbodydiv 关闭标签)来自布局文件,文本来自特定于页面的视图。

生成 HTML

如 第一章 中所述,ASP.NET Core 简介,浏览器只能理解 HTML、CSS 和 JavaScript,无论您使用什么技术构建 Web 应用程序。在 ASP.NET MVC 中构建应用程序时也是如此。

大多数应用程序获取用户输入,处理输入,然后将所需信息存储在数据库中以供以后检索。在 Web 应用程序的情况下,表单 HTML 元素用于获取用户输入。

以下是在 ASP.NET Core 中生成 HTML 元素的一些方法:

  • HTML 辅助器

  • 标签辅助器

HTML 辅助器是服务器端方法,有助于生成浏览器可以理解的 HTML 元素。HTML 辅助器是生成 HTML 元素的主要方法,直到 ASP.NET MVC 5。

ASP.NET Core 中引入的标签辅助器也会生成 HTML 元素。我们将在本章的后续部分讨论标签辅助器,它们将看起来像 HTML 元素,其中您添加属性来识别它们为标签辅助器。使用标签辅助器而不是 HTML 辅助器的优点是用户界面设计师/工程师不需要担心 Razor 代码。他们只需使用 HTML 元素和额外的属性进行编码。

在讨论 HTML 辅助器和标签辅助器之前,让我们退一步,谈谈为什么我们最初需要它们。

让我们考虑一个简单的表单,如下面的图片所示,我们希望获取用户的姓名和年龄。如果用户输入了她的年龄,我们将显示 您有资格投票! 。如果没有,我们将显示 您现在没有资格投票

生成 HTML

以下是将显示前面简单表单的 HTML 代码:

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

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

HTML 助手

HTML 助手是服务器端方法,为您生成 HTML。我们可以使用 HTML 助手生成相同的表单,如下所示(HTML.BeginForm@Html.Label@Html.TextBox 分别生成 HTML form 元素、标签和文本框元素):

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

HTML 助手

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

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

HTML 助手

Person 类只是一个 POCO(Plain Old C# Object)类,将充当模型。此类的完整代码如下:

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

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

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

在视图中,使用以下 HTML 助手创建 form

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

在第一行,我们告诉视图我们正在传递 Person 类型的模型。这使您能够使用模型的强类型,即当您键入 Model 并加一个点时,IntelliSense 会为您提供 Person 类的所有属性

在第二行,我们使用的是重载的 BeginForm HTML 助手,它接受三个参数——动作方法名称、控制器名称和 Form 方法。

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

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

现在,让我们以相同的方式编写相应的POST操作方法。在以下POST操作方法中,根据表单中输入的年龄,我们将动态属性设置为Message

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

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

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

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

当您运行应用程序时,您将得到以下输出:

HTML 助手

部分视图

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

考虑以下网页的结构——这是我们之前使用的相同布局页面,但有一些变化。最新新闻块被添加到侧边栏,而登录块被添加到顶部区域。这些块不受顶部区域侧边栏的限制,可以在应用程序的任何地方使用,包括以下图中的内容区域

部分视图

这些部分视图不仅限于静态内容,还可以包含form元素。在前面的截图中,最新新闻部分视图包含文本内容,而登录部分视图包含用于获取电子邮件 ID 和密码的form元素。

部分视图的位置——框架不限制部分视图的位置。然而,按照惯例,如果你的部分视图只由你的控制器使用,你可以在控制器特定的视图文件夹中创建该部分视图。例如,如果你的部分视图只会在HomeController文件中使用,你可以在Views\Home文件夹中创建该部分视图。

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

如前所述,部分视图就像一个普通视图。因此,我们将以创建普通视图相同的方式创建部分视图。

右键单击Shared文件夹,然后选择添加 | 新建项。按照惯例,就像所有共享内容一样,部分视图的名称也将以"_"(下划线)开头,如下面的截图所示:

部分视图

小贴士

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

在生成的部分视图中,我添加了以下简单的静态内容——一段文本和一个简单的表格:

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

调用部分视图

可以使用@Html.Partial HTML 助手调用部分视图。

在我们的案例中,我们将从Index2.cshtml文件中调用部分视图。你传递的参数将是部分文件的名称。它将根据该名称搜索部分视图,并将该完整内容作为Index2.cshtml文件的一部分进行渲染。

Index2.html文件的现在内容如下:

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

@Html.Partial("_PartialHelloWorld")

现在,运行应用程序并访问 URL http://localhost:50132/Home/Index2。你将看到以下输出:

调用部分视图

视图组件

视图组件是 ASP.NET Core 中引入的新功能,它们几乎与部分视图相似,但功能更强大。当你使用部分视图时,你对 Controller 有依赖。然而,当你使用ViewComponent属性时,你不需要依赖 Controller,因此我们将建立关注点的分离,并具有更好的可测试性。尽管现有的部分视图 HTML 辅助器仍然受支持,但在使用.NET Core 时,你想要显示可重用信息时,更倾向于使用视图组件。

创建视图组件

你可以使用以下任何一种方式创建ViewComponent

  • 通过从ViewComponent属性派生创建一个类

  • 使用[ViewComponent]属性装饰一个类,或者从具有[ViewComponent]属性的类派生

  • 你可以通过创建一个以ViewComponent属性后缀结尾的类来使用约定。

无论你选择哪种选项,这个ViewComponent都应该是一个公共的、非嵌套的、非抽象的类。

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

有一个名为Invoke(或InvokeAyncInvoke的异步等效方法),它将返回IComponentViewResult接口。这个方法类似于将返回视图的 Controller 的动作方法。

让我们亲自动手创建一个ViewComponent属性。

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

创建视图组件

我们创建的SimpleViewComponent文件看起来如下:

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

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

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

我们只有几个方法,一个用于填充数据,另一个是Invoke方法,我们将在这个方法中渲染视图。

一旦你创建了ViewComponent属性,你需要在Views_ViewImports.cshtml文件中包含ViewComponent命名空间,以便ViewComponents属性对所有视图可用。以下突出显示的代码片段被添加到视图中:

@using Chapter4
@using Chapter4.Models

@using Chapter4.ViewComponents 

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

我们已经创建了ViewComponent并将其提供给所有视图。HomeController文件中的一个简单的动作方法只是返回视图:

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

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

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

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

  • Views\<controller_name>\Components\<view component name>\<view name>文件夹

  • Views\Shared\Components\<view_component_name>/<view_name>文件夹

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

创建视图组件

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

@model IEnumerable<string>

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

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

创建视图组件

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

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

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

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

创建视图组件

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

标签助手

标签助手是 ASP.NET Core 中的新功能;它们帮助生成 HTML 元素。在 HTML 助手中,我们将编写 C#/Razor 代码来生成 HTML。与此方法相关的不利之处在于,许多前端工程师可能不知道 C#/Razor 代码。他们使用纯 HTML、CSS 和 JavaScript。标签助手看起来就像 HTML 代码,但具有所有服务器端渲染的功能。您甚至可以根据需要构建自己的自定义标签助手。

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

通过选择视图 | 其他窗口 | 包管理控制台来打开包管理控制台窗口,如图所示:

标签助手

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


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

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

标签助手

一旦安装了TagHelpers包,我们需要调用ViewImports文件,在那里我们将添加TagHelpers指令,以便标签助手可以供我们的视图使用。

右键单击Views文件夹,从上下文菜单中选择添加新项选项;你会看到以下屏幕:

Tag Helper

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

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

正如以下屏幕截图所示,我们直接在Views文件夹下创建_ViewImports.cshtml文件,因此 Tag Helper 将对所有视图可用。

Tag Helper

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

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

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

使用以下代码添加Index3操作方法的对应视图(Index3.cshtml文件):

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

="Name"></
label

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

="Age"></
label

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

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

>

在前面的代码中,以下是一些需要注意的事项,以便使用 Tag Helper:

  • 所有表单元素看起来就像标准的 HTML 元素一样,只是在属性上做了一些小的改动。这使得前端开发者可以独立工作,无需学习 HTML/Razor 代码,从而更容易实现关注点的分离。

  • 前面的视图的第一行指示从控制器传递到视图的模型数据类型。

  • 表单元素有两个属性名为asp-controllerasp-action,分别代表控制器名称和操作方法名称。

  • 标签和输入标签辅助器就像 HTML 元素一样,只是多了一个asp-for属性。这些属性的值代表模型属性。在输入这些属性的值时,你可以利用 IntelliSense。

创建自定义 Tag Helper

ASP.NET Core 提供了许多内置的 Tag Helper 来帮助你在多种场景下创建必要的 HTML 元素。然而,这个过程并不全面和详尽。有时,你可能想要对生成的 HTML 元素进行一些修改,或者你可能想要创建具有新属性或全新的 HTML 元素。你不仅限于在 ASP.NET Core 应用程序中使用现有的 Tag Helper。如果你现有的 Tag Helper 不能满足你的需求,你可以创建自己的 Tag Helper。让我们创建一个简单的 Tag Helper 来创建一个电子邮件链接:

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

有几种方法可以创建实现 ITagHelper 接口或继承 TagHelper 类的 Tag Helper。TagHelper 类有一个可以重写的 Process 方法,你可以用它来编写自定义 Tag Helper。TagHelper 类还有一个 TagHelperOutput 参数,你可以用它来编写和生成所需的输出 HTML。因此,通过从 TagHelper 类继承来创建 Tag Helper 是更可取的。

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

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

创建 ASP.NET Core 应用程序中的自定义 Tag Helper 需要执行以下步骤。

创建一个名为 TagHelper 的文件夹,并添加一个名为 EmailTagHelper.cs 的新项。按照惯例,所有 Tag Helper 类都应该以 TagHelper 结尾,即使我们可以覆盖这个惯例。

创建自定义 Tag Helper

创建文件后,你需要重写 Process 方法以生成所需的 HTML 输出:

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

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

前面代码中使用的参数解释如下:

  • context 参数将提供你在 Tag Helper 中提供的所有信息。例如,在 <email mailTo="mugil@greatestretailstore.com"></email> Tag Helper 中,你可以从 context 参数中获取 mailTo 属性及其相关值。在前面 Process 方法的第一行中,我们将获取 mailTo 属性值并使用该值在生成的 HTML(锚点标签)中创建一个属性。

  • output 参数是 TagHelperOutput 类型,用于生成所需的 HTML 输出。

  • output.Content.SetContent 参数将设置要显示在锚点标签中的文本。

我们已经创建了电子邮件 Tag Helper。现在,我们必须使其可用于我们的视图,以便我们可以在视图中使用该 Tag Helper。编辑 Views_ViewImports.cshtml 以包含 TagHelpers 的命名空间并添加相关的 TagHelpers。在以下 _ViewImports.cshtml 文件中,我们已添加了加粗的内容:

@using Chapter4
@using Chapter4.Models

@using Chapter4.TagHelpers 

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

@addTagHelper "*, Chapter4"

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


@addTagHelper "*, Chapter4"

你只能指定 TagHelpers,例如,以下行将仅包含 EmailTagHelper,因此它可用于我们的视图:


@addTagHelper "Chapter4.TagHelpers.EmailTagHelper, Chapter4" 

让我们在 Home 控制器中创建一个简单的操作方法。在相关操作方法的视图中,我们将使用电子邮件 Tag Helper:

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

下面的代码是前面 AboutUs 操作方法的视图:

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

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

当你运行应用程序并访问 http://localhost:50132/Home/AboutUs URL 时,你将看到以下输出:

创建自定义 Tag Helper

在这里,我们创建了一个带有 mailto 属性的锚点标签,并将电子邮件值作为 href 属性值。

我已经打开了开发者工具窗口(按F12键进行此操作并选择DOM 资源管理器标签页)来查看生成的 HTML。

摘要

在本章中,你学习了什么是视图引擎以及如何使用 Razor 视图引擎构建视图。我们还讨论了在 Razor 中可以使用的不同编程结构,以生成所需的 HTML 输出。然后,你了解了布局以及如何在你的 ASP.NET MVC 应用程序的所有页面中提供一致的网站结构。稍后,我们通过一个示例讨论了如何使用部分视图来提高可重用性。最后,你学习了如何使用标签助手生成干净的 HTML。

读累了记得休息一会哦~

公众号:古德猫宁李

  • 电子书搜索下载

  • 书单分享

  • 书友学习交流

网站:沉金书屋 https://www.chenjin5.com

  • 电子书搜索下载

  • 电子书打包资源分享

  • 学习资源分享

第五章。模型

数据是每个应用程序的核心。用户将数据输入到应用程序中,编辑输入的数据,并搜索数据。我们甚至可以说,我们构建的应用程序只是我们对应用程序数据进行操作的一个接口。因此,任何框架都绝对有必要提供一个机制来简化并更易于管理数据操作。ASP.NET MVC 中的模型用于表示业务领域数据。

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

  • 模型和它们的目的

  • 在 ASP.NET MVC 应用程序的控制器和视图中创建一个简单的模型并使用它

  • 创建特定于视图模型的模型

  • 在模型和 ViewModels 的上下文中,ASP.NET MVC 应用程序中的数据流

  • Entity Framework 的目的及其特性和优势

  • 使用 Entity Framework 添加、更新和删除数据

  • 在 ASP.NET MVC 应用程序中使用 Entity Framework

模型

模型是简单的 POCO ( Plain Old C# Objects ) 类,代表你的业务领域数据。对于一个电子商务业务,模型类会是 ProductOrderInventory。如果你正在为大学构建应用程序,模型类会是 StudentTeacherSubject。模型代表应用程序中的业务领域数据,并且它们不了解应用程序中使用的底层数据库。实际上,你甚至不需要数据库就可以与模型一起工作。

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

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

  1. 确保创建一个带有空模板的 ASP.NET 5 应用程序。安装 ASP.NET Core 的 NuGet 包,并按照前面章节中讨论的进行配置。

  2. 创建一个 Controllers 文件夹,并创建一个具有单个 Index 动作方法的 HomeController

  3. Views 模型创建以下文件夹/文件:

    • Views : 此文件夹位于你的项目中。

    • Views_ViewStart.cshtml : 此文件标识了 Layout 文件的名称。

    • Views\Shared 文件夹:此文件夹包含你的应用程序的所有共享视图组件。

    • Shared_Layout.cshtml : 此文件定义了你的 Web 应用程序结构应如何看起来。

    • Views\Home 文件夹:此文件夹包含你的 HomeController 的所有视图。

    • Views\Home\Index.cshtml : 这是与 HomeControllerIndex 动作方法相对应的视图。

现在,我们已经创建了一个包含控制器和视图的 ASP.NET Core 应用程序。

让我们在应用程序中创建一个 Models 文件夹;这个文件夹将包含所有你的模型文件。在实际应用中,这个文件夹和相应的模型文件将位于不同的项目中。为了简化,我们将 Models 文件夹及其文件保留在同一个项目中。

模型

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

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

这个 Product 模型类与其他任何 C# 类没有区别,并包含一些关于产品的属性。

更新 HomeController 中的 Index 动作方法,使用 Product 模型,如下面的代码片段所示。我们正在构建模型数据并将模型数据传递给视图,以便将其显示给用户。然而,不推荐在控制器动作方法中构建模型数据,因为这违反了关注点分离原则。为了简化起见,我们在这里的动作方法中构建模型数据。

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

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

@model List<Chapter5.Models.Product> 

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

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

模型

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

让我们创建一个相对复杂的模型类,Order(位于 Models 文件夹中的 Order.cs),它包含产品列表及其总金额:

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

模型

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

突出的粗体代码是我们对操作方法所做的更改:

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

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

  return View(order);

} 

视图已更新以适应模型的变化。模型元数据(@model)已更改,以指示将订单信息传递给视图,而不是产品列表。

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

@model Chapter5.Models.Order 

<table border="1"> 

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

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

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

模型

视图组件特定的模型

有一些场景,您可能只想更新大型模型中的几个属性,或者您可能想基于几个模型创建一个新的模型。在这种情况下,创建一个针对视图的新模型会更好。

例如,让我们假设我们正在构建一个屏幕,您可以在其中更新产品的价格。这个简单的屏幕可能只包含三个属性——产品 ID、产品名称和产品价格。但是,产品的模型可能包含超过 30 个属性来存储产品的所有详细信息,如制造商、颜色、尺寸和其他属性。我们不必发送包含所有属性的完整模型,而是可以创建一个针对此视图的新模型,只包含几个属性——ID、名称和价格。

关于 ViewModel 的说明

ViewModels 是实体,当您更新模型时,您的视图会自动更新,反之亦然。在许多在线文章甚至一些书中,当它们实际上试图指代“针对视图的模型”时,它们在提到ViewModels

在 ViewModel 中,绑定是双向的——当您更新模型或视图时,另一个会自动更新。让我们考虑一个简单的例子;您有一个带有左侧各种字段和右侧打印预览的表单。在这种情况下,您在表单中实时输入的内容将立即反映在右侧。在这种情况下,当您输入时,您可以使用纯 ViewModel,您的 ViewModel 会更新,并且该ViewModel会在右侧的打印预览中被消费。这些纯 ViewModel 正在被用于像KnockoutAngularJS这样的高级 JavaScript 框架中。

针对视图的模型中,我们只从模型到视图进行单向绑定。在这里,我们发送一个针对视图的特定模型,而不是通用的模型(它代表一个业务域类)。

然而,在这本书中,我们将为了简洁起见将针对视图的模型称为ViewModel。除非另有说明,您应该将所有 ViewModel 视为针对视图的模型。因此,我在这里犯了其他作者犯过的同样的错误 关于 ViewModel 的说明

与模型相关的数据流

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

与模型相关的数据流

数据源代表您的应用程序数据。应用程序数据可以存储在任何地方——从完整的 RDBMS(如 SQL 服务器)到简单的 Excel 电子表格,或者两者之间的任何地方。

模型代表您应用程序的业务域数据,并且与所使用的数据源无关。相同的模型可以与不同的数据源一起使用。

我们可以在我们的视图中直接使用模型来获取数据或展示数据。但在某些视图中,你可能不需要模型的所有属性。因此,我们不是将整个模型发送到视图,而是创建特定于视图的模型并使用它们在我们的视图中。这使得事情变得更简单。

当你在 ASP.NET Core 中使用模型存储或检索记录时,以下是一个高级事件序列:

  1. 用户在应用程序中通过表单(使用视图创建)输入数据。表单中的字段不需要代表完整的模型,因为我们只需要模型中的几个属性。

  2. 输入的数据被传递到控制器,在那里发生模型绑定。模型绑定是将视图中的输入数据映射到模型或 ViewModel 的过程。

  3. 如果数据是在 ViewModel 中接收的,那么我们将把 ViewModel 转换为 Model。

  4. 最后,模型数据存储在数据源中。

到目前为止,我们一直在我们的应用程序中处理内存中的数据。在几乎所有的实际应用中,都会使用某种形式的数据库来存储、访问和检索数据。在下一节中,我们将讨论 Entity Framework(对象关系映射框架),它使得从 .NET 应用程序中进行数据访问变得更加简单。

模型绑定

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

让我们考虑一个简单的表单,其中包含几个表单字段——NameEmailID。在表单提交时,这些值将被映射到控制器动作方法的 ViewModel 对象。模型绑定负责这个映射。模型绑定器会在表单字段、查询字符串和请求参数中寻找匹配项。

在前面的例子中,任何具有这些属性的类都会被 ModelBinder 无任何问题地选中。

由于以下 Person 类包含 NameEmailID 属性,模型绑定器不会对使用此模型来映射表单中输入的值提出任何异议:

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

以下代码片段展示了如何在动作方法中使用 Person 类:

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

Entity Framework

Entity Framework 是 对象关系映射 (ORM) 框架,它使开发者能够直接在域特定对象上工作以进行数据访问,而不是在数据库查询上工作。这大大减少了应用程序数据访问层中的代码复杂性。

在讨论 Entity Framework 和其功能之前,让我们暂停一下,思考一下当我们尝试使用 ADO.NET 将一些信息保存到数据库时我们遵循的步骤:

  1. 构造业务域对象。

  2. 创建到你的数据库的连接。

  3. 打开连接。

  4. 创建一个命令对象以及命令类型。

  5. 将你的业务域对象的属性添加到命令对象的参数中。

  6. 执行将数据保存到数据库中的命令。

我们必须遵循之前提到的六个步骤来进行常见操作,例如将数据保存到数据库中。

如果你使用 ORM 框架,例如 Entity Framework,你只需要三个步骤:

  1. 构建业务域对象。

  2. 为你的业务域对象创建DbContext类。DbContext类的实例代表了与数据库的会话。

  3. 使用DBContext类的实例将数据保存到数据库中。

你可能会想知道这是如何实现的。

实际上,在后台,Entity Framework 会创建与数据库的连接并执行查询,将业务域对象保存到数据库中。为了简化,Entity Framework 为你编写所有数据访问代码,这样你就可以专注于实现应用程序的业务功能,而不是编写数据库层代码。

Entity Framework 独立于 ASP.NET MVC

如前所述,Entity Framework 是一个用于访问数据的 ORM 框架,并且与 ASP.NET MVC 独立。Entity Framework 可以用于Windows Communication Foundation ( WCF )服务、Web API 服务和甚至控制台应用程序。你可以在任何类型的应用程序中使用 Entity Framework,并利用它使用对象来访问数据。无论你使用哪种类型的应用程序,Entity Framework 的概念和功能都保持不变。

现在,我们将使用控制台应用程序与 Entity Framework 结合使用。这使我们能够专注于手头的任务,并展示 Entity Framework 的功能,而不是编写 ASP.NET Core 应用程序的样板代码。在本章的后续部分,我们将集成 Entity Framework 与 ASP.NET Core 应用程序。

SQL Server 的 Entity Framework 最新版本是 7.0.0,在撰写本书时仍处于测试版。与之前的版本(Entity Framework 6)相比,EF7(Entity Framework 7)带来了重大变化。然而,EF7 是构建 ASP.NET 5 应用程序时的推荐版本,因此我们将在这本书中使用这个版本。

注意

我们需要一个数据库来解释 Entity Framework 的许多功能。请在继续之前在你的 PC 上安装 SQL Server 2014 Express。安装 SQL Server 2014 Express 和 SQL Server Management Studio 的逐步说明在附录 A中给出。

使用 Entity Framework 创建控制台应用程序

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

  1. 选择文件 | 新建项目,然后选择控制台应用程序

  2. 为项目命名并点击确定使用 Entity Framework 创建控制台应用程序

安装 Entity Framework 7 NuGet 包

在你的应用程序中安装任何NuGet包有两种方式:

  • 使用NuGet包管理器

  • 使用包管理器控制台

使用 NuGet 包管理器

喜欢图形界面的用户可以使用此选项:

  1. 右键单击控制台项目,从上下文菜单中选择 管理 NuGet 包使用 NuGet 包管理器

  2. NuGet 包中搜索 EntityFramework.MicrosoftSqlServer 并确保已勾选 包含预发布版 复选框。选择 EntityFramework.MicrosoftSqlServer 并点击 安装,然后选择 最新预发布版 7.0.0-rc1-final(本书编写时)。您可以选择 Entity Framework 7 的任何最新版本:使用 NuGet 包管理器

  3. 点击 安装 后,NuGet 包管理器将要求您审查更改。点击 确定使用 NuGet 包管理器

  4. 许可接受 窗口中点击 我接受使用 NuGet 包管理器

  5. 点击 我接受 后,它将安装包含所有依赖项的 Entity Framework。在 输出 窗口中,安装完成后将显示 完成 消息:使用 NuGet 包管理器

使用包管理器控制台

要使用包管理器控制台安装 NuGet 包,请按照以下步骤操作:

  1. 通过选择菜单选项 查看 | 其他窗口 | 包管理器控制台 打开 包管理器控制台 窗口。使用包管理器控制台

  2. 包管理器控制台 窗口中输入 Install-Package EntityFramework.MicrosoftSqlServer - Pre,如图所示:使用包管理器控制台

  3. 安装完成后,将显示一条消息,成功安装 'EntityFramework.MicrosoftSqlServer 7.0.0-rc1-final'使用包管理器控制台

安装 Entity Framework 命令

我们需要安装 Entity Framework 命令包以便执行迁移活动。迁移包括创建数据库及其相关表。任何架构更改也将由迁移处理:

安装 Entity Framework 命令

如前所述,当我们使用 Entity Framework 与数据库交互时,需要遵循三个步骤:

  1. 创建 Model 类。

  2. 为您的业务域对象创建 DbContext 类。DbContext 类的实例代表与数据库的会话。

  3. 使用 DBContext 类的实例构建业务域对象并将其保存到数据库。

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

创建 Model 类

Model 类是简单的 POCO 对象,可以与 Entity Framework 一起使用。

让我们为我们的业务域对象创建一个 POCO 类,在我们的例子中是 Employee 类。我在控制台应用程序中创建了一个名为 Employee.cs 的新文件,其内容如下。这个 Employee 类包含了一些员工属性,并且没有特殊属性或字段使其与 Entity Framework 一起工作。

让我们看一下以下代码片段:

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

按照惯例,如果属性名为 IdClassName+Id,Entity Framework 在创建数据库表时会将其视为主键。

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

创建 DbContext 类

DbContext 类的实例代表数据库会话,并且这个 DbContext 类为您的应用程序的数据访问做了大部分繁重的工作。

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

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

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

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

使用 App.Config 配置它:

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

在前面的代码片段中有几点需要注意:

  • 包含 Microsoft.Data.Entity 命名空间,因为该命名空间中提供了 DbContext 类。我们的连接字符串在 App.Config 文件中可用。为了读取 App.Config 文件的内容,我们在 System.Configuration 中包含了 ConfigurationManager 类。

  • 为了使用 DbContext API,必须创建一个继承自 DbContext 类的类,这样我们才能访问 DbContext API 的方法。我们已经创建了继承自 DbContext 类的 EmployeeDbContext 类。

  • DbSet 是一个类,它允许对给定实体类型执行 Entity Framework 操作。我们需要为我们在应用程序中使用的每个实体类型创建 DbSet 对象。在这个例子中,我们只使用一个 DbSet 对象,因为我们正在处理 Employee 类。

创建迁移

迁移是记录数据库所有更改的过程。Add-Migration 是 Entity Framework 添加迁移的命令:

创建迁移

  1. 一旦添加了迁移,您可以通过执行 Remove-Migration Entity Framework 命令撤销更改。创建迁移

    • 这是迁移目录的外观:创建迁移
  2. 通过执行 Entity Framework 命令 Update-Database 更新数据库,该命令根据迁移中可用的信息更新数据库表。由于我们之前已安装了 EntityFramework.Commands 包,因此这些命令将可用于应用程序:创建迁移

  3. 更新数据库后,您可以通过连接到 SQL Server Management Studio 来查看数据库中的更改:创建迁移

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

Main方法被以下代码更新:

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

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

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

首先,我们正在构建业务领域对象。然后,我们将构建的Employee对象添加到DbContext类的EmployeeDbSet中。最后,我们调用DbContext API 的SaveChanges方法,该方法将所有挂起的更改保存到数据库中。

你可能想知道我们没有提供连接字符串时,它是如何将其保存到数据库中的。

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

  • 当你对DbSet集合中的任何内容进行更改时,Entity Framework 会检查数据库是否存在。如果不存在,它将使用模式<DbContextName 的命名空间>创建一个新的数据库。在我们的例子中,一个名为EF6.EmployeeDbContext的数据库将被创建。

  • 然后,它为在DbSet中声明的实体创建数据库表。按照惯例,Entity Framework 使用实体的复数形式作为表名。由于我们为Employee实体声明了DbSet,Entity Framework 创建Employee的复数形式,并创建名为Employees的表。

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

db.Employees.Add(employee); 

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

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

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

创建迁移

SaveChanges方法的工作原理

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

例如,当我们向员工集合(DbSet)添加一个Employee对象时,该对象在Added状态下被Entity Framework跟踪。当调用SaveChanges时,Entity Framework 为该对象创建一个insert查询并执行它。更新和删除对象的情况也是一样。Entity Framework 将相应对象的Entity状态设置为ModifiedDeleted。当调用SaveChanges时,它创建并执行UpdateDelete查询。

方法的工作原理

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

更新记录

让我们尝试使用 Entity Framework 更新已插入的员工记录的薪水:

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

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

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

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

更新记录

删除记录

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

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

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

在 ASP.NET MVC 应用程序中使用 Entity Framework

在控制台应用程序和 ASP.NET MVC 应用程序中使用 Entity Framework 之间没有太大的区别。现在,我们将构建一个简单的应用程序,它只有一个屏幕,如图所示。在这个屏幕上,我们将有一个表单,用户将输入有关员工的信息;一旦用户提交表单,信息将被保存到数据库中,并在以下屏幕截图中反映出来:

在 ASP.NET MVC 应用程序中使用 Entity Framework

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

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

  1. 通过在 Visual Studio 中选择一个空的 ASP.NET 5 应用程序来创建一个 ASP.NET Core 项目。

  2. 安装 ASP.NET Core NuGet 包。

  3. 按照本章前面所述,安装 Entity Framework 7 的 NuGet 包和 **ef** EntityFramework 命令(用于数据库迁移)。

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

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

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

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

    • ConfigureServices 方法中将 MVC 服务和 Entity Framework 服务添加到服务中

    • Configure 方法中配置 MVC 路由:

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

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

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

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

    using Microsoft.Data.Entity; 
    using Microsoft.Extensions.Configuration; 
    
    namespace Validation.Models { 
      public class EmployeeDbContext : DbContext { 
    
        public IConfigurationRoot Configuration { get; set; } 
    
        public DbSet<Employee> Employees { get; set; } 
    
        public EmployeeDbContext() { 
          var builder = new ConfigurationBuilder() 
          .AddJsonFile("config.json") 
          .AddEnvironmentVariables(); 
          Configuration = builder.Build(); 
        } 
    
        protected override void OnConfiguring     (DbContextOptionsBuilder optionsBuilder) {      optionsBuilder.UseSqlServer       (Configuration.Get<string>       ("Data:DefaultConnection:ConnectionString")); 
          base.OnConfiguring(optionsBuilder); 
        } 
      } 
    } 
    
    
  11. 创建 ViewModels

    • 由于我们将在同一屏幕上显示员工列表和添加员工的表单,我们将构建一个针对此视图的特定模型。此模型将包含有关员工列表和要添加的员工的信息。
  12. 创建 ViewModels 文件夹并添加 EmployeeAddViewModel

    using MVCEF7.Models; 
    
    namespace MVCEF7.ViewModels { 
      public class EmployeeAddViewModel { 
        public List<Employee> EmployeesList { get; set; } 
        public Employee NewEmployee { get; set; } 
      } 
    } 
    
    
    • 这个 ViewModel 有几个属性。EmployeesListNewEmployeeEmployeesList 将包含员工列表。这个列表将从数据库中获取。NewEmployee 将保存用户输入的员工信息。
  13. 创建 Controllers 来处理传入的请求:

    • 创建一个 Controllers 文件夹并添加 EmployeeController 类,包含几个动作方法——一个用于 GET,另一个用于 POST。当您访问 URL (http://localhost/Employee/Index)或运行应用程序时,将调用与 GET 动作方法对应的 Index 动作方法。当您提交表单时,将调用 POST Index 动作方法:

      public IActionResult Index() { 
        EmployeeAddViewModel employeeAddViewModel = new    EmployeeAddViewModel(); 
        using (var db = new EmployeeDbContext()) { 
          employeeAddViewModel.EmployeesList =      db.Employees.ToList(); 
          employeeAddViewModel.NewEmployee = new Employee(); 
      
        } 
        return View(employeeAddViewModel); 
      } 
      
      
    • 在前面的 GET Index 动作方法中,我们创建 ViewModel 对象并将其传递到视图中。

    • 以下代码使用 POST Index 动作方法:

      [HttpPost] 
      public IActionResult Index(EmployeeAddViewModel  employeeAddViewModel) { 
      
        using (var db = new EmployeeDbContext()) { 
          db.Employees.Add(employeeAddViewModel.NewEmployee); 
          db.SaveChanges(); 
          //Redirect to get Index GET method 
          return RedirectToAction("Index"); 
        } 
      
      } 
      
      
    • 我们在 ViewModel 中获取 NewEmployee 属性,它包含用户信息。将其保存到数据库中。一旦我们将员工信息保存到数据库中并将控制权重定向到 GET Index 动作方法,GET Index 动作方法将再次显示输入员工信息的表单和员工列表。

  14. 添加 Views 文件夹:

    1. 创建 Views_ViewStart.cshtml 并包含以下内容:

      @{ 
        Layout = "_Layout"; 
      } 
      
      
    2. 创建 Views\Shared_Layout.cshtml 并包含以下内容:

      <!DOCTYPE html> 
      
      <html> 
        <head> 
          <meta name="viewport" content="width=device-width" /> 
          <title>@ViewBag.Title</title> 
        </head> 
        <body> 
          <div> 
            @RenderBody() 
          </div> 
        </body> 
      </html> 
      
      
    3. 创建 Views\Employee\Index.cshtml 并包含以下内容:

      @model MVCEF.ViewModels.EmployeeAddViewModel 
      @* 
      //For more information on enabling MVC for empty projects,  visit http://go.microsoft.com/fwlink/?LinkID=397860 
      *@ 
      @{ 
      } 
      
      <div> 
        @using (Html.BeginForm("Index", "Employee",    FormMethod.Post)) { 
          <table> 
            <tr> 
              <td>@Html.LabelFor(Model =>          Model.NewEmployee.Name)</td> 
              <td>@Html.TextBoxFor(Model =>          Model.NewEmployee.Name)</td> 
            </tr> 
            <tr> 
              <td>@Html.LabelFor(Model =>          Model.NewEmployee.Designation)</td> 
              <td>@Html.TextBoxFor(Model =>          Model.NewEmployee.Designation)</td> 
            </tr> 
            <tr> 
              <td>@Html.LabelFor(Model =>          Model.NewEmployee.Salary)</td> 
              <td>@Html.TextBoxFor(Model =>          Model.NewEmployee.Salary)</td> 
            </tr> 
            <tr> 
              <td colspan="2"><input type="submit"          value="Submit"/> 
              </td> 
            </tr> 
          </table> 
      
        } 
      </div> 
      
      <br/><br/> <br/> 
      
      <b> List of employees:</b> <br/> 
      <div> 
        <table border="1"> 
          <tr> 
            <th> ID </th> 
            <th> Name </th> 
            <th> Designation </th> 
            <th> Salary </th> 
          </tr> 
          @foreach(var employee in Model.EmployeesList) { 
            <tr> 
              <td>@employee.EmployeeId</td> 
              <td>@employee.Name</td> 
              <td>@employee.Designation</td> 
              <td>@employee.Salary</td> 
            </tr> 
          } 
        </table> 
      </div> 
      
      

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

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

在 ASP.NET MVC 应用程序中使用 Entity Framework

数据库迁移

我们已创建业务实体——Employee类。现在,我们可以进行迁移。迁移是一个两步过程:在第一步中,我们创建迁移文件。这可以通过从项目上下文在命令提示符中执行以下命令来完成:


dnx ef migrations add InitialMigration

数据库迁移

此命令将在您的项目中创建迁移文件,如下截图所示:

数据库迁移

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

数据库迁移

此命令将读取上一阶段创建的迁移文件,并创建数据库以及相关的表:

数据库迁移

运行应用程序。您将看到以下屏幕,用户可以在表单中输入员工信息。由于我们在视图中使用强类型模型,它为所有属性取默认值。NameDesignationstring类型的属性,这些字段的默认值是空字符串,Salarydecimal类型,decimal的默认值是0,因此当表单加载时,Salary字段显示0

由于没有记录,我们在“员工列表”表中显示0条记录:

数据库迁移

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

数据库迁移

摘要

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

第六章:验证

我们永远不能依赖用户输入的数据。有时他们可能对应用程序一无所知,因此他们可能无意中输入了错误的数据。在其他时候,一些恶意用户可能希望通过在应用程序中输入不适当的数据来破坏应用程序。在任何情况下,我们都需要在存储数据以供进一步处理之前验证输入数据。

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

  • 不同类型的验证

  • 带有示例的服务器端验证

  • 带有示例的客户端验证

  • 使用 jQuery 无障碍库进行无障碍 JavaScript 验证,其中我们不需要为验证编写单独的代码

在理想情况下,用户会在您的应用程序中以适当的格式输入有效数据。但,如您所意识到的那样,现实世界并不那么理想。用户会在您的应用程序中输入错误的数据。作为开发者,验证我们应用程序中的用户输入是我们的责任。如果输入的数据无效,您需要通知用户发生了什么错误,以便用户可以纠正输入数据并再次提交表单。

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

验证

如果验证是在服务器端进行的(将表单数据发送到服务器),则称为服务器端验证。例如,您可能想验证用户输入的数据与数据库中的数据是否一致。在这种情况下,进行服务器端验证是首选的,因为我们不能在客户端拥有数据库中的所有数据。

验证

客户端和服务器端验证

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

客户端和服务器端验证

前面的图示显示了验证在客户端和服务器端同时进行。如果数据没有输入到所需的字段中,我们可以在客户端本身捕捉到这个问题。没有必要将数据发送到服务器,最终发现没有输入数据。一旦所有必需的数据都输入完毕,数据就会发送回服务器,根据某些业务逻辑验证输入的数据。如果验证失败,表单数据会再次发送到浏览器,并带有错误信息,以便用户可以再次发送数据。

我们已经讨论了关于验证的必要性以及应用程序中通常使用的验证类型的理论。让我们通过向上一章构建的应用程序添加验证来实际操作。

下面的截图是我们上一章构建的表单。这个表单没有太多花哨的地方——只有三个字段。当用户在表单中输入数据时,数据将存储在数据库中,并将整个员工信息以表格格式检索并显示出来。

客户端和服务器端验证

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

客户端和服务器端验证

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

服务器端验证

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

  1. 将数据注释属性添加到ViewModels模型类中。输入数据将与此元数据进行验证,并且模型状态将自动更新。

  2. 更新view方法以显示每个字段的验证消息。将使用带有asp-validation-for属性的span标签助手来显示验证错误消息。

  3. 更新控制器操作方法以验证模型状态。如果模型状态有效,我们将数据插入数据库。否则,视图模型将被更新,并且view方法将再次渲染,带有验证错误消息,以便用户可以使用有效的输入数据更新并再次提交表单。

使用数据注释属性更新视图模型

数据注释属性定义了Model/ViewModel属性的验证规则。如果输入数据与模型中的属性定义不匹配,验证将失败,这反过来又使得相关的模型状态无效。

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

  • Required:此属性表示属性是必需的。

  • Range:此属性定义了最小和最大约束。

  • MinLength:这定义了属性必须具有的最小长度,以便验证成功。

  • MaxLength:正如其名所示,此属性定义了属性的长度上限。如果属性值的长度超过最大长度,验证将失败。

  • 正则表达式:如果我们使用此属性,我们可以使用正则表达式进行数据验证。

由于数据注释属性位于System.ComponentModel.DataAnnotations命名空间中,我们需要包含此命名空间。以下是更新后的视图模型代码:

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

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

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

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

我们为所有三个属性——NameDesignationSalary——添加了数据注释属性。

ErrorMessage属性显示在验证失败时显示的消息。如果验证失败且没有指定ErrorMessage,则将显示默认错误消息。

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

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

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

更新控制器操作方法以验证模型状态

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

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

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

更新控制器操作方法以验证模型状态

在之前的验证及其错误消息中,有一些需要注意的事项:

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

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

  • 如果没有错误消息可用且验证失败,将显示默认错误消息。我们没有为薪资字段提供任何错误消息。因此,当该字段的验证失败时,ASP.NET MVC 将根据字段名称和验证失败类型显示基于字段的默认错误消息。更新控制器操作方法以验证模型状态

前面的图示展示了服务器端验证的高级事件序列,具体描述如下:

  1. 用户输入了无效数据。

  2. 根据视图模型中的数据注释属性,模型状态会自动更新。这发生在模型绑定过程中,其中view方法中的数据映射到模型或视图模型中的数据。

  3. 在控制器的操作方法中,我们正在验证模型状态。

  4. 如果模型状态有效,我们将保存输入的数据到数据库。

  5. 如果模型状态无效,我们将再次渲染带有验证错误消息的视图模型,以便用户可以更正输入数据,并使用有效输入数据再次提交表单。

客户端验证

有一些场景中我们不需要去服务器验证输入数据。在前面的服务器端验证示例中,我们不需要去服务器验证用户是否为姓名字段输入了数据。我们可以在客户端本身进行验证。这防止了往返服务器,并减少了服务器负载。

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

注意

这些天,JavaScript 也被用作 Node.js 的一部分在服务器端。

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

  1. 表单中的更改:将id属性添加到所有span标签中,以便我们可以访问此 HTML 元素来显示 HTML 错误消息。在表单提交时,调用一个 JavaScript 函数来验证输入数据。

  2. 添加脚本 HTML 元素并创建一个 JavaScript 函数来验证输入数据。

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

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

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

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

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

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

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

            return isValidForm; 

        } 

</script> 

当你运行应用程序并提交表单而没有输入数据时,你会收到客户端本身生成的错误消息,而无需访问服务器。

客户端验证

在现实世界的应用程序中,我们不会手动在 JavaScript 中编写验证代码。相反,大多数应用程序使用无侵入验证,我们不需要为每个字段编写 JavaScript 代码。只需添加相应的 JavaScript 库即可。

你可能会想知道字段是如何在没有编写代码的情况下进行验证的。这个魔法在于根据数据注释属性添加到输入 HTML 元素中的 data- 属性。这个 jQuery 无侵入库获取到添加了 data- 属性的字段列表,并进行验证。

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

客户端验证

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

实现

将有一个布局文件 (_Layout.cshtml ) 来定义你的 Web 应用程序的布局结构。由于所有页面都将使用 JavaScript 库,这是添加诸如无侵入验证等常见功能的好地方。只需将 JavaScript 库(粗体显示)添加到布局文件 (_Layout.cshtml ) 中,这样它们就会对所有 View 文件可用:

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

<script src="img/jquery-2.2.3.js"></script> 
    <script src="img/jquery.validate.min.js"></script> 
    <script src="img/jquery.validate.unobtrusive.min.js"></script>

</body> 
</html> 

除了移除我们之前编写的用于验证字段的 JavaScript 函数外,视图模型没有发生变化。视图的完整代码如下:

@model Validation.ViewModels.EmployeeAddViewModel 

<div> 

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

        </table> 
    </form> 

</div> 

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

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

</div> 

实现

上述图解展示了无侵入客户端验证过程:

  1. Model /ViewModels 中添加了数据注释。

  2. 视图接受 Model /ViewModels 并生成 HTML。

  3. 视图模型生成的 HTML 包含 data-* 属性:

    • 对于设置了 Required 属性的字段,将创建 data-val-required 属性,其值为错误消息。

    • 对于具有 MinLength 数据注释属性的字段,data-val-minlength 属性设置为错误消息的值。

    • 对于范围数据注释,data-val-range 属性设置为错误消息的值。data-val-range-max 表示范围的最大值,而 data-val-range-min 属性表示范围的最小值。

  4. jQuery 无障碍验证库通过data-*属性读取这些元素并执行客户端验证。这允许开发者不必使用 JavaScript 编写分离的验证代码,因为所有内容都由配置本身解决。

摘要

在本章中,我们学习了验证的需求以及可用的不同类型的验证。我们还讨论了客户端和服务器端验证的工作原理,以及每种类型验证的优缺点。随后,我们对代码进行了更改,以在服务器端验证输入数据。然后我们使用 JavaScript 在客户端本身验证输入数据。最后,我们使用 jQuery 无障碍库在客户端进行验证,而无需编写任何用于在客户端验证输入数据的 JavaScript 代码。

在下一章中,我们将讨论路由原理以及如何自定义它。在早期章节中,我们看到了 ASP.NET 5 应用程序中路由的基础。现在我们将深入探讨这个主题。

看累了记得休息一会哦~

公众号:古德猫宁李

  • 电子书搜索下载

  • 书单分享

  • 书友学习交流

网站:沉金书屋 https://www.chenjin5.com

  • 电子书搜索下载

  • 电子书打包资源分享

  • 学习资源分享

第七章。路由

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

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

  • 使用 MapRoute 方法配置路由

  • 带有示例的不同类型的路由——约定和基于属性的

  • 在基于属性的路由中使用 HTTP 动词

我们在第三章 控制器 中简要讨论了路由。在本章中,我们将讨论路由以及 ASP.NET Core 中可用的自定义选项。

基于约定的路由

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

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

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

MapRoute 方法有两个参数:

  • name:这表示路由的名称,因为我们可以为同一应用程序配置多个路由。

  • template:这表示路由的实际配置。此配置值有三个部分。由于我们提供了默认参数,如果未传递值,它将采用默认参数值。

  • {controller=Employee}:第一个值作为控制器的名称,当 URL 中不可用控制器值时,我们使用 Employee 控制器作为默认控制器。

  • {action=Index}Index 操作方法将作为默认操作方法,URL 的第二个参数将被用作操作方法名称。

  • {id?}:通过在 id 参数后指定 "?",我们表示 id 是可选参数。如果作为第三个参数传递值,则将采用 id 值。否则,它将不被考虑。

另有一个具有相同功能的方法。app.UseMvcWithDefaultRoute() 方法配置了路由 "{controller=Employee}/{action=Index}/{id?}"。但我们使用了前面的方法来表明我们可以按需自定义路由。

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

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

示例 1

URL-http://localhost:49831/

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

http://localhost:49831/Employee/Index

示例 2

URL-http://localhost:49831/Employee/

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

http://localhost:49831/Employee/Index

示例 3

URL-http://localhost:49831/Manager/List

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

示例 4

URL-http://localhost:49831/Manager/Details/2

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

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

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

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

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

你可以为你的 ASP.NET MVC 应用程序定义任意数量的路由映射。对路由映射没有限制或限制。让我们向我们的应用程序添加另一个路由映射。我们向应用程序添加了另一个名为FirstRoute的路由映射(加粗显示):

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

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

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

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

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

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

当你尝试通过 URL http://localhost:49831/Hello 访问应用程序时,FirstRoutedefault两个路由映射都与 URL 模式匹配。

你认为在这种情况下,哪个映射路由会被应用?

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

  1. 匹配模式。

  2. 根据路由引擎定义的顺序。

第一个因素是显而易见的。为了被路由引擎选中,传入 URL 的模式应该与路由映射中定义的模板匹配。

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

示例 4

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

示例 4

基于属性的路由

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

配置路由引擎的另一种选项是基于属性的路由。在基于属性的路由中,不是在集中位置配置所有路由,而是在controller级别进行配置。

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

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

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

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

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

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

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

当你同时使用基于属性的路由和基于约定的路由时,基于属性的路由将具有优先级。

在控制器级别配置路由属性

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

在下面的代码中,URL 的公共部分(Home)在controller级别定义,而唯一部分在action方法级别定义。当 URL 模式映射到控制器的action方法时,两个路由部分(在controller级别和action方法级别)将被合并并匹配。因此,之前定义的路由和后续的路由之间将没有区别。

如果你想在基于属性的路由中使用两个参数,你可以在大括号内传递它们。在下面的示例中,我们对SayHello动作方法做了这样的事情。

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

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

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

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

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

我们甚至可以将路由值作为Route属性传递,而不是将路由值传递到 HTTP 操作动词,如HTTPGetHTTPPost

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

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

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

路由约束

路由约束允许你限制传递给控制器操作的值的类型。例如,如果你想将值限制为int类型,你可以这样做。以下是一个这样的例子:

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

ASP.NET 5 (ASP.NET Core)甚至支持默认参数值,这样你就可以传递默认参数:

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

摘要

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

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

第八章:使用 Bootstrap 美化 ASP.NET MVC 应用程序

你可能已经创建了一个包含所有必需功能的应用程序。它甚至可能在所有场景下都完美运行,没有任何问题。但你的应用程序的成功取决于你的用户如何访问它。为了成功,你的应用程序应该看起来很好(如果不是极好),并且要用户友好。

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

  • HTML 和 CSS 在 ASP.NET Core 应用程序中的作用

  • 前端框架的特点和可用的不同框架

  • Bootstrap 及其网格系统及其特性

  • Bootstrap 中可用的 CSS 类,用于表单元素(如输入和选择元素)

  • 不同类型的 HTML 元素(如表格)的 CSS 类

  • 在你的 ASP.NET Core 应用程序中使用 Bootstrap

在讨论如何使我们的应用程序看起来更好之前,让我们退一步,讨论 HTML 和 CSS 在你的应用程序中的作用。

了解 HTML 和 CSS

如前所述,所有浏览器只能理解 HTML、CSS 和 JavaScript。因此,你构建的应用程序应该生成 HTML、CSS 和 JavaScript 的输出。这一点对于使用 Java 或 Ruby on Rails 等其他技术构建的 Web 应用程序同样适用。话虽如此,我们只会讨论 HTML 和 CSS。

HTML超文本标记语言)用于在网页中结构化内容。例如,你可以在title标签中添加内容,这样它就会在浏览器的标签页或窗口中可用。让我们看看一个例子。

打开任何文本编辑器(你甚至可以使用记事本),输入以下 HTML 内容,并将文件保存为Bootstrap.html。请注意扩展名.html

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

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

在浏览器中打开文件,你应该会看到以下截图类似的内容:

了解 HTML 和 CSS

你会注意到放置在title标签中的内容显示为浏览器的标签页标题,标题内容被加粗并放大,段落则从新的一行开始。

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

CSS 不过是一组用于展示的规则。每个规则由两部分组成——一个需要应用声明的选择器和一个包含样式信息的声明。样式信息有一个属性和一个属性的值。

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

    h1{ 
        color : #0094ff; 
    } 

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

以下是我更新 CSS 样式(加粗显示)的更新后的 HTML 文件:

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

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

        h1{ 
            color : #0094ff; 
        } 

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

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

在浏览器中打开文件并做出样式更改后,你会得到以下输出:

了解 HTML 和 CSS

话虽如此,你需要创建 CSS 规则来让你的 Web 应用中的元素看起来更好。但为每个元素创建不同的样式是一个耗时且繁琐的任务。你可以选择任何可用的前端框架。

任何应用程序都应该具有以下特点:

  • 一致性:你应用中使用的元素和控制应该对用户来说很熟悉。例如,如果你使用下拉列表,用户应该只能从中选择一个值。

  • 响应式:你构建的应用程序应该在不同尺寸的设备上看起来都很好。你编写的代码应该适应你用户设备的屏幕大小。

  • 移动友好:这与前面的观点相关。如今,许多应用程序是通过移动设备而不是桌面或笔记本电脑访问的。我们必须确保我们构建的应用程序在移动设备上看起来很棒。

  • 可定制:如果你打算使用任何前端应用框架,它应该根据你的需求进行定制。例如,如果你想更新标题文本颜色,你应该能够更新或覆盖 CSS 文件以进行必要的更改。

  • 易于上手:学习前端框架的曲线应该尽可能平缓,因为你应该把时间花在为客户创造价值——构建和交付解决方案上。我们不是为了学习一个新潮的前端框架而获得报酬。

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

Bootstrap

Bootstrap 是最受欢迎的 HTML、CSS 和 JS 框架,用于在网络上开发响应式、移动优先的项目,您可以在 getbootstrap.com/ 访问它。

Bootstrap

Bootstrap 拥有许多满足网页应用各种需求的功能。它为每个 HTML 元素提供了不同的 CSS 类,这样您就可以在不接触任何 CSS 代码的情况下使用它。然而,如果您希望覆盖它,您也可以这样做。

让我们简要地看看 Bootstrap 的每个功能。

Bootstrap 网格系统

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

Bootstrap 提供了一个流体网格系统,随着设备或视口大小的增加,可以扩展到十二列。您可以将网格系统想象成 Excel 表格中的列(如下截图所示):

Bootstrap 网格系统

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

表单

当您在应用中使用 Bootstrap 时,所有表单控件都会收到全局样式。以下是一个这样的例子(截图来自 Bootstrap 网站):

表单

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

表单

内联表单

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

内联表单

水平表单

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

水平表单

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

水平表单

表格 CSS 类

对于基本的样式,将基础 CSS 类 table 添加到 table HTML 元素中,如下截图所示:

表格 CSS 类

条纹表格

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

条纹表格

悬停表格

当您将鼠标移至表格中的任何一行时,该行的背景颜色会改变。这种悬停行为可以通过将 CSS 类table-hover与类table一起应用到 HTML 表格元素上来实现。

悬停表格

带边框的表格

我们可以通过将 CSS 类table-bordered应用到table元素上来拥有一个带边框的表格(如下面的屏幕截图所示)。

带边框的表格

表格中的上下文类

有时您可能想要根据数据值突出显示行。例如,如果您以表格格式显示库存数据,您可能想要将计数少于规定计数的项目的行突出显示为红色背景颜色。在这种情况下,您可以通过将danger类应用到表格行上来突出显示它们为红色。有不同类型的上下文类可用于以不同颜色突出显示。您可以将这些类应用到单个单元格而不是整个行。

表格中的上下文类

按钮

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

按钮

btn-primary按钮类用于将按钮突出显示为蓝色,而btn-success按钮类则将按钮突出显示为绿色。在先前的屏幕截图中,展示了按钮样式的不同选项。

按钮大小

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

按钮大小

上下文颜色

根据上下文,您可能想要更改文本的颜色。例如,如果您想要在操作成功时使文本显示为绿色,对于不成功的操作,您可能想要以红色显示错误消息。在这种情况下,您可能使用这些辅助 CSS 类以不同的颜色显示它们。

上下文颜色

我们已经看到了 Bootstrap 的各种功能。现在,让我们使用 Bootstrap 来使我们的应用程序看起来更美观。基本上,在我们的视图中有两个主要组件——一个位于顶部的表单用于获取用户的输入,以及一个位于底部的表格用于以表格形式显示结果。

在 ASP.NET MVC 应用程序中使用 Bootstrap

有不同的方式可以将 Bootstrap 应用到您的应用程序中:

  • 参考应用中可用的 CDN(内容分发网络)上的 Bootstrap 文件

  • 下载源代码

  • 使用 Bower 安装

  • 使用 Grunt 编译

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

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

<!DOCTYPE html> 

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

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

    <script src="img/jquery-2.2.3.js"></script> 
    <script src="img/jquery.validate.min.js"></script> 
    <script src="img/jquery.validate.unobtrusive.min.js"></script> 
    <!-- Latest compiled and minified JavaScript --> 
    <script src="img/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script> 
</body> 
</html> 

使用 Bower 安装

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

使用 Bower 安装

一旦选择管理 Bower 包,你就可以像安装或卸载 NuGet 包一样安装或卸载 Bower 包。

使用 Bower 安装

HTML doctype

Bootstrap 使用某些需要使用 HTML 5 doctype 的 HTML 元素和 CSS 属性。默认情况下,你创建的 ASP.NET Core 视图将只有 HTML 5 doctype。因此,我们不需要对此做任何事情。


<!DOCTYPE html> 

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

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

  • 将 CSS 类 form-horizontal 应用到表单上。

  • 对于标签、输入和验证错误 span,分别使用 CSS 类 col-sm-2col-sm-4col-sm-3

  • 对于标签,应用 CSS 类 control-label

  • 对于输入 HTML 元素,应用 form-control CSS 类

  • 对于每个表单组(包含如标签和输入等 HTML 元素),应用 CSS 类 form-group

  • 对于所有验证错误消息,应用 text-danger CSS 类,以便它们将以红色显示

  • 应用 tabletable-bordered CSS 类来设置表格样式

以下是完全更新的视图代码;我们使用了 Bootstrap CSS 类来使我们的应用程序看起来很棒:

@model Validation.ViewModels.EmployeeAddViewModel 

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

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

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

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

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

    </form> 

</div> 

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

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

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

在进行上述更改后,当你运行应用程序时,你的屏幕应该看起来像以下这样:

HTML doctype

读累了记得休息一会哦~

公众号:古德猫宁李

  • 电子书搜索下载

  • 书单分享

  • 书友学习交流

网站:沉金书屋 https://www.chenjin5.com

  • 电子书搜索下载

  • 电子书打包资源分享

  • 学习资源分享

摘要

在本章中,我们学习了 HTML 和 CSS 在任何 Web 应用程序中的作用,包括 ASP.NET Core 应用程序。我们分析了前端框架的需求,并讨论了 Bootstrap 的特性——这是最受欢迎的 HTML、CSS 和 JS 框架,用于在 Web 上开发响应式、移动优先的项目。我们通过示例讨论了 Bootstrap 中可用于不同类型 HTML 元素的 CSS 和组件。最后,我们讨论了如何将 Bootstrap 集成到我们的 ASP.NET Core 应用程序中。

第九章。ASP.NET Core 应用程序的部署

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

在任何应用程序中,无论它是 Web、桌面还是移动应用程序,并不是所有的功能都是通过代码实现的。实际上,你不应该试图通过代码实现所有功能。

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

  • ASP.NET Core 应用程序的配置

  • 在 Microsoft Azure 平台上注册

  • 将 ASP.NET Core 应用程序部署到 Azure 云平台

如果你使用任何之前的 ASP.NET MVC 版本构建了 Web 应用程序,将有一个名为Web.config(一个 XML 文件)的文件,你可以在此配置应用程序的所有依赖项。但在 ASP.NET Core 中,你的解决方案中将没有Web.config文件:

ASP.NET Core 应用程序的部署

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

JSON 是JavaScript 对象表示法的缩写。它是一个开放标准的数据交换格式。它将以人类可读的文本形式存在,并包含属性/值对。考虑以下 JSON,让我们分析一下它代表什么:

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

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

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

每条记录或对象都包含在一对花括号内。例如,以下 JSON 数据代表一条记录或一个对象:

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

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

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

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

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

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

project.json文件

所有项目配置都应该放入 ASP.NET Core 应用程序的project.json文件中。以下是在使用预定义的 ASP.NET Core Web 应用程序模板时创建的project.json文件:

文件

在这个 JSON 文件中,针对不同的功能有不同的预定义节点。让我们讨论一下project.json文件中的一些重要节点。

dependencies节点

dependencies节点列出了你的 ASP.NET Core 应用程序的所有依赖项。

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

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

框架节点

在此节点中,我们提到了我们依赖的 ASP.NET Core 应用程序的框架。dotnet5.6代表完整的.NET 框架,而dnxcore50代表包含完整.NET 框架子集功能的.NET Core 框架:

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

微软 Azure

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

您可以将应用程序部署到任何具有您网络中互联网信息服务IIS)的服务器上。但这将限制您的应用程序只能从您的网络内部访问,假设您的服务器只能从您的网络内部访问(如大多数网络设置)。在本节中,我们将部署 ASP.NET Core 应用程序到微软 Azure,以便您的全球用户可以访问您的应用程序。

注册微软 Azure

为了将您的应用程序部署到 Azure,您需要有一个 Azure 账户。您可以免费创建一个 Azure 账户,并在前 30 天内有足够的信用额度免费部署您的应用程序(azure.microsoft.com/en-in/):

注册微软 Azure

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

注册微软 Azure

点击立即开始按钮,您将被重定向到以下页面。输入您的微软账户凭据并点击登录按钮。如果您没有微软账户,您可以通过点击页面底部的立即注册链接来创建一个:

注册微软 Azure

由于我已经有一个微软账户,我已经用我的凭据登录。一旦您登录,您将需要提供有关您的国家、名字、姓氏和您的办公电话的详细信息,如下所示:

注册微软 Azure

一旦您输入了所有必要的详细信息,您将需要提供您的国家代码和电话号码,以便 Azure 可以通过短信或电话验证您是一个真实的人,而不是机器人 注册微软 Azure 。如果您选择短信给我选项,您将收到一个手机短信中的验证码;您需要在最后一个字段中输入它并点击验证码

注册微软 Azure

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

注册 Microsoft Azure

一旦输入您的信用卡信息并点击下一步,您将需要在注册过程的最后一步同意订阅协议:

注册 Microsoft Azure

一旦点击注册按钮,完成过程将需要另外五分钟。您将看到以下屏幕,直到过程完成:

注册 Microsoft Azure

一旦完成注册过程,您将看到以下屏幕。您还将收到一封确认电子邮件(发送到您在第一步中提供的电子邮件地址),其中包含订阅详情:

注册 Microsoft Azure

Azure 部署先决条件

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

注意

如果您有 VS 2015 的最新版本,则无需安装更新 2。

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

要安装 SQL Server 数据工具,请转到控制面板 | 程序和功能。右键单击Microsoft Visual Studio Community 2015并选择更改选项,如图下截图所示:

Azure 部署先决条件

一旦点击更改选项,您将看到以下窗口——在这里您需要选择修改按钮。一旦点击修改按钮,您将获得一个选项,可以修改 Visual Studio 安装选项。我已选择Microsoft SQL Server 数据工具,如图下截图所示:

Azure 部署先决条件

一旦点击下一步,Visual Studio 将安装 SQL Server 数据工具,一旦完成,您将看到以下屏幕,显示设置完成状态:

Azure 部署先决条件

在 Azure 中部署 ASP.NET Core 应用程序

让我们创建一个可以部署到 Microsoft Azure 的 ASP.NET Core 应用程序:

在 Azure 中部署 ASP.NET Core 应用程序

一旦点击确定按钮,ASP.NET Core 应用程序将被创建:

在 Azure 中部署 ASP.NET Core 应用程序

由于默认的 ASP.NET Core Web 应用程序模板使用 Entity Framework,我们需要执行以下命令来创建数据库迁移:


dotnet ef database update

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

在 Azure 中部署 ASP.NET Core 应用程序

数据库迁移完成后,右键单击创建的 Core 应用程序,并选择发布选项,如下截图所示:

在 Azure 中部署 ASP.NET Core 应用程序

当您点击发布选项时,您将看到以下屏幕,展示了您可用的各种发布选项:

在 Azure 中部署 ASP.NET Core 应用程序

请选择  Microsoft Azure App Service 选项,以在 Microsoft Azure 平台上发布 Web 应用程序:

在 Azure 中部署 ASP.NET Core 应用程序

点击新建按钮,您将看到以下屏幕:

在 Azure 中部署 ASP.NET Core 应用程序

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

点击资源组旁边的新建按钮,并输入资源组的名称。资源组只是一个标签,您可以在其中分组所有计算资源,以便如果您想删除所有资源,只需删除资源组即可。例如,资源组可能包括一个 Web 服务器和一个数据库服务器——您可以将它视为资源集合。

现在,点击App Service Plan旁边的新建按钮。您将看到以下窗口,您可以在其中选择 Web 应用程序容器的位置和大小。您的位置可以从美国南部中心到欧洲,从日本到加拿大。您的应用程序容器可以是免费的,也可以是具有 7 GB RAM 的机器。我选择了免费选项,因为我们的目标是部署 ASP.NET Core 应用程序到云环境,而不是部署一个将被数百万用户访问的应用程序。当然,您可以使用 ASP.NET Core 和 Microsoft Azure 实现相同的目标:

在 Azure 中部署 ASP.NET Core 应用程序

现在,我们可以配置作为附加 Azure 服务提供的 SQL 数据库。

在 Azure 中部署 ASP.NET Core 应用程序

点击顶部区域可用的+按钮,这将带我们到 SQL 数据库的配置。

在 Azure 中部署 ASP.NET Core 应用程序

如果你已经在 Azure 环境中有一个现有的 SQL 服务器,你可以使用它。由于我没有这样的服务器,我将通过点击 SQL 服务器旁边的 新建 按钮来创建一个 SQL 服务器:

在 Azure 中部署 ASP.NET Core 应用程序

请输入 SQL 服务器管理员用户名和密码,然后点击 确定。你将看到以下屏幕:

在 Azure 中部署 ASP.NET Core 应用程序

一旦你点击 确定,你将看到以下屏幕:

在 Azure 中部署 ASP.NET Core 应用程序

在上述屏幕上点击 确定,我们将看到 创建应用服务 屏幕:

在 Azure 中部署 ASP.NET Core 应用程序

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

在 Azure 中部署 ASP.NET Core 应用程序

上述屏幕显示了部署配置选项,例如我们应用程序的 站点名称目标 URL。在上述屏幕上点击 下一步

在 Azure 中部署 ASP.NET Core 应用程序

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

在 Azure 中部署 ASP.NET Core 应用程序

上述屏幕是预览屏幕,其中你可以看到发布时将要部署的文件。这是一个可选步骤——如果你想查看文件,可以点击 开始预览 按钮。否则,你可以点击 发布 按钮在 Azure 平台上发布网络应用程序。

一旦你点击 发布 按钮,我们的 ASP.NET Core 应用程序将在 Azure 中部署,并且你的应用程序 URL 将在成功发布后打开。你将看到以下屏幕:

在 Azure 中部署 ASP.NET Core 应用程序

在 Linux 环境中部署 ASP.NET Core 网络应用程序

在本章的这部分内容中,我们将学习如何在 Linux 平台上创建和部署 ASP.NET Core 网络应用程序。我将使用 Amazon Web Services ( AWS ) 在云端部署该应用程序。显然,你不需要 AWS 就能在 Linux 上部署 ASP.NET Core 应用程序。我只是用它来避免在我的本地机器上安装 Linux。并且,使用 AWS(或任何其他公共云服务提供商或任何托管提供商)托管的一个优点是,我可以从任何地方访问网络应用程序,因为它将是公开可用的。

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

  • Linux 机器

  • Putty 客户端(如果你正在使用远程 Linux 机器)

创建 Linux 机器

我们将使用 AWS 来创建一个 Linux 机器。使用 AWS 或其他任何云服务提供商的优势在于,我们只需在我们需要的时候使用他们的服务,并且当您完成使用后可以关闭机器。您只需为使用的时间付费。对于第一年,AWS 有一个免费层,您可以免费托管机器(如果符合免费层条件),无需支付任何费用。我已经使用 AWS 超过几年时间,在云中尝试了许多事情,因此我不再符合免费层的条件。

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

一旦您注册了 AWS 账户,您就可以进入EC2 仪表板,在那里您可以创建EC2 实例

创建 Linux 机器

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

创建 Linux 机器

AWS 中有不同类型的实例可供选择,从小型实例(0.5 GB RAM)到大机器(1952 GB RAM)。我们将选择微型实例,因为它符合免费层的条件:

创建 Linux 机器

在上一步中,我们可以配置云中的实例。我们可以创建一个自动扩展组,当负载高时,AWS 云会自动启动实例。由于我们的目标是创建和部署 ASP.NET Core Web 应用程序,我们将保留默认值,并点击下一步:添加存储以进入下一屏幕:

创建 Linux 机器

微型实例不附带任何外部存储。因此,我们需要添加存储才能使用它。我们有三种存储选项可供选择:通用型 SSD预配 SSD磁 SSD。在这三种中,通用型 SSD是通常会被使用的存储。

当您的应用程序进行高输入输出操作时,吞吐量可能会下降。但在预配 SSD 中,您可以从存储设备中维持所需的吞吐量。磁存储只是旧式存储。我们将使用通用型 8 GB 固态硬盘SSD),因为它很好地满足了我们的需求。

创建 Linux 机器

如果您正在使用多个实例,您可以给它们打标签,这样您就可以通过标签名称来控制实例。由于我们只将启动一个实例,我将直接跳到下一步:

创建 Linux 机器

在此步骤中,我们可以为实例配置安全组——应该打开哪些端口以供传入流量使用。在任何配置中的通用规则是只打开你需要的端口,而不是其他端口。你还需要告诉 IP(或其范围),从哪里可以访问该机器。由于这是一个演示应用程序,我们将打开端口22,用于安全外壳SSH);使用 PuTTY,以及80,用于访问核心 Web 应用程序。

一旦你配置了安全组,点击审查和启动

创建 Linux 机器

在下一个屏幕上,你可以查看所选选项:

创建 Linux 机器

当你对所选选项满意时,可以点击启动。否则,你可以返回上一步重新配置它们为正确的值。

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

创建 Linux 机器

选择密钥对并点击启动实例。AWS 将为我们启动新的实例,状态将显示(如下截图所示)。实例 ID 也将可用(在截图中已框出):

创建 Linux 机器

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

创建 Linux 机器

安装 PuTTY 客户端

在创建了一个新的 Linux 服务器,我们可以在这里创建 ASP.NET 5 Web 应用程序并托管它之后,我们需要安装 PuTTY 客户端,这是一个可以将命令发送到 Linux 服务器并接收响应的小应用程序。由于我们将在 Linux 服务器上安装应用程序,我们需要一种方法从你的 Windows PC 连接到 Linux 服务器。PuTTY 客户端应用程序正是这样做的。

你可以通过访问 www.chiark.greenend.org.uk/~sgtatham/putty/  下载 PuTTY 客户端。

安装 PuTTY 客户端

点击下载链接,并在下一个屏幕中选择截图中的链接(已框出):

安装 PuTTY 客户端

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

安装 PuTTY 客户端

点击下一步,你将看到以下屏幕:

安装 PuTTY 客户端

选择安装文件夹——你可以保持默认设置并点击下一步

安装 PuTTY 客户端

选择您想要安装的产品功能。您可以保留默认选择并点击 安装。安装完成后,您将看到以下屏幕:

安装 PuTTY 客户端

点击 完成 并启动 PuTTY 应用程序。它将打开 PuTTY 配置窗口,我们将在此输入主机名和认证详情。主机名是 <username>@<public DNS>。在我们的例子中,它是 ubuntu@ec2-107-22-121-81.compute-1.amazonaws.com。Ubuntu 是我们选择的 Ubuntu AMI 的默认用户。我们可以在之前显示的状态窗口中获取公共 DNS 值:

安装 PuTTY 客户端

对于认证,在左侧面板中选择 连接 | SSH | 认证,并选择我们之前创建的 PPK 文件(私钥文件):

安装 PuTTY 客户端

点击 打开。您将收到一个警告,询问您是否信任此主机。点击 ,您将看到 Linux 屏幕的命令提示符。

安装 PuTTY 客户端

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

Linux 机器上安装.NET Core

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


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

您将看到以下屏幕:

Linux 机器上安装.NET Core 的截图

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


sudo apt-get update

对于此命令,您将看到以下屏幕:

Linux 机器上安装.NET Core 的截图

使用以下命令安装 .NET Core:


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

将会显示以下屏幕:

Linux 机器上安装.NET Core 的截图

创建一个新的 ASP.NET 5 项目

通过以下命令创建一个新的目录,我们将在此目录中创建 ASP.NET 5 应用程序。第一个命令(**mkdir** - 创建目录)是在 Linux 中创建目录,第二个命令(**cd** - 切换目录)是进入文件夹。最后一个命令是创建 .NET Core 应用程序的命令行:


mkdir aspnetcoreapp
cd aspnetcoreapp
dotnet new

将会显示以下屏幕:

创建一个新的 ASP.NET 5 项目

这将创建一个 .NET Core 应用程序,其中包含几个文件——Program.csproject.json。这是一个最小化应用程序,甚至没有 Startup 文件。

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

创建一个新的 ASP.NET 5 项目

按下 Escape 键和 " *:"  * 并输入 wq  以写入并退出 vi 编辑器。

由于我们添加了依赖项,我们需要通过执行以下命令来恢复包:


dotnet restore

当您输入此命令时,所有包都将如以下截图所示恢复:

创建新的 ASP.NET 5 项目

创建一个新文件,Startup.cs,内容如下。您可以通过输入命令 **vi Startup.cs** 来创建新文件。像往常一样,我们需要按下 Esc + I 使文件处于可写和可读模式。粘贴以下内容(您可以通过在此处复制后右键单击鼠标粘贴):

using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
namespace aspnetcoreapp
{
  public class Startup
  {
    public void Configure(IApplicationBuilder app)
    {
      app.Run(context =>
        {
          return context.Response.WriteAsync("This is ASP.NET Core application running in Linux!");
         });
    }
  }
}

按下 Esc + : 并输入 wq 以保存文件。更新 Program.cs 文件,内容如下:

namespace aspnetcoreapp
{
  public class Program
  {
    public static void Main(string[] args)
    {
      var host = new WebHostBuilder()
      .UseKestrel()
      .UseStartup<Startup>()
      .Build();
       host.Run();
     }
   }
}

您将看到以下屏幕:

创建新的 ASP.NET 5 项目

我们已创建 ASP.NET Core 网络应用程序。现在我们需要安装 Nginx,一个反向代理服务器,它使您能够卸载诸如服务静态内容、缓存和压缩请求等工作。您可以通过输入以下命令来配置 Nginx 以监听特定端口(我们将在本章后面讨论细节)。您可以通过以下命令安装 Nginx:


sudo apt-get install nginx

安装完成后,您可以输入以下命令来启动服务:


sudo service nginx start

当您运行命令时,您将看到以下屏幕:

创建新的 ASP.NET 5 项目

配置 Nginx 服务器

通过修改文件(/etc/nginx/sites-available/default)以包含以下内容来配置 Nginx 服务器,以便 Nginx 将请求转发到 ASP.NET。为了修改此文件,您需要足够的权限——尝试切换到超级用户。**Sudo su** 是切换到超级用户的命令。请参见以下代码:

server {
  listen 80;
  location / {
    proxy_pass http://localhost:5000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection keep-alive;
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
  }
}

代码看起来如下:

配置 Nginx 服务器

通过执行以下命令来运行应用程序:


dotnet run

您将看到以下屏幕:

配置 Nginx 服务器

现在,使用公共 DNS(实例启动时 AWS 创建了公共 DNS)从浏览器访问应用程序:

配置 Nginx 服务器

哇!我们已经在 Linux 箱子中创建了 ASP.NET Core 网络应用程序并启动了它。我们甚至通过 Amazon Web ServicesAWS)使用了云服务。

摘要

在本章中,您学习了 project.json 文件中可用的不同组件,其中包含您所有 ASP.NET Core 的配置。我们讨论了如何注册 Microsoft Azure 云平台并在 Azure 平台上部署 ASP.NET Core 应用程序。我们还学习了如何在云中使用 Amazon Web Services 在 Linux 中创建和部署 ASP.NET Core 网络应用程序。

第十章。使用 ASP.NET Web API 构建基于 HTTP 的 Web 服务

到目前为止,我们已经学习了如何使用 ASP.NET Core 创建 Web 应用程序。但有时仅仅创建一个 Web 应用程序是不够的。让我们假设你正在使用 ASP.NET Core 创建一个提供全球所有城市天气信息的 Web 应用程序。人们访问你的 Web 应用程序以获取天气信息,并且对服务感到满意。但这个天气信息可能被许多其他网站或 Web 应用程序需要,例如旅游网站、新闻网站以及许多其他移动应用程序。

而不是为他们的网站重新编写代码,你可以创建和发布 Web 服务,网站可以在需要时消费所需的 Web 服务。

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

  • 基于 HTTP 的服务是什么以及它如何有用

  • Fiddler 是什么

  • 如何使用 Fiddler 组合 HTTP 请求并触发以获取 HTTP 响应

  • 如何使用 Web API 设计和实现 HTTP 服务

微软为程序员提供了 ASP.NET Web API 来构建基于 HTTP 的服务。但 HTTP 不仅仅用于服务网页。你可以将 HTTP 作为一个平台。这带来了几个优势:

  • 由于使用 ASP.NET Web API 构建的 Web 服务使用 HTTP 进行通信,因此这些 Web 服务可以从各种应用程序中消费,从控制台应用程序到 Web 应用程序,以及从 WCF 服务到移动应用程序

  • 无论何时,只要 Web 服务的逻辑/代码有任何变化,客户端(使用这些服务的网站)都不需要做任何改变。他们可以像之前一样消费 Web 服务

HTTP 基础知识

HTTP 是构建服务的强大平台。你可以使用现有的 HTTP 动词来构建服务。例如,你可以使用现有的 HTTP 动词 GET 来获取产品列表或 POST 来更新产品信息。让我们快速看一下 HTTP 在构建服务方面的作用。

在 ASP.NET MVC 中服务 HTML 页面和在 HTTP 服务上下文中服务数据的基本机制之间没有区别。两者都遵循请求-响应模式以及相同的路由机制。

一个 HTTP 请求可以从任何客户端(桌面、笔记本电脑、平板电脑、手机等等)发送到服务器,服务器将回送一个 HTTP 响应。HTTP 响应可以以任何格式发送到客户端,例如 JSON 或 XML。这将在以下图中展示:

HTTP 基础知识

在前面的图中,请求是从桌面计算机发送的(它同样可以从手机或平板电脑发送;没有区别)并且服务器为请求发送 HTTP 响应。由于 HTTP 在大多数设备上得到支持,因此它是无处不在的。

HTTP 动词

HTTP 动词描述了请求必须如何发送。这些是在 HTTP 中定义的方法,规定了 HTTP 请求从客户端发送到服务器的方式

GET 方法

当我们使用 HTTP GET 请求时,信息通过 URL 本身传递:

GET api/employees/{id} 

这个 GET 请求根据传递的 ID 获取员工信息。使用 GET 请求的优势在于它轻量级,所有必要的信息都会通过 URL 或头信息本身传递,如下面的图所示:

GET 方法

PUT 方法

PUT 方法用于创建资源或更新它。PUT 是一个幂等操作,这意味着即使多次执行,预期的行为也不会改变:

PUT 方法

POST 方法

您可以使用 POST 来创建或更新资源。通常,POST 用于创建资源而不是更新它。根据 HTTP 标准,每次创建新资源时,都应该返回一个 201 HTTP 状态码:

POST 方法

DELETE 方法

DELETE 方法用于删除资源。通常,当你删除资源时,你会传递一个 ID 作为参数,并且你不会在请求体中传递任何内容:

DELETE 方法

通常,HTTP 服务会被其他应用程序和服务消费。消费服务的应用程序被称为客户端。测试 HTTP 服务的一个选项是构建客户端。但这会耗费时间,一旦测试了 HTTP 服务,我们可能会丢弃客户端代码。

另一个广泛使用的选择是使用能够让我们发送 HTTP 请求并监控响应的应用程序。有许多应用程序可供选择,Fiddler 就是其中广泛使用的一个。

Fiddler 工具

Fiddler 是一个代理服务器应用程序,用于监控 HTTP 和 HTTPS 流量。您可以监控从客户端发送到服务器的请求,发送到客户端的响应,以及从服务器接收到的响应。这就像看到服务器和客户端之间管道中的流量。您甚至可以编写请求,发送它,并分析收到的响应,而无需为服务编写客户端。

您可以在 www.telerik.com/fiddler 下载 Fiddler。您将看到以下窗口:

Fiddler 工具

理论已经足够。让我们使用 ASP.NET Web API 创建一个简单的 Web 服务。

启动 Visual Studio 2015:

Fiddler 工具

当您点击 确定 时,将创建一个 Web API 解决方案。就像 ASP.NET Core 应用程序控制器从 Controller 类继承一样。

Fiddler 工具

Web API 类也将继承相同的 Controller 类。这是 ASP.NET Core 与早期版本的 ASP.NET MVC 之间的区别。在早期版本中,所有 Web API 控制器类都继承自ApiController类。在 ASP.NET 5 中,它已经统一,相同的基类 Controller 被用于构建 Web 应用程序和服务。

以下是在创建项目时选择Web API模板选项时默认创建的ValuesController类:

Fiddler 工具

在我们创建自己的自定义 Controller 之前,让我们分析默认的 API Controller。在ValuesController文件中,已经定义了几个 API 方法。

有两个重载的GET方法——一个带有参数,另一个不带参数。不带参数的GET方法返回该类型的所有资源。在这种情况下,我们只返回几个字符串。在现实世界中,我们会返回资源的元数据。例如,如果我们对电影 API 控制器发起GET请求,它会返回关于所有电影的信息。带有id参数的GET方法返回与传递的 ID 匹配的资源。例如,如果你传递一个电影 ID,它会返回关于该电影的信息。其他方法(如PUTPOSTDELETE)的正文在这个控制器中为空,我们将在稍后讨论这些方法。

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

Fiddler 工具

默认情况下,它会向api/values发起请求,并在浏览器中显示这些值。

让我们学习如何在 Fiddler 应用程序中发起 HTTP 请求。打开 Fiddler 应用程序。在左下角,选择红色方框中的Web 浏览器选项。选择此选项将使我们能够查看来自Web 浏览器的流量:

Fiddler 工具

选择作曲家选项卡,输入 URL http://localhost:49933/api/values,如以下截图所示,然后在右上角点击执行按钮:

Fiddler 工具

一旦你点击执行按钮,就会创建一个 HTTP 会话,在左侧面板中可见(如蓝色方框所示)。点击会话,然后在右上角面板中选择检查器选项卡。在右下角面板中选择 JSON 选项卡(如下面的截图所示,由紫色边框突出显示)。

你可以看到 HTTP 请求返回的 JSON 数据——以下截图中的value1value2

Fiddler 工具

现在轮到我们编写自定义 API 了。

在这个自定义 API 中,我们将提供创建员工对象、列出所有员工对象以及删除员工对象的 API 方法。

首先,让我们为员工创建一个模型。我们需要创建一个文件夹来存放这些模型。右键单击项目,选择 添加 | 新建文件夹,并将文件夹命名为 Models

Fiddler 工具

右键单击 Models 文件夹,选择 添加 | 新建项… 来创建一个员工模型类。这个员工模型类只是一个 POCO 类。请看以下代码:

public class Employee
{
  public int Id {get; set;}
  public string FirstName {get; set;}
  public string LastName {get; set;}
  public string Department {get; set;}
}

然后,我们定义存储库接口来处理模型:

public interface IEmployeeRepository
{
  void AddEmployee(Employee e);
  IEnumerable<Employee> GetAllEmployees();
  Employee GetEmployee(int id);
  Employee RemoveEmployee(int id);
  void UpdateEmployee(Employee employee);
}

然后,我们为这个模型实现接口:

public class EmployeeRepository : IEmployeeRepository
{
  private static List<Employee> employees = new List<Employee>();

  public EmployeeRepository()
  {

    Employee employee1 = new Employee
    {
      FirstName = "Mugil",
      LastName = "Ragu",
      Department = "Finance",
      Id = 1
    };

   Employee employee2 = new Employee
   {
      FirstName = "John",
      LastName = "Skeet",
      Department = "IT",
      Id = 2
   };

  employees.Add(employee1);
  employees.Add(employee2);
  }

  public IEnumerable<Employee> GetAllEmployees()
  {
    return employees;
  }

  public void AddEmployee(Employee e)
  {
    e.Id = GetNextRandomId();
    employees.Add(e);
  }

  public Employee GetEmployee(int id)
  {
    return employees.Where(emp => emp.Id == id).FirstOrDefault();
  }

  public Employee RemoveEmployee(int id)
  {
    Employee employee = employees.Where(emp => emp.Id ==   id).FirstOrDefault();
    if (employee !=null )
    {
      employees.Remove(employee);
    }
    return employee;
  }

  public void UpdateEmployee(Employee emp)
  {
    Employee employee = employees.Where(e => e.Id ==  emp.Id).FirstOrDefault();
    if(employee != null)
    {
    employee.Department = emp.Department;
    employee.FirstName = emp.FirstName;
    employee.LastName = emp.LastName;
    }
  }

  private int GetNextRandomId()
  {
    int id = -1;
    bool isIdExists;
    Random random = new Random();
    do
    {
      id = random.Next();
      isIdExists = employees.Any(emp => emp.Id == id);
    } while (isIdExists);
    return id;
  }
}

在实现类中需要注意以下几点:

  • 我们决定不使用数据库,因为我们的目标是使用 Web API 创建一个 HTTP 服务,而不是编写数据访问代码。

  • 我们正在使用内存中的列表来存储数据。所有操作都将在这个列表上执行。实际上,数据可以是任何形式,从关系型数据库到简单的内存列表。

  • 在构造函数方法中,我们正在将一个对象添加到列表中。这个列表将作为我们 HTTP 服务的数据库。

  • GetAllEmployees API 方法将返回所有员工作为 IEnumerable 接口。

  • AddEmployee 方法会将传入的员工(作为参数)添加到列表中。

  • GetEmployee 方法将返回与参数 ID 匹配的员工。

  • RemoveEmployee 方法将从列表中删除员工。

  • UpdateEmployee 方法将更新员工信息。

  • GetNextRandomId 方法将返回下一个可用的随机整数。这个整数值被用来生成员工 ID。

依赖注入

在大多数实际项目中,我们不会在任何一个控制器中使用 new 实例化任何对象,原因是我们不希望依赖组件(控制器和存储库之间)之间有紧密耦合。相反,我们向控制器传递一个接口,当需要为控制器创建对象时,依赖注入容器(如 Unity)会为我们创建对象。这种设计模式通常被称为 控制反转

假设有一个名为 ClassA 的类使用了另一个名为 ClassB 的类。在这种情况下,ClassA 只需要知道 ClassB 的行为、方法和属性,而不需要知道 ClassB 的内部实现细节。因此,我们可以抽象 ClassB 并将其转换为接口,然后使用这个接口作为参数,而不是具体的类。这种方法的优点是,只要它实现了一个共同约定的合同(接口),我们就可以在运行时传递任何类。

在 ASP.NET 5(包括 ASP.NET Core 和 Web API)中,我们内置了对依赖注入的支持。在ConfigureServices方法中,我们添加了(以粗体突出显示)执行依赖注入的行。我们指示内置的依赖注入容器在引用IEmployeeRepository接口的地方创建EmployeeRepository类,并且我们还指示它为单例;这意味着相同的对象(由依赖注入容器创建)将在整个应用程序的生命周期中共享:

public void ConfigureServices(IServiceCollection services)
{
  // Add framework services.
  services.AddApplicationInsightsTelemetry(Configuration);
  services.AddMvc();

 services.AddSingleton<IEmployeeRepository, EmployeeRepository>(); 

}

在前面的代码中,我们使用了单例模式进行依赖注入,它只在第一次请求时创建服务。还有其他类型的生命周期服务,如TransientScoped。瞬态生命周期服务每次请求时都会创建,而作用域生命周期服务每次请求都会创建一次。以下是在使用此类生命周期时创建的代码片段:


services.AddTransient

 <IEmployeeRepository, EmployeeRepository>(); 

services.AddScoped  <

IEmployeeRepository, EmployeeRepository>();

现在是时候进入创建 API 控制器的核心操作了。在Controllers文件夹上右键单击,然后选择Add | New Item。然后从列表中选择Web API Controller Class,如下所示截图。命名你的控制器,然后点击Add按钮:

依赖注入

从控制器中删除生成的代码,并添加以下构造函数:

public EmployeeController(IEmployeeRepository employeesRepo)
{
  employeeRepository = employeesRepo;
}
private IEmployeeRepository employeeRepository {get; set;}

在前面的构造函数中,我们正在注入依赖。在调用此构造函数时,将创建EmployeeRepository对象。

让我们实现几个GET方法——第一个将返回所有员工的详细信息,第二个GET方法将根据传递的员工 ID 返回员工:

public IEnumerable<Employee> GetAll()
{
  return employeeRepository.GetAllEmployees();
}

[HttpGet("{id}",Name ="GetEmployee")]
public IActionResult GetById(int id)
{
  var employee = employeeRepository.GetEmployee(id);
  if(employee == null)
  {
  return NotFound();
  }
  return new ObjectResult(employee);
}

让我们从 Fiddler 中调用这些 HTTP 方法。

运行解决方案,打开 Fiddler 应用程序,并点击Composer标签。

选择 HTTP 方法(我们选择了GET方法,因为我们有一个 GET API 方法)并输入 URL http://localhost:49933/api/employee

请注意,当我的应用程序运行时,它运行在端口49933上;你的端口号码可能会有所不同,因此请相应地构造你的 URL。

一旦你输入了 URL 并选择了方法,点击以下截图所示的Execute按钮:

依赖注入

一旦你点击Execute按钮,将创建一个 HTTP 会话,并发送请求。

点击左侧面板上的会话(如下所示截图),然后在右侧面板中选择Inspectors标签。你可以在底部右侧面板的JSON标签中查看结果:

依赖注入

让我们再发送一个 HTTP 请求来获取特定员工的信息,比如说 ID 为 2 的员工。我们将通过附加 ID 来构造 URL,如下所示:http://localhost:49933/api/employee/2

依赖注入

选择最近创建的 HTTP 会话并点击它:

你可以在右侧面板中看到以 JSON 格式的结果。

现在,我们将向我们的服务添加 CreateUpdateDelete 操作。首先,我们将提供创建功能以将员工添加到我们的服务中:

[HttpPost]
public IActionResult Add([FromBody] Employee emp)
{
  if (emp == null)
  {
    return BadRequest();
  }
  employeeRepository.AddEmployee(emp);
  return CreatedAtRoute("GetEmployee", new { id = emp.Id }, emp);
}

在遵循前面的 Add 方法时,应考虑以下要点:

  1. 我们将 Employee 对象作为参数传递。我们指示 Add 方法通过指定 [FromBody] 属性从请求体中获取该对象:

    • 如果没有传递员工对象,我们将向调用客户端返回错误请求。

    • 如果它不为空,我们将调用存储库方法将员工添加到我们的列表中(在现实世界中,我们会将其添加到数据库中)。

  2. 一旦我们添加了员工,当创建新资源时,我们将返回 201 状态码(根据 HTTP 标准)。

打开 Fiddler 应用程序并按照以下步骤添加员工:

  1. 选择 HTTP 方法为 POST 并输入 URL http://localhost:54504/api/employee/

  2. 你需要在请求头中指定内容类型为 application/json。请参阅以下截图,其中我们在请求头中添加了 Content-Type: application/json

  3. 如代码所述,我们必须以 JSON 格式在请求体中传递员工对象。在以下请求中,我们创建了一个包含 Employee 对象属性的 JSON,括号内为值 { "FirstName" : "James", "LastName" : "Anderson","Department" : "IT"}:依赖注入

一旦你已编写完请求,你可以点击 执行 按钮来发送请求。这将返回 201 HTTP 状态码,这是创建新资源的标准 HTTP 响应:

依赖注入

一旦我们在服务器上创建了资源,我们将重定向响应以获取新创建的资源。这发生在我们调用 CreatedAtRoute 方法并将新创建的员工 ID 作为参数传递时。

点击左侧的会话并选择右侧面板中的 检查器 选项卡。现在你可以看到请求的响应。响应包含在服务器上新创建的 Employee 对象。我们必须注意,Employee 对象的 ID 是在服务器上生成的,并在以下响应中可用。在这种情况下,为员工生成的 ID 是 1771082655

依赖注入

在上一个 Fiddler 窗口的右下角面板中,我们可以看到新创建资源的完整 JSON 响应。

现在我们将添加一个 Web API 方法来更新资源。更新资源的方法与创建资源的方法非常相似,只有一些细微差别。当我们创建资源时,我们使用了 HTTP POST 方法,而当我们更新资源时,我们使用了 HTTP PUT 方法。

如果传递的员工 ID 在存储库中找不到,我们将返回 404 错误 响应,这是未找到资源的 HTTP 标准错误响应。

以下是为更新资源而编写的 Web API 控制器方法代码:

[HttpPut]
public IActionResult Update([FromBody] Employee emp)
{
  if( emp == null)
  {
    return BadRequest();
  }
  Employee employee = employeeRepository.GetEmployee(emp.Id);
  if(employee == null)
  {
    return NotFound();
  }
  employeeRepository.UpdateEmployee(emp);
  return new NoContentResult();
}

以下是为更新员工信息的存储库层代码:

public void UpdateEmployee(Employee emp)
{
  Employee employee = employees.Where(e => e.Id == emp.Id).FirstOrDefault();
  if (employee != null)
  {
    employee.Department = emp.Department;
    employee.FirstName = emp.FirstName;
    employee.LastName = emp.LastName;
  }
}

打开 Fiddler 应用程序,并编写一个 HTTP PUT 请求。由于我们将在请求体中传递 Employee 对象,因此需要指定内容类型为 application/json。在请求体中,我们需要以 JSON 格式提供 Employee 对象,如下截图所示:

依赖注入

当你点击 执行 按钮,将触发 HTTP PUT 请求,并且我们的 Web API 方法将被调用。一旦成功,将返回 HTTP 204 响应:

依赖注入

删除方法

当删除资源时,应使用 HTTP DELETE 方法。请求体中无需传递任何内容。

删除资源的 Web API 方法

Delete Web API 方法有一个 void 返回类型,它将返回 HTTP 200 响应:

[HttpDelete("{id}")]
public void Delete(int id)
{
  employeeRepository.RemoveEmployee(id);
}

删除员工数据的 Web 存储库层代码

在以下存储库层方法中,我们正在从员工的内部列表中删除(其 ID 与传递的参数匹配)员工。但在现实生活中,我们会与数据库交互以删除该特定员工。考虑以下代码:

public Employee RemoveEmployee(int id)
{
  Employee employee = employees.Where(emp => emp.Id ==   id).FirstOrDefault();
  if(employee != null)
  {
    employees.Remove(employee);
  }
  return employee;
}

打开 Fiddler 应用程序,选择 DELETE HTTP 方法,传递带有参数的 URL,然后点击 执行 按钮。请注意,我们不在请求头中传递内容类型,因为我们没有在请求体中传递任何员工对象:

删除员工数据的 Web 存储库层代码

由于我们返回的是空值,Web API 的 DELETE 方法返回 HTTP 200 状态,如 Fiddler 应用程序的左侧窗格所示:

删除员工数据的 Web 存储库层代码

摘要

在本章中,你学习了 HTTP 服务及其用途。我们讨论了如何使用 Web API 设计和实现 HTTP 服务。我们使用 Fiddler 工具构建 HTTP 请求并获取响应。我们还学习了如何编写 Web API 方法以执行 CRUD 操作,从编写 Web API 方法到发送请求并获取响应。

读累了记得休息一会哦~

公众号:古德猫宁李

  • 电子书搜索下载

  • 书单分享

  • 书友学习交流

网站:沉金书屋 https://www.chenjin5.com

  • 电子书搜索下载

  • 电子书打包资源分享

  • 学习资源分享

第十一章. 提高 ASP.NET Core 应用程序的性能

当你考虑经常访问的应用程序(我们每天使用的那些),例如 Google、YouTube 和 Facebook 时,正是这些应用程序的性能使它们与类似的应用程序区分开来。思考一下。如果 Google 在提供搜索结果时需要超过 10 秒,大多数人会转而使用 Bing 或其他搜索引擎。因此,性能是应用程序成功的一个主要因素。

在本章中,我们将学习以下内容:

  • 分析应用程序性能问题的方法

  • 如何利用浏览器开发者工具分析应用程序的性能

  • 用户界面层性能改进

  • 网络应用层性能改进

  • 数据库层性能改进

通常,当人们谈论应用程序的性能时,他们会想到应用程序的速度。虽然速度对应用程序的性能有重大贡献,但我们还需要考虑应用程序的可维护性、可扩展性和可重用性。

一份维护良好的代码将更加清晰,并且技术债务较少,这反过来又会提高开发者的生产力。当我们基于面向服务的架构或微服务编写代码时,我们的代码将更容易被他人使用。这也会使我们的代码更具可扩展性。

通常,当应用程序的开发几乎完成,试点用户对应用程序的速度表示不满时,人们才会考虑应用程序的性能。讨论性能的正确时机是在应用程序开发之前;我们需要与产品所有者、业务分析师和实际用户合作,以确定应用程序可接受性能水平的标准。然后我们以这个预期的性能水平为目标进行设计和编码。

这也取决于应用程序的领域。例如,一个关键任务型医疗保健应用程序会要求极高的性能(他们可能期望在不到一秒内得到响应),而后台应用程序的性能可能不需要那么高。因此,了解我们正在工作的领域至关重要。

如果你被要求调整现有应用的性能,了解应用的现有架构也同样重要。使用 ASP.NET Core,你可以构建一个简单的 CRUD 应用,也可以构建一个服务于全球数百万用户的任务关键型应用。大型应用可能还有许多其他组件,例如负载均衡器、独立的缓存服务器、内容分发网络CDN)、一组从属 DB 服务器等等。因此,在分析应用性能时,首先你需要研究架构,分析涉及的每个单独组件,测量每个组件的性能,并在应用不符合你可接受的性能时尝试优化它们。主要的事情不是在没有研究和分析应用架构的情况下直接跳到性能改进技术。如果你正在创建一个新应用,你可以在应用创建的初期就考虑性能问题。

我们将检查一个典型的 Web 应用设置,如下面的截图所示。然后我们将分析它并考虑如何改进:

提高 ASP.NET Core 应用性能

以下步骤展示了使用 Web 应用的过程:

  1. 用户通过浏览器(如 Internet Explorer、Firefox 或 Chrome)访问 ASP.NET Core 网络应用。当用户在浏览器中输入 URL 并按下 Enter 键时,浏览器会创建一个会话并触发 HTTP 请求。这并不特定于 ASP.NET Core 应用。这种行为对所有基于不同技术构建的 Web 应用都是相同的。

  2. 请求到达 Web 服务器。如果这是一个简单请求,Web 服务器本身将处理该请求。提供静态 HTML 文件是这种类型的典型例子。如果请求稍微复杂一些,例如基于业务逻辑返回一些数据,请求将被转发到应用服务器。

  3. 应用服务器将查询数据库以获取数据。然后它可能在将数据返回给 Web 服务器之前对收到的数据进行一些业务处理。有时,Web 服务器可能作为较小 Web 应用的应用服务器。

  4. 然后,Web 服务器将返回响应,通常是 HTML 格式,给请求的客户。

因此,我们可以将这些组件分为三个层次——UI 层、Web/应用层和 DB 层。为了提高 ASP.NET Core 应用的整体性能,我们需要彻底了解如何提高每一层的性能。

在实施任何性能改进技术之前,我们首先需要分析应用中每一层的性能。只有在此基础上,我们才能提出提高应用整体性能的方法。

UI 层

UI 层代表浏览器和服务器之间发生的所有事件(以及相关内容)。有许多事件,包括但不限于以下内容:

  • 发送 HTTP 请求

  • 获取响应

  • 下载资源

  • 在浏览器中渲染它们

  • 任何 JavaScript 代码执行

减少 HTTP 请求的数量

一个典型的网页可能不仅仅包含 HTML 内容。它可能包含对 CSS 文件、JS 文件、图片或其他资源的引用。因此,当你尝试访问一个网页时,客户端将为每个这些引用发送 HTTP 请求,并将这些引用从服务器下载到客户端。

当你想分析客户端发出的 HTTP 请求时,浏览器开发者工具非常有用。大多数浏览器都有你可以使用的开发者工具。

当你在 Internet Explorer 中按下F12键时,开发者工具窗口将在 Internet Explorer 窗口的底部打开,如下面的截图所示:

减少 HTTP 请求的数量

点击网络标签页。在浏览器中输入 URL 之前,点击开始按钮(绿色播放按钮),或者点击绿色播放按钮并刷新页面:

减少 HTTP 请求的数量

一旦按下网络标签页的启动按钮,Internet Explorer 的网络标签页将监听来自当前标签页的所有请求。每个请求将包含信息,例如 URL、协议、方法、结果(HTTP 状态码)以及其他信息。

我再次运行了应用程序,并开启了(跟踪网络请求选项),可以看到请求被跟踪,如下面的截图所示:

减少 HTTP 请求的数量

网络标签页中有很多有用的数据。首先,URL 列显示了正在访问的资源。正如其名称所暗示的,协议列显示了用于访问资源的协议。

首先,URL 列显示了正在访问的资源。正如其名称所暗示的,协议列显示了用于访问资源的协议。方法列显示了请求的类型,而在结果列中,我们可以看到请求的 HTTP 状态码(HTTP 200 响应表示成功的 GET 请求)。

类型列显示了正在访问的资源类型,而耗时列显示了从服务器接收文件所需的时间。接收列显示了作为请求一部分下载的文件大小。

使用 GZip 压缩

当你提供服务时,你可以使用 GZip 压缩内容,这样就可以通过网络发送更少的数据。你需要添加适当的 HTTP 头信息,以便浏览器可以理解正在发送的内容模式。在 IIS 中,默认情况下为静态资源启用此选项。你可以通过访问路径C:\Windows\System32\inetsrv\config下的applicationHost.config文件来验证这一点:

<httpCompression directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files">
  <scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll" />
  <staticTypes>
    <add mimeType="text/*" enabled="true" />
    <add mimeType="message/*" enabled="true" />
    <add mimeType="application/x-javascript" enabled="true" />
    <add mimeType="application/atom+xml" enabled="true" />
    <add mimeType="application/xaml+xml" enabled="true" />
    <add mimeType="*/*" enabled="false" />
  </staticTypes>
</httpCompression>

如果在你的applicationHost.config文件中不可用,你必须进行必要的更改。

使用内容分发网络(CDN)

内容分发网络(Content Delivery Network,CDN)是一个分布在全球各地的服务器系统,根据内容访问的地理位置来提供服务。亚马逊的CloudFront是 CDN 的一个例子。亚马逊在全球各地都有边缘位置(服务器所在的位置),以便从最近的位置向用户提供服务。

在以下行中,我们是从官方 jQuery 网站提供的 CDN 中访问 jQuery:

<script src="img/jquery-3.1.1.min.js" ></script>

尽可能使用 JavaScript

如果你可以使用 JavaScript 来实现一个功能,那么就去做。例如,在服务器上验证表单数据之前,总是先尝试进行客户端验证。这种方法有几个优点——网站将会非常快,因为所有的事情都是在客户端完成的,服务器将处理更多的请求,因为一些请求是在客户端处理的。

尽可能使用 JavaScript

使用 CSS 样式表

由于浏览器是逐步渲染网页的(浏览器会在收到内容后立即显示它),因此将样式表放在顶部而不是网页的末尾会更好。如果我们把样式表放在底部,它会阻止渐进式渲染,因为浏览器需要用样式重新绘制内容。

大多数浏览器在下载 JavaScript 文件时都会阻止并行下载,因此最好将脚本放在底部。这意味着在浏览器下载脚本的同时,用户可以看到你的内容。以下是在 ASP.NET Core 应用程序中创建的示例布局文件,其中 CSS 文件在顶部引用,JavaScript 文件在底部引用:

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

    <!-- Latest compiled and minified CSS -->

    <link rel="stylesheet"  href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">

    <!-- Optional theme -->

    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap-theme.min.css" integrity="sha384-fLW2N01lMqjakBkx3l/M9EahuwpSfeNvV63J5ezn3uZzapT0u7EYsXMjQV+0En5r" crossorigin="anonymous">

  </head>
  <body>
    <div>
      @RenderBody()
    </div>
    <script src="img/jquery-2.2.3.js"></script>

    <script src="img/jquery.validate.min.js"></script>

    <script src="img/jquery.validate.unobtrusive.min.js"></script>

    <!-- Latest compiled and minified JavaScript -->
    <script src="img/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>

  </body>
</html>

JavaScript 和 CSS 文件的压缩及其合并

下载网页相关资源所需的时间与下载的文件大小成正比。如果我们在不改变实际内容的情况下减小文件大小,这将大大提高性能。压缩是改变文件内容以减小文件大小的过程。移除多余的空白和将变量名更改为更短的名字都是在压缩过程中常用的技术。

如 jQuery 和前端框架等流行的 JavaScript 库默认提供压缩文件。你可以直接使用它们。在下面的屏幕截图中,我已下载了 jQuery 的压缩版本。你可以压缩你为应用程序编写的自定义 JavaScript 和 CSS 文件:

JavaScript 和 CSS 文件的压缩及其组合

捆绑是将两个或多个文件合并为一个的过程。当捆绑和压缩一起使用时,将减少负载的大小,从而提高应用程序的性能。

你可以从以下 URL 安装Bundler & Minifier Visual Studio 扩展:

visualstudiogallery.msdn.microsoft.com/9ec27da7-e24b-4d56-8064-fd7e88ac1c40

一旦安装了这个 Visual Studio 扩展,你可以通过选择文件并从右键点击弹出的上下文菜单中选择Bundler & Minifier选项来选择你想要捆绑和压缩的文件。如下面的屏幕截图所示:

JavaScript 和 CSS 文件的压缩及其组合

一旦选择捆绑和压缩文件选项,它将要求你保存捆绑文件,如下面的屏幕截图所示:

JavaScript 和 CSS 文件的压缩及其组合

你可以命名你想要的文件并保存文件。一旦保存文件,你的解决方案中就会创建另一个文件——在我们的例子中,它是bundleconfig.json文件:

JavaScript 和 CSS 文件的压缩及其组合

此文件将包含输入文件和捆绑输出文件的信息。以下是一个示例:

[
  {
    "outputFileName": "wwwroot/css/site.min.css",
    "inputFiles": [
    "wwwroot/css/site.css"
    ]
  },

{
  "outputFileName": "wwwroot/js/site.min.js",
  "inputFiles": [
  "wwwroot/js/site.js"
  ],
  "minify": {
  "enabled": true,
  "renameLocals": true
  }
},

{
  "outputFileName": "wwwroot/css/bundle.css",
  "inputFiles": [
  "wwwroot/css/site.css",
  "wwwroot/css/StyleSheet1.css"
    ]
  }
]

你可以在应用程序中使用这个捆绑文件,从而提高性能。

缓存过程

缓存是将数据复制到内存中,而不是再次通过外部资源(如网络、文件或数据库)获取数据的过程。缓存中使用的数据是短暂的,可以随时删除。因为我们直接访问数据,所以缓存可以大大提高应用程序的性能。

缓存可以在任何层进行——在浏览器端的客户端,在代理服务器(或某些中间件)上,或者在 Web/应用程序服务器上。对于数据库层缓存,我们可能不需要进行任何自定义编码。根据使用的数据库服务器类型,可能需要做一些配置更改。然而,如今的大多数数据库都有足够强大的功能,可以在需要时缓存数据。

客户端缓存

如果我们添加适当的 HTTP 响应头,我们可以在客户端进行缓存。例如,如果我们想缓存所有静态资源,如 CSS、图像和 JavaScript 文件,我们可以在Cache-Control头中添加max-age响应头:

客户端缓存

在上一张开发者工具窗口的网络选项卡截图,当再次发起请求时,我们得到HTTP 304 响应(未修改)。这意味着相同的文件不会在网络上两次传输,因为它们已经在浏览器中可用。

实现静态文件的浏览器缓存相当简单,只需要几个步骤——添加依赖项和配置应用程序。

将以下NuGet包添加到project.json文件中的依赖项列表中:

"Microsoft.AspNet.StaticFiles": "1.0.0-rc1-final" 

将以下命名空间添加到Startup.cs文件中,并配置应用程序使用这些静态文件:

using Microsoft.AspNet.StaticFiles;
using Microsoft.Net.Http.Headers;

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

 app.UseStaticFiles(new StaticFileOptions

() 

 { 

      OnPrepareResponse = (context) => 

 { 

 var headers = context.Context.Response.GetTypedHeaders(); 

 headers.CacheControl = new CacheControlHeaderValue() 

 { 

 MaxAge = TimeSpan.FromSeconds(60), 

 }; 

} 

 }); 

}

响应缓存

在响应缓存中,当 MVC 动作返回时,会在 HTTP 响应中添加与缓存相关的 HTTP 头。Cache-Control头是添加到响应的主要 HTTP 头。

响应缓存

上一张图展示了响应缓存的工作原理。在第一次请求中,我们调用控制器(Controller)的动作方法;请求来自客户端,并通过代理服务器,实际上击中(访问)了 web 服务器。由于我们添加了响应缓存,任何后续的请求将不会转发到 web 服务器,响应将由代理服务器本身返回。这将减少对 web 服务器的请求次数,从而减少对 web 服务器的负载。

缓存控制器动作方法的响应相当简单。只需添加带有持续时间参数的ResponseCache属性。在以下动作方法中,我们添加了持续时间为 60 秒的响应缓存,因此,在接下来的 60 秒内,如果再次有请求,响应将直接从代理服务器返回,而不是发送到 web 服务器:

[ResponseCache(Duration = 60)]
public IActionResult Index()
{
  EmployeeAddViewModel employeeAddViewModel = new   EmployeeAddViewModel();
  using (var db = new EmployeeDbContext())
  {
    employeeAddViewModel.EmployeesList = db.Employees.ToList();
  }
  return View(employeeAddViewModel);
}

网络应用层

网络应用层由从接收客户端请求到发送响应(或查询数据库层以获取所需数据)之间发生的一切组成。网络应用层的大部分内容将使用服务器端语言,如 C#,因此当您尝试优化网络应用层时,需要结合 ASP.NET MVC 和 C#的最佳实践。

视图中没有业务逻辑

视图是渲染到浏览器的内容,它可以包含表示逻辑。表示逻辑表示数据要显示的位置和方式。视图模型(实际上是针对视图的特定模型)是包含特定视图数据的模型。

视图(Views)和视图模型(ViewModels)都不应包含任何业务逻辑,因为这违反了关注点分离原则。

看看下面的 Razor 视图代码。我们只是在模型中的列表上进行循环,并以表格格式展示数据——没有其他内容:

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

在某些代码中,视图模型中可能会有一个仓库层(repository layer),这绝不应该发生。请特别小心查看 View/ViewModel 代码中的内容。

使用异步日志记录

尽可能地使用异步日志记录来提高性能。大多数日志框架,如 Log4Net,提供了异步日志记录的选项。就 ASP.NET Core 而言,你可以通过依赖注入来实现日志记录。

以下是在 MVC 控制器中实现日志框架的典型示例:

public class EmployeeController : Controller
{
  private readonly IEmployeeRepository _employeeRepo;
  private readonly ILogger<EmployeeController> _logger;
  public EmployeeController(IEmployeeRepository employeeRepository,
  ILogger<EmployeeController> logger)
  {
    _employeeRepo = employeeRepository;
    _logger = logger;
  }
  [HttpGet]
  public IEnumerable<Employee> GetAll()
  {
    _logger.LogInformation(LoggingEvents.LIST_ITEMS, "Listing all employees");
    return _employeeRepo.GetAll();
  }
}

数据库层

虽然数据库层与 ASP.NET Core 应用程序没有直接关系,但开发者有责任完全掌控应用程序的性能,这包括关注数据库的性能。现在我们将探讨在提高 ASP.NET Core 应用程序性能时需要考虑的数据库层的一些区域。

理解 ORM 生成的查询

在当今的大多数应用程序中,我们使用 对象关系映射 ( ORM ),例如 Entity Framework 或 NHibernate。正如你可能知道的,ORM 的主要目标是让你能够使用基于域的类和对象来编写数据访问层,而不是直接编写查询。但这并不意味着你永远不需要理解生成的 SQL 查询的基本知识,或者这些查询的优化。有时,Entity Framework 生成的查询可能没有优化,因此更好的做法是运行分析器,分析生成的查询,并根据你的需求进行调整。你可以使用 Entity Framework 中的拦截器来记录 SQL 查询。

如果你真的想使用经典 ADO.NET

ASP.NET Core 只是一个 Web 开发框架,它与任何数据访问框架或技术都没有绑定。如果你在应用程序中使用的 ORM 无法提供你期望的性能,你可以使用经典的 ADO.NET 并手动编写查询/存储过程。

只返回所需数据

总是只返回你需要的数据,不多也不少。这种方法减少了我们在网络中发送的数据量(从数据库服务器到 Web/应用程序服务器)。

例如,我们不会使用以下:

Select * from employees

相反,我们会使用这个:

Select FirstName,LastName from employees

后者查询只会从表中获取所需的字段,因此,只有所需的数据被传递给调用客户端。

紧凑调整索引

初学者倾向于在遇到数据库问题时添加索引。在表的每一列上添加索引是坏习惯,会降低性能。正确的方法是列出最常执行的查询。一旦你有了这个列表,尝试调整它们——移除不必要的连接,避免相关子查询等。只有在你尝试并耗尽所有查询调整选项后,你才开始添加索引。这里要注意的重要一点是,你应该只在所需的列数上添加索引。

为你的数据库列使用正确的列类型和大小

当你想要将 int 用作列的数据类型时,使用整数。不要使用 double。如果你表中有大量行,这将节省很多空间。

避免使用相关子查询

相关子查询使用其父查询的值,这反过来又使得它逐行运行。这会显著影响查询性能。

以下是一个相关子查询的示例:

SELECT e.Name, 
e.City, 
(SELECT DepartmentName FROM EmployeeDepartment WHERE ID = e.DepartmentId) 
AS DepartmentName 
FROM Employee e

通用性能改进技巧

这里有一些提高 ASP.NET Core Web 应用程序整体性能的技巧。

避免使用 Response.Redirect 方法

当我们想要进行客户端重定向时,开发者可以通过传递 URL 作为参数调用Response.Redirect方法。但这种方法存在一个小问题。如果我们使用Response.Redirect,浏览器将再次向服务器发送请求,这需要再次往返服务器。因此,如果可能的话,最好避免使用Response.Redirect方法,如果可能的话,可以使用RedirectToAction方法。

使用字符串构建器

如果你的应用程序涉及大量的字符串操作,最好使用字符串构建器而不是常规的字符串连接。字符串连接会在每次操作时创建一个新的字符串对象,而字符串构建器则直接在单个对象上操作。当我们在大规模字符串操作中使用字符串构建器时,可以显著提高性能。

摘要

在本章中,我们学习了如何分析 Web 应用程序的性能以及提高性能时应该针对哪些层。然后我们讨论了如何在每个层中提高性能——UI 层、Web/应用程序层和数据库层。

第十二章。ASP.NET Core Identity

安全性对所有类型的应用程序都是必不可少的,包括 Web 应用程序。如果任何人都可以通过冒充您来更新您的状态,您会使用 Facebook 吗?如果这是可能的,那么没有人会再回到 Facebook。从这个例子中,我们可以看到安全性与其说是一个特性,不如说是一个对所有应用程序的必需品。

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

  • 认证和授权

  • ASP.NET Identity

  • 如何在 ASP.NET Core 应用程序中使用 ASP.NET Identity 和 Entity Framework 实现安全性

当我们谈论应用程序的安全性时,我们主要希望防止任何未经授权的访问,这意味着只有有权访问信息的人才能访问它——不多也不少。

在继续之前,我想澄清一些关于安全性的核心概念。

认证

认证是验证用户是否有权访问系统的过程。在任何应用程序中,用户首先都会进行认证。这可以通过要求用户输入他们的用户 ID 和密码来实现。

授权

授权是验证用户是否有权访问请求的资源的过程。他们可能对系统有合法的访问权限,但他们可能没有访问请求资源的权限,因为他们没有所需的访问权限。例如,只有管理员用户可以访问应用程序的配置页面,而普通用户不应被允许使用此页面。

ASP.NET Identity 为保护应用程序提供了几个功能。

让我们考虑以下简单场景,其中用户试图访问 受保护页面,这是一个只有授权人员才能访问的页面。由于用户未登录,他们将被重定向到 登录页面,以便我们可以对用户进行认证和授权。认证成功后,用户将被重定向到 受保护页面。如果由于任何原因我们无法对用户进行认证和授权,我们可以将他们重定向到 “访问被拒绝”页面

授权

ASP.NET Core Identity 是一个会员系统,它使您能够轻松地保护应用程序,并具有添加登录功能到应用程序等特性。以下是我们需要遵循的步骤,以便在我们的应用程序中使用 ASP.NET Identity(与 Entity Framework 结合):

  1. 将相关依赖项添加到 project.json 文件中。

  2. 创建一个 appsettings.json 文件并存储数据库连接字符串。

  3. 创建 ApplicationUser 类和 ApplicationDbContext 类。

  4. 配置应用程序以使用 ASP.NET Identity。

  5. 创建注册和登录的 ViewModels。

  6. 创建必要的控制器和相关动作方法和视图。

将相关依赖项添加到 project.json 文件中

如果您想在应用程序中使用 ASP.NET Identity 和 Entity Framework,您需要添加以下依赖项:

"EntityFramework.Commands": "7.0.0-rc1-final", 
    "EntityFramework.MicrosoftSqlServer": "7.0.0-rc1-final", 
    "Microsoft.AspNet.Authentication.Cookies": "1.0.0-rc1-final", 

创建一个 appsettings.json 文件并存储数据库连接字符串。

在项目根级别创建一个名为 appsettings.json 的文件,如以下截图所示:

将相关依赖项添加到 project.json 文件中

将以下连接字符串存储在 appsettings.json 中。此连接字符串将由 ASP.NET Identity 用于在相关表中存储数据:

{ 
  "Data": { 
    "DefaultConnection": { 
      "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=aspnet_security;Trusted_Connection=True;MultipleActiveResultSets=true" 
   } 
  } 
} 

添加 ApplicationUser 和 ApplicationDbContext 类

创建一个 Models 文件夹和几个文件——ApplicationDbContext.csApplicationUser.cs——如以下截图所示:

添加 ApplicationUser 和 ApplicationDbContext 类

ApplicationUser 类从 AspNet.Identity.EntityFramework6 命名空间中的 IdentityUser 类继承,如下所示:

public class ApplicationUser : IdentityUser 
{
..  
} 

您可以根据应用程序的需求向用户添加属性。我没有添加任何属性,因为我希望保持简单,以展示 ASP.NET Identity 的功能。

ApplicationDbContext 类从 ApplicationUserIdentityDbContext 类继承。在构造函数方法中,我们传递 connectionstring,它最终传递到基类。

甚至 OnModelCreating 方法也被重写。如果您想更改任何表名(由 Identity 生成),可以按以下方式操作:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser> 
    { 
        public ApplicationDbContext(string nameOrConnectionString) : base(nameOrConnectionString) { } 

        protected override void OnModelCreating(DbModelBuilder modelBuilder) 
        { 
            base.OnModelCreating(modelBuilder);             
        } 
    } 

一旦我们创建了 Models 文件,我们需要配置应用程序和服务。您可以在 Startup 类中的 ConfigureConfigureServices 中配置这些。

配置应用程序以使用 Identity

为了使用 Identity,我们只需在 Startup 类的 Configure 方法中添加以下行:

app.UseIdentity(); 

以下代码显示了完整的 Configure 方法,包括对 UseIdentity 方法的调用,即 app.UseIdentity() :

public void Configure(IApplicationBuilder app, IHostingEnvironment env,  ILoggerFactory loggerFactory) 
        { 
            loggerFactory.AddConsole(Configuration.GetSection("Logging")); 
            loggerFactory.AddDebug(); 

            if (env.IsDevelopment()) 
            { 
                app.UseBrowserLink(); 
                app.UseDeveloperExceptionPage(); 
                app.UseDatabaseErrorPage(); 
            } 
            else 
            { 
                app.UseExceptionHandler("/Home/Error"); 

            app.UseIISPlatformHandler(options => options.AuthenticationDescriptions.Clear()); 

            app.UseStaticFiles(); 

            app.UseIdentity(); 

            // To configure external authentication please see http://go.microsoft.com/fwlink/?LinkID=532715 

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

ConfigureServices 方法中,我们将进行以下更改:

  • 我们将添加 ApplicationDbContext 类,其连接字符串来自 appsettings.json 文件

  • 我们将添加 UserStoreRoleStore 以使用 Identity

  • 最后,我们将要求 ASP.NET Core 在我们请求 IEmailSenderISMSSender 类时返回 AuthMessageSender

    public void ConfigureServices(IServiceCollection services 
    { 
    // Add framework services. 
    
                services.AddScoped<ApplicationDbContext>(f => { 
                    return new ApplicationDbContext(Configuration["Data:DefaultConnection:ConnectionString"]); 
                }); 
    
                services.AddIdentity<ApplicationUser, IdentityRole>() 
                    .AddUserStore<UserStore<ApplicationUser, ApplicationDbContext>>() 
                    .AddRoleStore<RoleStore<ApplicationDbContext>>() 
                    .AddDefaultTokenProviders(); 
    
                services.AddMvc(); 
    
                // Add application services. 
                services.AddTransient<IEmailSender, AuthMessageSender>(); 
                services.AddTransient<ISmsSender, AuthMessageSender>(); 
            } 
    
    

创建 ViewModels

接下来,我们将创建几个 ViewModels,我们将在我们的视图模型中使用它们。

首先,我们将创建一个包含三个属性——EmailPasswordConfirmPasswordRegisterViewModel 类。我们使用适当的属性装饰这些属性,以便我们可以使用无障碍 jQuery 验证进行客户端验证。我们将所有字段都设置为必填项,如下所示:

public class RegisterViewModel 
    { 
        [Required] 
        [EmailAddress] 
        [Display(Name = "Email")] 
        public string Email { get; set; } 

        [Required] 
        [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] 
        [DataType(DataType.Password)] 
        [Display(Name = "Password")] 
        public string Password { get; set; } 

        [DataType(DataType.Password)] 
        [Display(Name = "Confirm password")] 
        [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] 
        public string ConfirmPassword { get; set; } 
    } 

现在,我们可以创建LoginViewModel模型,用户可以使用它来登录到你的应用程序。还有一个额外的属性RememberMe,当勾选时,将允许你登录而无需再次输入密码:

public class LoginViewModel 
    { 
        [Required] 
        [EmailAddress] 
        public string Email { get; set; } 

        [Required] 
        [DataType(DataType.Password)] 
        public string Password { get; set; } 

        [Display(Name = "Remember me?")] 
        public bool RememberMe { get; set; } 
    } 

创建控制器和相关操作方法

现在我们需要创建一个AccountController类,我们将在这里定义认证和授权的操作方法:

public class AccountController : Controller 
    { 
        private readonly UserManager<ApplicationUser> _userManager; 
        private readonly SignInManager<ApplicationUser> _signInManager; 
        private readonly IEmailSender _emailSender; 
        private readonly ISmsSender _smsSender; 
        private readonly ILogger _logger; 

        public AccountController( 
            UserManager<ApplicationUser> userManager, 
            SignInManager<ApplicationUser> signInManager, 
            IEmailSender emailSender, 
            ISmsSender smsSender, 
            ILoggerFactory loggerFactory) 
        { 
            _userManager = userManager; 
            _signInManager = signInManager; 
            _emailSender = emailSender; 
            _smsSender = smsSender; 
            _logger = loggerFactory.CreateLogger<AccountController>(); 
        } 
    } 

在前面的代码中,我们正在使用不同组件提供的服务。UserManagerSignInManager由 ASP.NET Identity 提供。IEmailSenderISmsSender是我们编写的自定义类,将用于发送电子邮件和短信。我们将在本章后面更详细地了解电子邮件和短信。日志记录由 Microsoft Logging 扩展提供。以下是一个简单的登录HTTPGET方法。它只是存储访问Login方法的 URL 并返回登录页面:

[HttpGet] 
        [AllowAnonymous] 
        public IActionResult Login(string returnUrl = null) 
        { 
            ViewData["ReturnUrl"] = returnUrl; 
            return View(); 
        } 

创建视图

现在,我们将创建相应的登录视图页面。在这个视图页面中,我们只是显示了以下详细信息:

@using System.Collections.Generic 
@using Microsoft.AspNet.Http 
@using Microsoft.AspNet.Http.Authentication 
@using AspNet.Identity.EntityFramework6 

@model LoginViewModel 
@inject SignInManager<ApplicationUser> SignInManager 

@{ 
    ViewData["Title"] = "Log in"; 
} 

<h2>@ViewData["Title"].</h2> 
<div class="row"> 
    <div class="col-md-8"> 
        <section> 
            <form asp-controller="Account" asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal" role="form"> 
                <h4>Use a local account to log in.</h4> 
                <hr /> 
                <div asp-validation-summary="ValidationSummary.All" class="text-danger"></div> 
                <div class="form-group"> 
                    <label asp-for="Email" class="col-md-2 control-label"></label> 
                    <div class="col-md-10"> 
                        <input asp-for="Email" class="form-control" /> 
                        <span asp-validation-for="Email" class="text-danger"></span> 
                    </div> 
                </div> 
                <div class="form-group"> 
                    <label asp-for="Password" class="col-md-2 control-label"></label> 
                    <div class="col-md-10"> 
                        <input asp-for="Password" class="form-control" /> 
                        <span asp-validation-for="Password" class="text-danger"></span> 
                    </div> 
                </div> 
                <div class="form-group"> 
                    <div class="col-md-offset-2 col-md-10"> 
                        <div class="checkbox"> 
                            <input asp-for="RememberMe" /> 
                            <label asp-for="RememberMe"></label> 
                        </div> 
                    </div> 
                </div> 
                <div class="form-group"> 
                    <div class="col-md-offset-2 col-md-10"> 
                        <button type="submit" class="btn btn-default">Log in</button> 
                    </div> 
                </div> 
                <p> 
                    <a asp-action="Register">Register as a new user?</a> 
                </p> 
                <p> 
                    <a asp-action="ForgotPassword">Forgot your password?</a> 
                </p> 
            </form> 
        </section> 
    </div> 

</div> 

@section Scripts { 
    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } 
} 

创建视图

当用户首次登录应用程序时,他们可能没有任何登录凭证,因此我们的应用程序应该提供一个功能,让他们可以创建自己的登录账户。我们将创建一个简单的Register操作方法,该方法将仅返回一个视图,用户可以通过该视图注册自己:

[HttpGet] 
[AllowAnonymous] 
public IActionResult Register() 
{ 
    return View(); 
} 

我们还将创建相应的视图,其中包含电子邮件、密码、密码确认和Register按钮的输入控件:

@model RegisterViewModel 
@{ 
    ViewData["Title"] = "Register"; 
} 

<h2>@ViewData["Title"].</h2> 

<form asp-controller="Account" asp-action="Register" method="post" class="form-horizontal" role="form"> 
    <h4>Create a new account.</h4> 
    <hr /> 
    <div asp-validation-summary="ValidationSummary.All" class="text-danger"></div> 
    <div class="form-group"> 
        <label asp-for="Email" class="col-md-2 control-label"></label> 
        <div class="col-md-10"> 
            <input asp-for="Email" class="form-control" /> 
            <span asp-validation-for="Email" class="text-danger"></span> 
        </div> 
    </div> 
    <div class="form-group"> 
        <label asp-for="Password" class="col-md-2 control-label"></label> 
        <div class="col-md-10"> 
            <input asp-for="Password" class="form-control" /> 
            <span asp-validation-for="Password" class="text-danger"></span> 
        </div> 
    </div> 
    <div class="form-group"> 
        <label asp-for="ConfirmPassword" class="col-md-2 control-label"></label> 
        <div class="col-md-10"> 
            <input asp-for="ConfirmPassword" class="form-control" /> 
            <span asp-validation-for="ConfirmPassword" class="text-danger"></span> 
        </div> 
    </div> 
    <div class="form-group"> 
        <div class="col-md-offset-2 col-md-10"> 
            <button type="submit" class="btn btn-default">Register</button> 
        </div> 
    </div> 
</form> 

@section Scripts { 
    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } 
} 

以下是对应的注册POST操作方法。在这里,程序检查模型是否有效,如果有效,它将使用模型数据创建一个ApplicationUser对象,并调用身份验证 API(CreateAsync方法)。如果可以创建user变量,用户将使用该用户 ID 登录并重定向到Home页面:

[HttpPost] 
        [AllowAnonymous] 
        [ValidateAntiForgeryToken] 
        public async Task<IActionResult> Register(RegisterViewModel model) 
        { 
            if (ModelState.IsValid) 
            { 
                var user = new ApplicationUser { UserName = model.Email,  Email = model.Email }; 
                var result = await _userManager.CreateAsync(user,  model.Password); 
                if (result.Succeeded) 
                { 
                    await _signInManager.SignInAsync(user, isPersistent:  false); 
                    return RedirectToAction(nameof(HomeController.Index),  "Home"); 
                } 
                AddErrors(result); 
            } 

            return View(model); 
        } 

登出功能相当简单。只需调用身份验证 API 的SignoutAsync方法,然后重定向到Index页面:

[HttpPost] 
        [ValidateAntiForgeryToken] 
        public async Task<IActionResult> LogOff() 
        { 
            await _signInManager.SignOutAsync(); 
            _logger.LogInformation(4, "User logged out."); 
            return RedirectToAction(nameof(HomeController.Index), "Home"); 
       } 

回到登录功能,以下是对应的操作方法。我们调用身份验证 API 的PasswordSignInAsync方法。登录成功后,我们将重定向到登录功能访问的 URL:

[HttpPost] 
        [AllowAnonymous] 
        [ValidateAntiForgeryToken] 
        public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null) 
        { 
            ViewData["ReturnUrl"] = returnUrl; 
            if (ModelState.IsValid) 
            { 
                var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false); 
                if (result.Succeeded) 
                { 
                    return RedirectToLocal(returnUrl); 
                } 

            } 
            // If there is any error, display the form again 
            return View(model); 
        } 

电子邮件和短信服务

如果你想将电子邮件和短信服务添加到应用程序的认证功能中,你可以通过创建这里显示的接口和类来实现:

public interface IEmailSender
{
        Task SendEmailAsync(string email, string subject, string message)
  }
  public interface ISmsSender
    {
        Task SendSmsAsync(string number, string message);
    }
public class AuthMessageSender : IEmailSender, ISmsSender
    {
        public Task SendEmailAsync(string email, string subject, string message)
        {
            // We can plug in our email service here to send an email.
            return Task.FromResult(0);
        }
        public Task SendSmsAsync(string number, string message)
        {
            // We can plug in our SMS service here to send a text message.
            return Task.FromResult(0);
        }
    }

在控制器中保护操作方法

为了解释方便,让我们假设关于页面是一个受保护的页面,只有经过认证的用户才能访问它。

我们只需要在Home控制器中的About操作方法上装饰一个[Authorize]属性:

[Authorize] 
        public IActionResult About() 
        { 
            ViewData["Message"] = "This is my about page"; 
            return View(); 
        } 

进行上述更改后,当用户尝试访问登录页面而未登录应用程序时,将重定向用户到登录页面:

在控制器中保护操作方法

在下面的屏幕截图中,您将注意到 URL 中有一个额外的查询参数,ReturnURL。这个ReturnURL参数将把应用程序重定向到特定的页面(在我们的例子中,是ReturnURL参数传递的值——首页 / 关于)。

一旦登录,您将被重定向到您之前请求的页面:

在控制器中保护操作方法

当您注册新用户时,用户的详细信息将被存储在 ASP.NET Identity 创建的相关表中。

通过选择视图 | SQL Server 对象资源管理器选项打开 SQL Server 对象资源管理器窗口,如下面的屏幕截图所示:

在控制器中保护操作方法

一旦选择SQL Server 对象资源管理器选项,您将看到一个类似于以下屏幕截图的窗口。ASP.NET Identity 使用 Entity Framework 和我们之前在appsettings.json包中提供的连接字符串为我们创建数据库。

ASP.NET Identity 创建多个表来维护与身份相关的信息和 Entity Framework 的数据库迁移历史。由于我们在基本级别使用 ASP.NET Identity,除了  dbo.AspNetUsers 表外,没有任何与身份相关的表会被填充:

在控制器中保护操作方法

您可以右键单击dbo.AspNetUsers表并选择查看数据来查看数据:

在控制器中保护操作方法

由于在我们的应用程序中只注册了一个用户,因此只创建了一行。请注意,哈希密码(由 ASP.NET Identity 为我们标记)和空白密码将不会存储在表中。

读累了记得休息一会哦~

公众号:古德猫宁李

  • 电子书搜索下载

  • 书单分享

  • 书友学习交流

网站:沉金书屋 https://www.chenjin5.com

  • 电子书搜索下载

  • 电子书打包资源分享

  • 学习资源分享

摘要

在本章中,我们学习了身份验证和授权。我们还学习了如何通过逐步过程在 ASP.NET Core 应用程序中实现 ASP.NET Identity。我们还讨论了 ASP.NET Identity 中涉及的表,并学习了如何查看 ASP.NET Identity 创建的数据。

posted @ 2025-10-21 10:43  绝不原创的飞龙  阅读(5)  评论(0)    收藏  举报