动手实验:使用 ASP.NET Web API 和 Angular.js 生成单页应用程序 (SPA)

Web Camp 团队

下载 Web Camp 培训工具包

本动手实验室演示如何使用 ASP.NET Web API 和 Angular.js 为 ASP.NET 4.x 生成单页应用程序 (SPA) 。

在此实践实验室中,你将利用这些技术来实现极客测验,这是一个基于 SPA 概念的琐事网站。 首先使用 ASP.NET Web API 实现服务层,以公开检索测验问题和存储答案所需的终结点。 然后,你将使用 AngularJS 和 CSS3 转换效果生成丰富且响应迅速的 UI。

在传统的 Web 应用程序中,客户端 (浏览器) 通过请求页面来启动与服务器的通信。 然后,服务器处理请求,并将页面的 HTML 发送到客户端。 在后续与页面交互(例如,用户导航到链接或提交包含数据的表单)中,将向服务器发送一个新请求,然后再次启动流:服务器处理请求并将新页面发送到浏览器,以响应客户端请求的新操作。

在 Single-Page 应用程序 (SPA) 在初始请求后在浏览器中加载整个页面,但后续交互通过 Ajax 请求进行。 这意味着浏览器必须仅更新已更改的页面部分;无需重新加载整个页面。 SPA 方法可减少应用程序响应用户操作所需的时间,从而获得更流畅的体验。

SPA 的体系结构涉及传统 Web 应用程序中不存在的某些挑战。 但是,ASP.NET Web API、JavaScript 框架(如 AngularJS)等新兴技术以及 CSS3 提供的新样式设置功能使设计和生成 SPA 变得非常简单。

所有示例代码和代码片段都包含在 Web Camp 培训工具包中,可在 上 https://aka.ms/webcamps-training-kit获取。

概述

目标

在本动手实验中,您将了解如何:

  • 创建用于发送和接收 JSON 数据的 ASP.NET Web API服务
  • 使用 AngularJS 创建响应式 UI
  • 使用 CSS3 转换增强 UI 体验

先决条件

完成本动手实验需要以下各项:

设置

若要在此动手实验室中运行练习,需要先设置环境。

  1. 打开 Windows 资源管理器并浏览到实验室的 “源 ”文件夹。
  2. 右键单击 “Setup.cmd ”,然后选择“ 以管理员身份运行” ,启动将配置环境的安装过程,并安装此实验室的 Visual Studio 代码片段。
  3. 如果显示“用户帐户控制”对话框,请确认操作以继续。

注意

确保在运行安装过程之前,您已检查本实验的所有依赖项。

使用代码段

在整个实验文档中,将指示您插入代码块。 为方便起见,此代码的大部分作为Visual Studio Code代码段提供,你可以从Visual Studio 2013内部访问这些代码片段,以避免手动添加代码段。

注意

每个练习都附带一个起始解决方案,该解决方案位于练习的 Begin 文件夹中,使你能够独立于其他练习跟踪每个练习。 请注意,在练习期间添加的代码片段在这些起始解决方案中缺失,在完成练习之前可能无法正常工作。 在练习的源代码中,还可以找到一个 End 文件夹,其中包含 Visual Studio 解决方案,其中包含完成相应练习中的步骤后产生的代码。 如果您在演练本动手实验时需要其他帮助,则可以使用这些解决方案作为指南。


练习

本动手实验包括以下练习:

  1. 创建 Web API
  2. 创建 SPA 接口

完成本实验室的估计时间: 60 分钟

注意

首次启动 Visual Studio 时,必须选择一个预定义的设置集合。 每个预定义集合都设计为匹配特定的开发样式,并确定窗口布局、编辑器行为、IntelliSense 代码片段和对话框选项。 本实验室中的过程描述了在使用 “常规开发设置” 集合时,在 Visual Studio 中完成给定任务所需的操作。 如果为开发环境选择不同的设置集合,则应考虑的步骤可能存在差异。

练习 1:创建 Web API

SPA 的关键部分之一是服务层。 它负责处理 UI 发送的 Ajax 调用,并返回响应该调用的数据。 检索到的数据应以计算机可读格式呈现,以便客户端分析和使用。

