网络营
下载网络营训练包
在传统的Web应用程序中,客户机(浏览器)通过请求页面启动与服务器的通信。然后,服务器处理请求,并将页面的HTML发送给客户端。在与页面的后续交互中,用户导航到链接或提交带有数据的表单 - 新的请求将发送到服务器,流程将再次启动:服务器处理请求并将新页面发送到浏览器回应客户要求的新动作。
在单页面应用程序(SPA)中,整个页面在初始请求之后加载到浏览器中,但后续的交互通过Ajax请求进行。这意味着浏览器必须仅更新已更改的页面部分; 没有必要重新加载整个页面。SPA方法减少了应用程序响应用户操作所花费的时间,从而导致更流畅的体验。
SPA的架构涉及传统Web应用程序中不存在的某些挑战。然而,像ASP.NET Web API这样的新兴技术,像AngularJS这样的JavaScript框架和CSS3提供的新的样式功能使得设计和构建SPA变得非常简单。
在这个动手实验室,您将利用这些技术来实施基于SPA概念的琐事网站Geek Quiz。您将首先使用ASP.NET Web API实现服务层,以公开所需的端点,以检索测验问题并存储答案。然后,您将使用AngularJS和CSS3转换效果构建一个丰富和响应的UI。
http://aka.ms/webcamps-trainingkit获取。
概观
目标
在这个动手实验中,您将学习如何:
- 创建一个ASP.NET Web API服务来发送和接收JSON数据
- 使用AngularJS创建响应式UI
- 通过CSS3转换增强UI体验
先决条件
完成此动手实验室需要以下内容:
建立
为了在这个动手实验室中运行练习,你需要首先设置你的环境。
- 打开Windows资源管理器并浏览实验室的“ 源”文件夹。
- 右键单击Setup.cmd,然后选择以管理员身份运行,启动安装过程,该过程将配置您的环境并安装本实验的Visual Studio代码片段。
- 如果显示“用户帐户控制”对话框,请确认该操作继续。
注意
在运行安装程序之前,请确保已经检查了该实验室的所有依赖项。
使用代码片段
在整个实验室文档中,将指示您插入代码块。为方便起见,这些代码大部分作为Visual Studio代码片段提供,您可以从Visual Studio 2013中访问,以避免手动添加。
注意
每个练习都附有一个起始解决方案,位于练习的Begin文件夹中,可以让您独立于其他练习进行每项练习。请注意,在这些开始的解决方案中缺少在练习期间添加的代码段,并且在完成练习之前可能无法正常工作。在练习的源代码中,您还将找到一个包含Visual Studio解决方案的End文件夹,其中包含完成相应练习中的步骤所产生的代码。您可以使用这些解决方案作为指导,如果您需要额外的帮助,当您通过这个动手实验室。
演习
这个动手实验室包括以下练习:
预计完成本实验的时间:60分钟
注意
当您第一次启动Visual Studio时,您必须选择一个预定义的设置集合。每个预定义的集合都被设计为匹配特定的开发风格,并确定窗口布局,编辑器行为,智能感知代码片段和对话框选项。本实验中的过程描述了在使用“ 常规开发设置”集合时在Visual Studio中完成给定任务所需的操作。如果您为开发环境选择不同的设置集合,那么您应该考虑的步骤可能会有所不同。
练习1:创建Web API
SPA的关键部分之一是服务层。它负责处理由UI发送的Ajax调用,并响应于该调用返回数据。检索的数据应以机器可读格式呈现,以便客户端进行解析和使用。
Web API框架是ASP.NET堆栈的一部分,旨在使HTTP服务变得容易,通常通过RESTful API发送和接收JSON或XML格式的数据。在本练习中,您将创建Web站点来托管Geek Quiz应用程序,然后实施后端服务,以使用ASP.NET Web API公开并保留测验数据。
任务1 - 创建Geek测验的初始项目
在此任务中,您将开始创建一个新的ASP.NET MVC项目,并支持基于Visual Studio附带的一个ASP.NET项目类型的ASP.NET Web API 。一个ASP.NET统一了所有的ASP.NET技术,并提供了根据需要进行混合和匹配的选项。然后,您将添加实体框架的模型类和数据库初始化器以插入测验问题。
-
打开Visual Studio Express 2013 for Web,然后选择File | 新项目...开始一个新的解决方案。
创建新项目
-
在“ 新建项目”对话框中,在Visual C#|下选择ASP.NET Web应用程序 网页标签。确保选择了.NET Framework 4.5,将其命名为GeekQuiz,选择位置,然后单击确定。
创建一个新的ASP.NET Web应用程序项目
-
在“ 新建ASP.NET项目”对话框中,选择MVC模板并选择Web API选项。此外,请确保“ 身份验证”选项设置为“ 个人用户帐户”。单击确定继续。
使用MVC模板创建一个新项目,包括Web API组件
-
在解决方案资源管理器中,右键单击GeekQuiz项目的Models文件夹,然后选择Add | 现有项...。
添加现有项目
-
在“ 添加现有项目”对话框中,导航到“ 源/资产/模型”文件夹,然后选择所有文件。单击添加。
添加模型资产
注意
通过添加这些文件,您将添加数据模型,Entity Framework的数据库上下文和Geek Quiz应用程序的数据库初始化程序。
实体框架(EF)是一种对象关系映射器(ORM),它使您能够通过使用概念应用程序模型编程创建数据访问应用程序,而不是直接使用关系存储架构进行编程。您可以在这里了解有关实体框架的更多信息。
以下是刚刚添加的类的描述:
- TriviaOption:表示与测验问题相关联的单个选项
- TriviaQuestion:表示一个测验问题,并通过Options属性公开相关选项
- TriviaAnswer:表示用户响应测验问题选择的选项
- TriviaContext:代表实体框架的Geek Quiz应用程序的数据库环境。该类派生自DContext,并公开表示上述实体的集合的DbSet属性。
- TriviaDatabaseInitializer:实现了继承自CreateDatabaseIfNotExists的TriviaContext类的Entity Framework初始化器。此类的默认行为是仅在不存在的情况下创建数据库,才能插入“ 种子”方法中指定的实体。
-
打开Global.asax.cs文件并添加以下using语句。
C#using GeekQuiz.Models;
-
在Application_Start方法的开头添加以下代码,将TriviaDatabaseInitializer设置为数据库初始化程序。
C#public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { System.Data.Entity.Database.SetInitializer(new TriviaDatabaseInitializer()); AreaRegistration.RegisterAllAreas(); GlobalConfiguration.Configure(WebApiConfig.Register); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); } }
-
修改Home控制器以限制对经过身份验证的用户的访问。为此,请打开Controllers文件夹中的HomeController.cs文件,并将Authorize属性添加到HomeController类定义。
C#namespace GeekQuiz.Controllers { [Authorize] public class HomeController : Controller { public ActionResult Index() { return View(); } ... } }
注意
该授权过滤器检查,看是否该用户进行身份验证。如果用户未通过身份验证,则返回HTTP状态码401(未经授权),而不调用该操作。您可以在全局,控制器级别或单个操作级别应用过滤器。
-
您现在将自定义网页的布局和品牌。为此,请在Views |中打开_Layout.cshtml文件 通过使用Geek Quiz替换My ASP.NET应用程序来共享文件夹并更新<title>元素的内容。
CSHTML<head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>@ViewBag.Title - Geek Quiz</title> @Styles.Render("~/Content/css") @Scripts.Render("~/bundles/modernizr") </head>
-
在同一个文件中,通过删除关于和联系人链接并将主页链接重命名为播放来更新导航栏。另外,重命名应用程序名称链接到Geek Quiz。导航栏的HTML应如下所示。
CSHTML<div class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> @Html.ActionLink("Geek Quiz", "Index", "Home", null, new { @class = "navbar-brand" }) </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li>@Html.ActionLink("Play", "Index", "Home")</li> </ul> @Html.Partial("_LoginPartial") </div> </div> </div>
-
通过使用Geek Quiz替换“ 我的ASP.NET应用程序”来更新布局页面的页脚。为此,请使用以下突出显示的代码替换<footer>元素的内容。
HTML<div class="container body-content"> @RenderBody() <hr /> <footer> <p>© @DateTime.Now.Year - Geek Quiz</p> </footer> </div>
任务2 - 创建TriviaController Web API
在上一个任务中,您创建了Geek Quiz Web应用程序的初始结构。您现在将构建一个简单的Web API服务,它与测验数据模型进行交互,并公开以下操作:
- GET / api / trivia:从测验列表中检索下一个问题,由认证用户回答。
- POST / api / trivia:存储由验证的用户指定的测验答案。
您将使用Visual Studio提供的ASP.NET脚手架工具为Web API控制器类创建基准。
- 打开App_Start文件夹中的WebApiConfig.cs文件。该文件定义了Web API服务的配置,就像路由映射到Web API控制器操作一样。
-
在文件开头添加以下using语句。
C#using Newtonsoft.Json.Serialization;
-
将以下突出显示的代码添加到Register方法中,以全局配置Web API操作方法检索的JSON数据的格式化程序。
C#public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API configuration and services // Use camel case for JSON data. config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); // Web API routes config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } }
注意
该CamelCasePropertyNamesContractResolver自动属性名称转换为骆驼的情况下,这是在JavaScript属性名称的一般惯例。
-
在解决方案资源管理器中,右键单击GeekQuiz项目的Controllers文件夹,然后选择Add | 新搭建的项目......。
创建一个新的脚手架物品
-
在“ 添加脚手架”对话框中,确保在左侧窗格中选择了“ 公共”节点。然后,在中央窗格中选择Web API 2控制器 - 空模板,然后单击添加。
选择Web API 2控制器空模板
注意
ASP.NET脚手架是ASP.NET Web应用程序的代码生成框架。Visual Studio 2013包括用于MVC和Web API项目的预安装代码生成器。当您希望快速添加与数据模型交互的代码,以减少开发标准数据操作所需的时间,您应该在项目中使用脚手架。
脚手架过程还确保所有所需的依赖关系都安装在项目中。例如,如果您从一个空的ASP.NET项目开始,然后使用脚手架来添加一个Web API控制器,则所需的Web API NuGet软件包和引用将自动添加到您的项目中。
-
在“ 添加控制器”对话框中,在“ 控制器名称”文本框中键入TriviaController,然后单击“ 添加”。
添加Trivia控制器
-
然后将TriviaController.cs文件添加到GeekQuiz项目的Controllers文件夹中,其中包含一个空的TriviaController类。在文件的开头添加以下使用语句。
(代码片段 - AspNetWebApiSpa - Ex1 - TriviaControllerUsings)
C#using System.Data.Entity; using System.Threading; using System.Threading.Tasks; using System.Web.Http.Description; using GeekQuiz.Models;
-
在TriviaController类的开头添加以下代码,以便在控制器中定义,初始化和处理TriviaContext实例。
(代码片段 - AspNetWebApiSpa - Ex1 - TriviaControllerContext)
C#public class TriviaController : ApiController { private TriviaContext db = new TriviaContext(); protected override void Dispose(bool disposing) { if (disposing) { this.db.Dispose(); } base.Dispose(disposing); } }
注意
的处置的方法TriviaController调用处置所述的方法TriviaContext例如,这确保了在由所述上下文对象中使用的所有资源被释放TriviaContext实例设置或垃圾收集。这包括关闭Entity Framework打开的所有数据库连接。
-
在TriviaController类的末尾添加以下帮助方法。此方法从数据库中检索以下问题以由指定的用户回答。
(代码片段 - AspNetWebApiSpa - Ex1 - TriviaControllerNextQuestion)
C#private async Task<TriviaQuestion> NextQuestionAsync(string userId) { var lastQuestionId = await this.db.TriviaAnswers .Where(a => a.UserId == userId) .GroupBy(a => a.QuestionId) .Select(g => new { QuestionId = g.Key, Count = g.Count() }) .OrderByDescending(q => new { q.Count, QuestionId = q.QuestionId }) .Select(q => q.QuestionId) .FirstOrDefaultAsync(); var questionsCount = await this.db.TriviaQuestions.CountAsync(); var nextQuestionId = (lastQuestionId % questionsCount) + 1; return await this.db.TriviaQuestions.FindAsync(CancellationToken.None, nextQuestionId); }
-
将以下Get操作方法添加到TriviaController类中。此操作方法调用上一步中定义的NextQuestionAsync帮助器方法来检索已验证用户的下一个问题。
(代码片段 - AspNetWebApiSpa - Ex1 - TriviaControllerGetAction)
C#// GET api/Trivia [ResponseType(typeof(TriviaQuestion))] public async Task<IHttpActionResult> Get() { var userId = User.Identity.Name; TriviaQuestion nextQuestion = await this.NextQuestionAsync(userId); if (nextQuestion == null) { return this.NotFound(); } return this.Ok(nextQuestion); }
-
在TriviaController类的末尾添加以下帮助方法。该方法将指定的答案存储在数据库中,并返回一个布尔值,指示答案是否正确。
(代码片段 - AspNetWebApiSpa - Ex1 - TriviaControllerStoreAsync)
C#private async Task<bool> StoreAsync(TriviaAnswer answer) { this.db.TriviaAnswers.Add(answer); await this.db.SaveChangesAsync(); var selectedOption = await this.db.TriviaOptions.FirstOrDefaultAsync(o => o.Id == answer.OptionId && o.QuestionId == answer.QuestionId); return selectedOption.IsCorrect; }
-
将以下Post操作方法添加到TriviaController类中。此操作方法将答案与经过身份验证的用户相关联,并调用StoreAsync帮助程序方法。然后,它发送一个响应与由辅助方法返回的布尔值。
(代码段 - AspNetWebApiSpa - Ex1 - TriviaControllerPostAction)
C#// POST api/Trivia [ResponseType(typeof(TriviaAnswer))] public async Task<IHttpActionResult> Post(TriviaAnswer answer) { if (!ModelState.IsValid) { return this.BadRequest(this.ModelState); } answer.UserId = User.Identity.Name; var isCorrect = await this.StoreAsync(answer); return this.Ok<bool>(isCorrect); }
-
修改Web API控制器以通过将授权属性添加到TriviaController类定义来限制对经过身份验证的用户的访问。
C#[Authorize] public class TriviaController : ApiController { ... }
任务3 - 运行解决方案
在此任务中,您将验证您在前一任务中构建的Web API服务是否按预期工作。您将使用Internet Explorer F12开发人员工具捕获网络流量并检查Web API服务的完整响应。
注意
确保在Visual Studio工具栏上的“ 开始”按钮中选择了Internet Explorer。
-
按F5运行解决方案。将在登录页面应该出现在浏览器中。
注意
当应用程序启动时,默认的MVC路由被触发,默认情况下映射到HomeController类的Index操作。由于HomeController仅限于经过身份验证的用户(请记住,您使用练习1中的Authorize属性来修饰该类),并且还没有用户认证,应用程序将原始请求重定向到登录页面。
运行解决方案
-
单击注册以创建新用户。
注册新用户
-
在注册页面中,输入用户名和密码,然后单击注册。
注册页面
-
应用程序注册新帐户,用户被认证并重新定向到主页。
用户进行身份验证
-
在浏览器中,按F12打开“ 开发人员工具”面板。按CTRL + 4或单击网络图标,然后单击绿色箭头按钮开始捕获网络流量。
启动Web API网络捕获
-
在浏览器地址栏中的URL 附加api / trivia。您现在将从TriviaController中的Get操作方法检查响应的详细信息。
通过Web API检索下一个问题数据
注意
一旦下载完成,系统将提示您对下载的文件进行操作。让对话框打开,以便能够通过开发人员工具窗口来观看响应内容。
-
现在你将检查身体的反应。为此,请单击详细信息选项卡,然后单击响应正文。您可以检查下载的数据是否与具有对应于TriviaQuestion类的属性选项(它是TriviaOption对象的列表),id和标题的对象。
查看Web API响应体
- 返回到Visual Studio,然后按SHIFT + F5停止调试。
练习2:创建SPA界面
在本练习中,您将首先构建Geek Quiz的Web前端部分,重点介绍使用AngularJS的单页应用程序交互。然后,您将通过CSS3增强用户体验,以执行丰富的动画,并在从一个问题转换到下一个问题时提供上下文切换的视觉效果。
任务1 - 使用AngularJS创建SPA接口
在这个任务中,您将使用AngularJS来实现Geek Quiz应用程序的客户端。AngularJS是一个开放源码的JavaScript框架,通过模型 - 视图 - 控制器(MVC)功能来增强基于浏览器的应用程序,有助于开发和测试。
您将首先从Visual Studio的软件包管理器控制台安装AngularJS。然后,您将创建控制器以提供Geek Quiz应用程序的行为和使用AngularJS模板引擎呈现测验问题和答案的视图。
注意
[http://angularjs.org/](http://angularjs.org/))。
- 打开Visual Studio Express 2013 for Web,并打开位于Source / Ex2-CreatingASPAInterface / Begin文件夹中的GeekQuiz.sln解决方案。或者,您可以继续在上一个练习中获得的解决方案。
-
从工具 | 打开包管理器控制台 图书馆包裹经理。键入以下命令来安装AngularJS.Core NuGet包。
电源外壳Install-Package AngularJS.Core
- 在解决方案资源管理器中,右键单击GeekQuiz项目的脚本文件夹,然后选择添加 新建文件夹。命名文件夹应用程序,然后按Enter键。
-
右键单击刚刚创建的应用程序文件夹,然后选择添加| JavaScript文件。
创建一个新的JavaScript文件
-
在“ 指定项目名称”对话框中,在“ 项目名称”文本框中键入quiz-controller,然后单击“ 确定”。
命名新的JavaScript文件
-
在quiz-controller.js文件中,添加以下代码来声明和初始化AngularJS QuizCtrl控制器。
(代码片段 - AspNetWebApiSpa - Ex2 - AngularQuizController)
JavaScript的angular.module('QuizApp', []) .controller('QuizCtrl', function ($scope, $http) { $scope.answered = false; $scope.title = "loading question..."; $scope.options = []; $scope.correctAnswer = false; $scope.working = false; $scope.answer = function () { return $scope.correctAnswer ? 'correct' : 'incorrect'; }; });
注意
QuizCtrl控制器的构造函数需要一个名为$ scope的可注射参数。应在构造函数中通过将属性附加到$ scope对象来设置范围的初始状态。属性包含视图模型,并且在注册控制器时可以访问模板。
所述QuizCtrl控制器被命名模块中定义QuizApp。模块是工作单元,可以将您的应用程序分解成单独的组件。使用模块的主要优点是代码更容易理解,便于单元测试,可重用性和可维护性。
-
您现在将向作用域添加行为,以便对从视图触发的事件作出反应。在QuizCtrl控件的末尾添加以下代码,以定义$ scope对象中的nextQuestion函数。
(代码片段 - AspNetWebApiSpa - Ex2 - AngularQuizControllerNextQuestion)
JavaScript的.controller('QuizCtrl', function ($scope, $http) { ... $scope.nextQuestion = function () { $scope.working = true; $scope.answered = false; $scope.title = "loading question..."; $scope.options = []; $http.get("/api/trivia").success(function (data, status, headers, config) { $scope.options = data.options; $scope.title = data.title; $scope.answered = false; $scope.working = false; }).error(function (data, status, headers, config) { $scope.title = "Oops... something went wrong"; $scope.working = false; }); }; };
注意
此函数从上一个练习中创建的Trivia Web API中检索下一个问题,并将问题数据附加到$ scope对象。
-
在QuizCtrl控件的末尾插入以下代码,以定义$ scope对象中的sendAnswer函数。
(代码片段 - AspNetWebApiSpa - Ex2 - AngularQuizControllerSendAnswer)
JavaScript的.controller('QuizCtrl', function ($scope, $http) { ... $scope.sendAnswer = function (option) { $scope.working = true; $scope.answered = true; $http.post('/api/trivia', { 'questionId': option.questionId, 'optionId': option.id }).success(function (data, status, headers, config) { $scope.correctAnswer = (data === true); $scope.working = false; }).error(function (data, status, headers, config) { $scope.title = "Oops... something went wrong"; $scope.working = false; }); }; };
注意
此功能将用户选择的答案发送到Trivia Web API,并存储结果,如果答案是否正确,则在$ scope对象中。
上述的NextQuestion和sendAnswer函数使用AngularJS $ http对象通过浏览器中的XMLHttpRequest JavaScript对象抽象与Web API的通信。AngularJS支持另一种服务,它通过RESTful API为资源执行CRUD操作提供了更高的抽象级别。AngularJS $资源对象具有提供高级行为的操作方法,而不需要与$ http对象进行交互。考虑在需要CRUD模型的场景中使用$资源对象(前提信息,请参阅$资源文档)。
-
下一步是创建定义测验视图的AngularJS模板。为此,请在Views |中打开Index.cshtml文件 主文件夹,并用以下代码替换内容。
(Code Snippet - AspNetWebApiSpa - Ex2 - GeekQuizView)
CSHTML@{ ViewBag.Title = "Play"; } <div id="bodyContainer" ng-app="QuizApp"> <section id="content"> <div class="container" > <div class="row"> <div class="flip-container text-center col-md-12" ng-controller="QuizCtrl" ng-init="nextQuestion()"> <div class="back" ng-class="{flip: answered, correct: correctAnswer, incorrect:!correctAnswer}"> <p class="lead">{{answer()}}</p> <p> <button class="btn btn-info btn-lg next option" ng-click="nextQuestion()" ng-disabled="working">Next Question</button> </p> </div> <div class="front" ng-class="{flip: answered}"> <p class="lead">{{title}}</p> <div class="row text-center"> <button class="btn btn-info btn-lg option" ng-repeat="option in options" ng-click="sendAnswer(option)" ng-disabled="working">{{option.title}}</button> </div> </div> </div> </div> </div> </section> </div> @section scripts { @Scripts.Render("~/Scripts/angular.js") @Scripts.Render("~/Scripts/app/quiz-controller.js") }
注意
AngularJS模板是一个声明性规范,它使用模型信息和控制器将静态标记转换为用户在浏览器中看到的动态视图。以下是可以在模板中使用的AngularJS元素和元素属性的示例:
- 的NG-应用指令告诉AngularJS代表应用程序的根元素的DOM元素。
- 在NG-控制器指令附加一个控制器在其中指令申报点的DOM。
- 大括号符号{{}}表示绑定到控制器中定义的范围属性。
- 的NG-点击指令用于调用响应于用户点击在范围中定义的功能。
-
打开Content文件夹中的Site.css文件,并在文件末尾添加以下突出显示的样式,以提供测验视图的外观。
(代码片段 - AspNetWebApiSpa - Ex2 - GeekQuizStyles)
CSS.validation-summary-valid { display: none; } /* Geek Quiz styles */ .flip-container .back, .flip-container .front { border: 5px solid #00bcf2; padding-bottom: 30px; padding-top: 30px; } #content { position:relative; background:#fff; padding:50px 0 0 0; } .option { width:140px; margin: 5px; } div.correct p { color: green; } div.incorrect p { color: red; } .btn { border-radius: 0; } .flip-container div.front, .flip-container div.back.flip { display: block; } .flip-container div.front.flip, .flip-container div
请发表评论