Web API 框架是 ASP.NET Stack 的一部分,旨在简化 HTTP 服务的实现,通常通过 RESTful API 发送和接收 JSON 或 XML 格式的数据。 在本练习中,你将创建网站来托管 Geek Quiz 应用程序,然后实现后端服务,以使用 ASP.NET Web API公开和持久保存测验数据。

任务 1 - 为极客测验创建初始项目

在此任务中,你将开始创建一个新的 ASP.NET MVC 项目,该项目支持基于 Visual Studio 附带的 One ASP.NET 项目类型 ASP.NET Web API。 一个 ASP.NET 统一所有 ASP.NET 技术,并提供根据需要混合和匹配它们的选项。 然后,将添加 Entity Framework 的模型类和数据库初始值设定项以插入测验问题。

  1. 打开 Visual Studio Express 2013 for Web,然后选择“文件” |新建项目...以启动新解决方案。

    创建新项目

    创建新项目

  2. 在“新建项目”对话框中,在 Visual C# 下选择 ASP.NET Web 应用程序|“Web”选项卡。请确保已选择 .NET Framework 4.5,将其命名为 GeekQuiz,选择“位置”并单击“确定”。

    创建新的 ASP.NET Web 应用程序项目

    创建新的 ASP.NET Web 应用程序项目

  3. 在“ 新建 ASP.NET 项目 ”对话框中,选择 MVC 模板,然后选择“ Web API ”选项。 此外,请确保将 “身份验证 ”选项设置为 “个人用户帐户”。 单击 “确定” 继续。

    使用 MVC 模板创建新项目,包括 Web API 组件

    使用 MVC 模板创建新项目,包括 Web API 组件

  4. “解决方案资源管理器”中,右键单击 GeekQuiz 项目的“模型”文件夹,然后选择“添加 |现有项...

    添加现有项

    添加现有项

  5. 在“ 添加现有项 ”对话框中,导航到 “源/资产/模型 ”文件夹,然后选择所有文件。 单击“添加”。

    添加模型资产

    添加模型资产

    注意

    通过添加这些文件,你将为 Geek Quiz 应用程序添加数据模型、实体框架的数据库上下文和数据库初始值设定项。

    Entity Framework (EF) 是一种对象关系映射器 (ORM) ,使你能够通过使用概念应用程序模型编程来创建数据访问应用程序,而不是直接使用关系存储架构进行编程。 可 在此处了解有关实体框架的详细信息。

    下面是刚添加的类的说明:

    • TriviaOption: 表示与测验问题关联的单个选项
    • TriviaQuestion: 表示测验问题并通过 Options 属性公开关联的选项
    • TriviaAnswer: 表示用户为响应测验问题而选择的选项
    • TriviaContext: 表示 Geek Quiz 应用程序的实体框架数据库上下文。 此类派生自 DContext 并公开表示上述实体集合的 DbSet 属性。
    • TriviaDatabaseInitializer:CreateDatabaseIfNotExists 继承的 TriviaContext 类的 Entity Framework 初始值设定项的实现。 此类的默认行为是仅当数据库不存在时才创建数据库,并插入 Seed 方法中指定的实体。
  6. 打开 Global.asax.cs 文件并添加以下 using 语句。

    using GeekQuiz.Models;
    
  7. Application_Start 方法的开头添加以下代码,以将 TriviaDatabaseInitializer 设置为数据库初始值设定项。

    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);
        }
    }
    
  8. 修改 控制器以限制对经过身份验证的用户的访问。 为此,请打开 Controllers 文件夹中的 HomeController.cs 文件,并将 Authorize 属性添加到 HomeController 类定义。

    namespace GeekQuiz.Controllers
    {
        [Authorize]
        public class HomeController : Controller
        {
            public ActionResult Index()
            {
                return View();
            }
    
            ...
        }
    }
    

    注意

    “授权”筛选器将检查用户是否已通过身份验证。 如果用户未通过身份验证,则返回 HTTP 状态代码 401 (未调用操作的“未授权) ”。 可以在全局、控制器级别或单个操作级别应用筛选器。

  9. 现在,你将自定义网页的布局和品牌。 为此,请在视图 | 中打开 _Layout.cshtml 文件共享文件夹,并通过将“我的 ASP.NET 应用程序”替换为“Geek Quiz”来更新 title> 元素的内容<

    <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>
    
  10. 在同一文件中,通过删除 “关于” 和“ 联系人” 链接并将 “主页” 链接重命名为 “播放”来更新导航栏。 此外,将 应用程序名称 链接重命名为 Geek Quiz。 导航栏的 HTML 应如以下代码所示。

    <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>
    
  11. “我的 ASP.NET 应用程序 ”替换为 “Geek 测验”来更新布局页的页脚。 为此,请将页脚>元素的内容<替换为以下突出显示的代码。

    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; @DateTime.Now.Year - Geek Quiz</p>
        </footer>
    </div>
    

任务 2 - 创建 TriviaController Web API

在上一个任务中,你创建了 Geek 测验 Web 应用程序的初始结构。 现在,你将生成一个简单的 Web API 服务,该服务与测验数据模型交互并公开以下操作:

  • GET /api/trivia:从测验列表中检索要由经过身份验证的用户回答的下一个问题。
  • POST /api/trivia:存储经过身份验证的用户指定的测验答案。

你将使用 Visual Studio 提供的 ASP.NET 基架工具为 Web API 控制器类创建基线。

  1. 打开 App_Start 文件夹中的 WebApiConfig.cs 文件。 此文件定义 Web API 服务的配置,例如路由如何映射到 Web API 控制器操作。

  2. 在文件的开头添加以下 using 语句。

    using Newtonsoft.Json.Serialization;
    
  3. 将以下突出显示的代码添加到 Register 方法,以全局配置 Web API 操作方法检索的 JSON 数据的格式化程序。

    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 会自动将属性名称转换为 camel 大小写,这是 JavaScript 中属性名称的一般约定。

  4. “解决方案资源管理器”中,右键单击 GeekQuiz 项目的 Controllers 文件夹,然后选择“添加 ” |新的基架项目...

    创建新的基架项

    创建新的基架项

  5. “添加基架 ”对话框中,确保在左窗格中选择了 “公共 ”节点。 然后,在中心窗格中选择 “Web API 2 控制器 - 空 模板”,然后单击“ 添加”。

    选择 Web API 2 控制器空模板

    选择 Web API 2 控制器空模板

    注意

    ASP.NET 基架 是用于 ASP.NET Web 应用程序的代码生成框架。 Visual Studio 2013包括为 MVC 和 Web API 项目预安装的代码生成器。 如果要快速添加与数据模型交互的代码,以减少开发标准数据操作所需的时间,则应在项目中使用基架。

    基架过程还确保在项目中安装所有必需的依赖项。 例如,如果从空 ASP.NET 项目开始,然后使用基架添加 Web API 控制器,则所需的 Web API NuGet 包和引用会自动添加到项目中。

  6. 在“添加控制器”对话框中的“控制器名称”文本框中键入“TriviaController”,然后单击“添加”。

    添加 Trivia 控制器

    添加 Trivia 控制器

  7. 然后,将 TriviaController.cs 文件添加到 GeekQuiz 项目的 Controllers 文件夹,其中包含一个空的 TriviaController 类。 在文件开头添加以下 using 语句。

    (代码片段 - AspNetWebApiSpa - Ex1 - TriviaControllerUsings)

    using System.Data.Entity;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Web.Http.Description;
    using GeekQuiz.Models;
    
  8. TriviaController 类的开头添加以下代码,以在控制器中定义、初始化和释放 TriviaContext 实例。

    (代码片段 - AspNetWebApiSpa - Ex1 - TriviaControllerContext)

    public class TriviaController : ApiController
    {
        private TriviaContext db = new TriviaContext();
    
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                this.db.Dispose();
            }
    
            base.Dispose(disposing);
        }
    }
    

    注意

    TriviaControllerDispose 方法调用 TriviaContext 实例的 Dispose 方法,这可确保在处置 TriviaContext 实例或进行垃圾回收时释放上下文对象使用的所有资源。 这包括关闭实体框架打开的所有数据库连接。

  9. TriviaController 类的末尾添加以下帮助程序方法。 此方法从数据库中检索由指定用户回答的以下测验问题。

    (代码片段 - AspNetWebApiSpa - Ex1 - TriviaControllerNextQuestion)

    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);
    }
    
  10. 将以下 Get 操作方法添加到 TriviaController 类。 此操作方法调用上一步中定义的 NextQuestionAsync 帮助程序方法,以检索经过身份验证的用户的下一个问题。

    (代码片段 - AspNetWebApiSpa - Ex1 - TriviaControllerGetAction)

    // 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);
    }
    
  11. TriviaController 类的末尾添加以下帮助程序方法。 此方法将指定的答案存储在数据库中,并返回一个布尔值,该值指示答案是否正确。

    (代码片段 - AspNetWebApiSpa - Ex1 - TriviaControllerStoreAsync)

    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;
    }
    
  12. 将以下 Post 操作方法添加到 TriviaController 类。 此操作方法将答案关联到经过身份验证的用户,并调用 StoreAsync 帮助程序方法。 然后,它发送一个响应,其中包含由帮助程序方法返回的布尔值。

    (代码片段 - AspNetWebApiSpa - Ex1 - TriviaControllerPostAction)

    // 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);
    }
    
  13. 修改 Web API 控制器,通过将 Authorize 属性添加到 TriviaController 类定义来限制对经过身份验证的用户的访问。

    [Authorize]
    public class TriviaController : ApiController
    {
        ...
    }
    

任务 3 - 运行解决方案

在此任务中,你将验证在上一个任务中生成的 Web API 服务是否按预期工作。 你将使用 Internet Explorer F12 开发人员工具 捕获网络流量并检查来自 Web API 服务的完整响应。

注意

确保在 Visual Studio 工具栏上的“开始”按钮中选择 Internet Explorer

Internet Explorer 选项

  1. “F5”运行该解决方案。 浏览器中应显示 “登录 ”页。

    注意

    应用程序启动时,将触发默认 MVC 路由,默认情况下,该路由映射到 HomeController 类的 Index 操作。 由于 HomeController 仅限于经过身份验证的用户 (请记住,你在练习 1) 中使用 Authorize 属性修饰了该类,并且尚未对用户进行身份验证,因此应用程序会将原始请求重定向到登录页。

    运行解决方案

    运行解决方案

  2. 单击“ 注册 ”以创建新用户。

    注册新用户

    注册新用户

  3. “注册 ”页中,输入 “用户名 ”和“ 密码”,然后单击“ 注册”。

    注册页

    注册页

  4. 应用程序注册新帐户,用户进行身份验证并重定向回主页。

    用户已验证

    用户已经过身份验证

  5. 在浏览器中,按 F12 打开 “开发人员工具” 面板。 按 Ctrl + 4 或单击 “网络 ”图标,然后单击绿色箭头按钮开始捕获网络流量。

    启动 Web API 网络捕获

    启动 Web API 网络捕获

  6. api/trivia 追加到浏览器地址栏中的 URL。 现在,你将检查 TriviaControllerGet 操作方法的响应的详细信息。

    通过 Web API 检索下一个问题数据 通过

    通过 Web API 检索下一个问题数据

    注意

    下载完成后,系统会提示你对下载的文件执行操作。 使对话框保持打开状态,以便能够通过“开发人员工具”窗口watch响应内容。

  7. 现在,你将检查响应的正文。 为此,请单击“ 详细信息 ”选项卡,然后单击“ 响应正文”。 可以检查下载的数据是一个具有属性选项 (是 TriviaOption 对象列表,) 、ID标题对应于 TriviaQuestion 类。

    查看 Web API 响应正文

    查看 Web API 响应正文

  8. 返回 Visual Studio 并按 SHIFT + F5 停止调试。

练习 2:创建 SPA 接口

在本练习中,你将首先生成 Geek 测验的 Web 前端部分,重点介绍使用 AngularJS Single-Page 应用程序交互。 然后,你将使用 CSS3 增强用户体验,以执行丰富的动画,并在从一个问题转换到下一个问题时提供上下文切换的视觉效果。

任务 1 - 使用 AngularJS 创建 SPA 接口

在此任务中,你将使用 AngularJS 实现 Geek Quiz 应用程序的客户端。 AngularJS 是一个开源 JavaScript 框架,它通过 模型-视图-控制器 (MVC) 功能来增强基于浏览器的应用程序,从而促进开发和测试。

首先从 Visual Studio 的包管理器控制台安装 AngularJS。 然后,你将创建控制器,以提供 Geek 测验应用的行为以及使用 AngularJS 模板引擎呈现测验问题和答案的视图。

注意

有关 AngularJS 的详细信息,请参阅 [http://angularjs.org/] (http://angularjs.org/) 。

  1. 打开 Visual Studio Express 2013 for Web 并打开 Source/Ex2-CreatingASPAInterface/Begin 文件夹中的 GeekQuiz.sln 解决方案。 或者,可以继续使用在上一练习中获取的解决方案。

  2. 工具> NuGet 包管理器打开包管理器控制台。 键入以下命令以安装 AngularJS.Core NuGet 包。

    Install-Package AngularJS.Core
    
  3. “解决方案资源管理器”中,右键单击 GeekQuiz 项目的“脚本”文件夹,然后选择“添加” |新建文件夹。 将文件夹命名 为应用 ,然后按 Enter

  4. 右键单击刚刚创建 的应用 文件夹,然后选择“ 添加 | ”JavaScript 文件

    创建新的 JavaScript 文件

    创建新的 JavaScript 文件

  5. “指定项目的名称”对话框中,在“项名称”文本框中键入 quiz-controller,然后单击“确定”。

    命名新的 JavaScript 文件

    命名新的 JavaScript 文件

  6. quiz-controller.js 文件中,添加以下代码以声明和初始化 AngularJS QuizCtrl 控制器。

    (代码片段 - AspNetWebApiSpa - Ex2 - AngularQuizController)

    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 的模块中定义。 模块是工作单元,可让你将应用程序分解为单独的组件。 使用模块main优点是代码更易于理解,便于单元测试、可重用性和可维护性。

  7. 现在,你将将行为添加到范围,以便对从视图触发的事件做出反应。 在 QuizCtrl 控制器的末尾添加以下代码,以在 $scope 对象中定义 nextQuestion 函数。

    (代码片段 - AspNetWebApiSpa - Ex2 - AngularQuizControllerNextQuestion)

    .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 对象。

  8. QuizCtrl 控制器的末尾插入以下代码,以在 $scope 对象中定义 sendAnswer 函数。

    (代码片段 - AspNetWebApiSpa - Ex2 - AngularQuizControllerSendAnswer)

    .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 对象中。

    上述 nextQuestionsendAnswer 函数使用 AngularJS $http 对象从浏览器中通过 XMLHttpRequest JavaScript 对象抽象化与 Web API 的通信。 AngularJS 支持另一项服务,该服务提供更高级别的抽象,以便通过 RESTful API 对资源执行 CRUD 操作。 AngularJS $resource 对象具有操作方法,这些方法提供高级行为,而无需与 $http 对象交互。 在需要 CRUD 模型 (相关信息的情况下,请考虑使用 $resource 对象,请参阅 $resource文档) 。

  9. 下一步是创建定义测验视图的 AngularJS 模板。 为此,请在视图 | 中打开 Index.cshtml 文件 文件夹,并将内容替换为以下代码。

    (代码片段 - AspNetWebApiSpa - Ex2 - GeekQuizView)

    @{
        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-app 指令告诉 AngularJS DOM 元素,该元素表示应用程序的根元素。
    • ng-controller 指令在声明指令时将控制器附加到 DOM。
    • 大括号表示法 {{ }} 表示对控制器中定义的作用域属性的绑定。
    • ng-click 指令用于调用作用域中定义的函数,以响应用户单击。
  10. 打开 Content 文件夹内的 Site.css 文件,并在文件末尾添加以下突出显示的样式,以提供测验视图的外观。

    (代码片段 - AspNetWebApiSpa - Ex2 - GeekQuizStyles)

    .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.back {
        display: none;
    }
    

任务 2 - 运行解决方案

在此任务中,你将使用使用 AngularJS 构建的新用户界面执行解决方案,以回答一些测验问题。

  1. “F5”运行该解决方案。

  2. 注册新的用户帐户。 为此,请按照练习 1,任务 3 中所述的注册步骤操作。

    注意

    如果使用上一练习中的解决方案,可以使用之前创建的用户帐户登录。

  3. 此时会显示 “主页 ”,其中显示了测验的第一个问题。 单击其中一个选项回答问题。 这将触发前面定义的 sendAnswer 函数,该函数将所选选项发送到 Trivia Web API。

    回答问题

    回答问题

  4. 单击其中一个按钮后,应会显示答案。 单击“ 下一个问题 ”以显示以下问题。 这将触发控制器中定义的 nextQuestion 函数。

    请求下一个问题

    请求下一个问题

  5. 应显示下一个问题。 继续根据需要多次回答问题。 完成所有问题后,应返回到第一个问题。

    另一个问题

    下一个问题

  6. 返回 Visual Studio 并按 SHIFT + F5 停止调试。

任务 3 - 使用 CSS3 创建翻转动画

在此任务中,你将使用 CSS3 属性执行丰富的动画,方法是在回答问题时和检索下一个问题时添加翻转效果。

  1. “解决方案资源管理器”中,右键单击 GeekQuiz 项目的 Content 文件夹,然后选择“添加|现有项...

    将现有项目添加到“内容”文件夹

    将现有项目添加到 Content 文件夹

  2. “添加现有项 ”对话框中,导航到 “源/资产” 文件夹,然后选择“ Flip.css”。 单击“添加”。

    从资产添加 Flip.css 文件 从资产

    从资产中添加 Flip.css 文件

  3. 打开刚添加的 Flip.css 文件并检查其内容。

  4. 找到 翻转转换 注释。 下面的批注样式使用 CSS 透视rotateY 转换来生成“卡翻转”效果。

    /* flip transformation */
    .flip-container div.front {
        -moz-transform: perspective(2000px) rotateY(0deg);
        -webkit-transform: perspective(2000px) rotateY(0deg);
        -o-transform: perspective(2000px) rotateY(0deg);
        transform: perspective(2000px) rotateY(0deg);
    }
    
        .flip-container div.front.flip {
            -moz-transform: perspective(2000px) rotateY(179.9deg);
            -webkit-transform: perspective(2000px) rotateY(179.9deg);
            -o-transform: perspective(2000px) rotateY(179.9deg);
            transform: perspective(2000px) rotateY(179.9deg);
        }
    
    .flip-container div.back {
        -moz-transform: perspective(2000px) rotateY(-180deg);
        -webkit-transform: perspective(2000px) rotateY(-180deg);
        -o-transform: perspective(2000px) rotateY(-180deg);
        transform: perspective(2000px) rotateY(-180deg);
    }
    
        .flip-container div.back.flip {
            -moz-transform: perspective(2000px) rotateY(0deg);
            -webkit-transform: perspective(2000px) rotateY(0deg);
            -ms-transform: perspective(2000px) rotateY(0);
            -o-transform: perspective(2000px) rotateY(0);
            transform: perspective(2000px) rotateY(0);
        }
    
  5. 翻转批注期间找到窗格的隐藏背面 。 注释下面的样式通过将背面可见性 CSS 属性设置为隐藏来隐藏人脸在远离查看器时隐藏人脸背面。

    /* hide back of pane during flip */
    .front, .back {
        -moz-backface-visibility: hidden;
        -webkit-backface-visibility: hidden;
        backface-visibility: hidden;
    }
    
  6. 打开 App_Start 文件夹中的 BundleConfig.cs 文件,并在“~/Content/css”样式捆绑包中添加对 Flip.css 文件的引用

    bundles.Add(new StyleBundle("~/Content/css").Include(
        "~/Content/bootstrap.css",
        "~/Content/site.css",
        "~/Content/Flip.css"));
    
  7. F5 运行解决方案并使用凭据登录。

  8. 单击其中一个选项回答问题。 请注意在视图之间切换时的翻转效果。

    使用翻转效果回答问题

    使用翻转效果回答问题

  9. 单击“ 下一个问题 ”检索以下问题。 翻转效果应再次出现。

    使用翻转效果检索以下问题

    使用翻转效果检索以下问题


总结

通过完成本动手实验,你已了解如何:

  • 使用 ASP.NET 基架创建 ASP.NET Web API控制器
  • 实现 Web API 获取操作以检索下一个测验问题
  • 实现 Web API Post 操作以存储测验答案
  • 从 Visual Studio 包管理器控制台安装 AngularJS
  • 实现 AngularJS 模板和控制器
  • 使用 CSS3 切换效果执行动画效果