从 Windows Phone 8 应用程序调用 Web API (C#)Calling Web API from a Windows Phone 8 Application (C#)

作者: Robert mcmurray 有关by Robert McMurray

在本教程中,你将了解如何创建一个完整的端到端方案,该方案包含一个 ASP.NET Web API 应用程序,该方案向 Windows Phone 8 应用程序提供书籍目录。In this tutorial, you will learn how to create a complete end-to-end scenario consisting of an ASP.NET Web API application that provides a catalog of books to a Windows Phone 8 application.

概述Overview

RESTful 服务(如 ASP.NET Web API)通过抽象服务器端和客户端应用程序的体系结构,为开发人员简化了基于 HTTP 的应用程序的创建过程。RESTful services like ASP.NET Web API simplify the creation of HTTP-based applications for developers by abstracting the architecture for server-side and client-side applications. Web API 开发人员只需为其应用程序发布必要的 HTTP 方法(例如: GET、POST、PUT、DELETE)和客户端应用程序开发人员只需使用其应用程序所需的 HTTP 方法。Instead of creating a proprietary socket-based protocol for communication, Web API developers simply need to publish the requisite HTTP methods for their application, (for example: GET, POST, PUT, DELETE), and client application developers only need to consume the HTTP methods that are necessary for their application.

本端到端教程介绍如何使用 Web API 创建以下项目:In this end-to-end tutorial, you will learn how to use Web API to create the following projects:

  • 本教程的第一部分中,你将创建一个 ASP.NET Web API 应用程序,该应用程序支持用于管理书籍目录的所有创建、读取、更新和删除(CRUD)操作。In the first part of this tutorial, you will create an ASP.NET Web API application that supports all of the Create, Read, Update, and Delete (CRUD) operations to manage a book catalog. 此应用程序将使用 MSDN 的示例 XML 文件(books.xml)This application will use the Sample XML File (books.xml) from MSDN.
  • 本教程的第二部分中,你将创建一个交互式 Windows Phone 8 应用程序,该应用程序从 Web API 应用程序检索数据。In the second part of this tutorial, you will create an interactive Windows Phone 8 application that retrieves the data from your Web API application.

系统必备Prerequisites

  • Visual Studio 2013 安装了 Windows Phone 8 SDKVisual Studio 2013 with the Windows Phone 8 SDK installed
  • Windows 8 或更高版本位于安装了 Hyper-v 的64位系统上Windows 8 or later on a 64-bit system with Hyper-V installed
  • 有关其他要求的列表,请参阅WINDOWS PHONE SDK 8.0下载页面上的 "系统要求" 部分。For a list of additional requirements, see the System Requirements section on the Windows Phone SDK 8.0 download page.

Note

如果要在本地系统上测试 Web API 与 Windows Phone 8 项目之间的连接,则需按照将 Windows Phone 8 模拟器连接到本地计算机上的 WEB Api 应用程序 一文中的说明设置测试环境。If you are going to test the connectivity between Web API and Windows Phone 8 projects on your local system, you will need to follow the instructions in the Connecting the Windows Phone 8 Emulator to Web API Applications on a Local Computer article to set up your testing environment.

步骤1:创建 Web API 书店项目Step 1: Creating the Web API Bookstore Project

本端到端教程的第一步是创建一个支持所有 CRUD 操作的 Web API 项目;请注意,你将在本教程的第2步中将 Windows Phone 应用程序项目添加到此解决方案。The first step of this end-to-end tutorial is to create a Web API project that supports all of the CRUD operations; note that you will add the Windows Phone application project to this solution in Step 2 of this tutorial.

  1. 打开Visual Studio 2013Open Visual Studio 2013.

  2. 依次单击 "文件"、"新建"、"项目"。Click File, then New, and then Project.

  3. 显示 "新建项目" 对话框时,依次展开 "已安装"、"模板"、" C#Visual" 和 " Web"。When the New Project dialog box is displayed, expand Installed, then Templates, then Visual C#, and then Web.

    单击图像进行扩展Click image to expand
  4. 突出显示 " ASP.NET Web 应用程序",输入书店作为项目名称,然后单击 "确定"Highlight ASP.NET Web Application, enter BookStore for the project name, and then click OK.

  5. 在 "新建 ASP.NET 项目" 对话框出现时,选择 " Web API " 模板,然后单击 "确定"When the New ASP.NET Project dialog box is displayed, select the Web API template, and then click OK.

    单击图像进行扩展Click image to expand
  6. 当 Web API 项目打开时,从项目中删除示例控制器:When the Web API project opens, remove the sample controller from the project:

    1. 展开 "解决方案资源管理器" 中的 "控制器" 文件夹。Expand the Controllers folder in the solution explorer.
    2. 右键单击 " ValuesController.cs " 文件,然后单击 "删除"。Right-click the ValuesController.cs file, and then click Delete.
    3. 系统提示确认删除时,单击 "确定"Click OK when prompted to confirm the deletion.
  7. 将 XML 数据文件添加到 Web API 项目;此文件包含书店目录的内容:Add an XML data file to the Web API project; this file contains the contents of the bookstore catalog:

    1. 在 "解决方案资源管理器" 中右键单击应用_Data文件夹,然后单击 "添加",然后单击 "新建项"。Right-click the App_Data folder in the solution explorer, then click Add, and then click New Item.

    2. 当显示 "添加新项" 对话框时,突出显示XML 文件模板。When the Add New Item dialog box is displayed, highlight the XML File template.

    3. 将文件命名为books.xml,然后单击 "添加"。Name the file Books.xml, and then click Add.

    4. 打开books.xml文件后,将文件中的代码替换为 MSDN 上的示例books.xml文件中的 xml:When the Books.xml file is opened, replace the code in the file with the XML from the sample books.xml file on MSDN:

      <?xml version="1.0" encoding="utf-8"?>
      <catalog>
        <book id="bk101">
          <author>Gambardella, Matthew</author>
          <title>XML Developer's Guide</title>
          <genre>Computer</genre>
          <price>44.95</price>
          <publish_date>2000-10-01</publish_date>
          <description>
            An in-depth look at creating applications
            with XML.
          </description>
        </book>
        <book id="bk102">
          <author>Ralls, Kim</author>
          <title>Midnight Rain</title>
          <genre>Fantasy</genre>
          <price>5.95</price>
          <publish_date>2000-12-16</publish_date>
          <description>
            A former architect battles corporate zombies,
            an evil sorceress, and her own childhood to become queen
            of the world.
          </description>
        </book>
        <book id="bk103">
          <author>Corets, Eva</author>
          <title>Maeve Ascendant</title>
          <genre>Fantasy</genre>
          <price>5.95</price>
          <publish_date>2000-11-17</publish_date>
          <description>
            After the collapse of a nanotechnology
            society in England, the young survivors lay the
            foundation for a new society.
          </description>
        </book>
        <book id="bk104">
          <author>Corets, Eva</author>
          <title>Oberon's Legacy</title>
          <genre>Fantasy</genre>
          <price>5.95</price>
          <publish_date>2001-03-10</publish_date>
          <description>
            In post-apocalypse England, the mysterious
            agent known only as Oberon helps to create a new life
            for the inhabitants of London. Sequel to Maeve
            Ascendant.
          </description>
        </book>
        <book id="bk105">
          <author>Corets, Eva</author>
          <title>The Sundered Grail</title>
          <genre>Fantasy</genre>
          <price>5.95</price>
          <publish_date>2001-09-10</publish_date>
          <description>
            The two daughters of Maeve, half-sisters,
            battle one another for control of England. Sequel to
            Oberon's Legacy.
          </description>
        </book>
        <book id="bk106">
          <author>Randall, Cynthia</author>
          <title>Lover Birds</title>
          <genre>Romance</genre>
          <price>4.95</price>
          <publish_date>2000-09-02</publish_date>
          <description>
            When Carla meets Paul at an ornithology
            conference, tempers fly as feathers get ruffled.
          </description>
        </book>
        <book id="bk107">
          <author>Thurman, Paula</author>
          <title>Splish Splash</title>
          <genre>Romance</genre>
          <price>4.95</price>
          <publish_date>2000-11-02</publish_date>
          <description>
            A deep sea diver finds true love twenty
            thousand leagues beneath the sea.
          </description>
        </book>
        <book id="bk108">
          <author>Knorr, Stefan</author>
          <title>Creepy Crawlies</title>
          <genre>Horror</genre>
          <price>4.95</price>
          <publish_date>2000-12-06</publish_date>
          <description>
            An anthology of horror stories about roaches,
            centipedes, scorpions  and other insects.
          </description>
        </book>
        <book id="bk109">
          <author>Kress, Peter</author>
          <title>Paradox Lost</title>
          <genre>Science Fiction</genre>
          <price>6.95</price>
          <publish_date>2000-11-02</publish_date>
          <description>
            After an inadvertant trip through a Heisenberg
            Uncertainty Device, James Salway discovers the problems
            of being quantum.
          </description>
        </book>
        <book id="bk110">
          <author>O'Brien, Tim</author>
          <title>Microsoft .NET: The Programming Bible</title>
          <genre>Computer</genre>
          <price>36.95</price>
          <publish_date>2000-12-09</publish_date>
          <description>
            Microsoft's .NET initiative is explored in
            detail in this deep programmer's reference.
          </description>
        </book>
        <book id="bk111">
          <author>O'Brien, Tim</author>
          <title>MSXML3: A Comprehensive Guide</title>
          <genre>Computer</genre>
          <price>36.95</price>
          <publish_date>2000-12-01</publish_date>
          <description>
            The Microsoft MSXML3 parser is covered in
            detail, with attention to XML DOM interfaces, XSLT processing,
            SAX and more.
          </description>
        </book>
        <book id="bk112">
          <author>Galos, Mike</author>
          <title>Visual Studio 7: A Comprehensive Guide</title>
          <genre>Computer</genre>
          <price>49.95</price>
          <publish_date>2001-04-16</publish_date>
          <description>
            Microsoft Visual Studio 7 is explored in depth,
            looking at how Visual Basic, Visual C++, C#, and ASP+ are
            integrated into a comprehensive development
            environment.
          </description>
        </book>
      </catalog>
      
    5. 保存并关闭该 XML 文件。Save and close the XML file.

  8. 将书店模型添加到 Web API 项目;此模型包含书店应用程序的创建、读取、更新和删除(CRUD)逻辑:Add the bookstore model to the Web API project; this model contains the Create, Read, Update, and Delete (CRUD) logic for the bookstore application:

    1. 在 "解决方案资源管理器" 中,右键单击 "模型" 文件夹,单击 "添加",然后单击 ""。Right-click the Models folder in the solution explorer, then click Add, and then click Class.

    2. 显示 "添加新项" 对话框时,将类文件命名为 " BookDetails.cs",然后单击 "添加"。When the Add New Item dialog box is displayed, name the class file BookDetails.cs, and then click Add.

    3. 打开BookDetails.cs文件时,将文件中的代码替换为以下代码:When the BookDetails.cs file is opened, replace the code in the file with the following:

      using System;
      using System.Collections.Generic;
      using System.ComponentModel.DataAnnotations;
      using System.Linq;
      using System.Xml;
      using System.Xml.Linq;
      using System.Xml.XPath;
      using System.Web;
      
      namespace BookStore.Models
      {
          /// <summary>
          /// Define a class that will hold the detailed information for a book.
          /// </summary>
          public class BookDetails
          {
              [Required]
              public String Id { get; set; }
              [Required]
              public String Title { get; set; }
              public String Author { get; set; }
              public String Genre { get; set; }
              public Decimal Price { get; set; }
              public DateTime PublishDate { get; set; }
              public String Description { get; set; }
          }
      
          /// <summary>
          /// Define an interface which contains the methods for the book repository.
          /// </summary>
          public interface IBookRepository
          {
              BookDetails CreateBook(BookDetails book);
              IEnumerable<BookDetails> ReadAllBooks();
              BookDetails ReadBook(String id);
              BookDetails UpdateBook(String id, BookDetails book);
              Boolean DeleteBook(String id);
          }
      
          /// <summary>
          /// Define a class based on the book repository interface which contains the method implementations.
          /// </summary>
          public class BookRepository : IBookRepository
          {
              private string xmlFilename = null;
              private XDocument xmlDocument = null;
      
              /// <summary>
              /// Define the class constructor.
              /// </summary>
              public BookRepository()
              {
                  try
                  {
                      // Determine the path to the books.xml file.
                      xmlFilename = HttpContext.Current.Server.MapPath("~/app_data/books.xml");
                      // Load the contents of the books.xml file into an XDocument object.
                      xmlDocument = XDocument.Load(xmlFilename);
                  }
                  catch (Exception ex)
                  {
                      // Rethrow the exception.
                      throw ex;
                  }
              }
      
              /// <summary>
              /// Method to add a new book to the catalog.
              /// Defines the implementation of the POST method.
              /// </summary>
              public BookDetails CreateBook(BookDetails book)
              {
                  try
                  {
                      // Retrieve the book with the highest ID from the catalog.
                      var highestBook = (
                          from bookNode in xmlDocument.Elements("catalog").Elements("book")
                          orderby bookNode.Attribute("id").Value descending
                          select bookNode).Take(1);
                      // Extract the ID from the book data.
                      string highestId = highestBook.Attributes("id").First().Value;
                      // Create an ID for the new book.
                      string newId = "bk" + (Convert.ToInt32(highestId.Substring(2)) + 1).ToString();
                      // Verify that this book ID does not currently exist.
                      if (this.ReadBook(newId) == null)
                      {
                          // Retrieve the parent element for the book catalog.
                          XElement bookCatalogRoot = xmlDocument.Elements("catalog").Single();
                          // Create a new book element.
                          XElement newBook = new XElement("book", new XAttribute("id", newId));
                          // Create elements for each of the book's data items.
                          XElement[] bookInfo = FormatBookData(book);
                          // Add the element to the book element.
                          newBook.ReplaceNodes(bookInfo);
                          // Append the new book to the XML document.
                          bookCatalogRoot.Add(newBook);
                          // Save the XML document.
                          xmlDocument.Save(xmlFilename);
                          // Return an object for the newly-added book.
                          return this.ReadBook(newId);
                      }
                  }
                  catch (Exception ex)
                  {
                      // Rethrow the exception.
                      throw ex;
                  }
                  // Return null to signify failure.
                  return null;
              }
      
              /// <summary>
              /// Method to retrieve all of the books in the catalog.
              /// Defines the implementation of the non-specific GET method.
              /// </summary>
              public IEnumerable<BookDetails> ReadAllBooks()
              {
                  try
                  {
                      // Return a list that contains the catalog of book ids/titles.
                      return (
                          // Query the catalog of books.
                          from book in xmlDocument.Elements("catalog").Elements("book")
                          // Sort the catalog based on book IDs.
                          orderby book.Attribute("id").Value ascending
                          // Create a new instance of the detailed book information class.
                          select new BookDetails
                          {
                              // Populate the class with data from each of the book's elements.
                              Id = book.Attribute("id").Value,
                              Author = book.Element("author").Value,
                              Title = book.Element("title").Value,
                              Genre = book.Element("genre").Value,
                              Price = Convert.ToDecimal(book.Element("price").Value),
                              PublishDate = Convert.ToDateTime(book.Element("publish_date").Value),
                              Description = book.Element("description").Value
                          }).ToList();
                  }
                  catch (Exception ex)
                  {
                      // Rethrow the exception.
                      throw ex;
                  }
              }
      
              /// <summary>
              /// Method to retrieve a specific book from the catalog.
              /// Defines the implementation of the ID-specific GET method.
              /// </summary>
              public BookDetails ReadBook(String id)
              {
                  try
                  {
                      // Retrieve a specific book from the catalog.
                      return (
                          // Query the catalog of books.
                          from book in xmlDocument.Elements("catalog").Elements("book")
                          // Specify the specific book ID to query.
                          where book.Attribute("id").Value.Equals(id)
                          // Create a new instance of the detailed book information class.
                          select new BookDetails
                          {
                              // Populate the class with data from each of the book's elements.
                              Id = book.Attribute("id").Value,
                              Author = book.Element("author").Value,
                              Title = book.Element("title").Value,
                              Genre = book.Element("genre").Value,
                              Price = Convert.ToDecimal(book.Element("price").Value),
                              PublishDate = Convert.ToDateTime(book.Element("publish_date").Value),
                              Description = book.Element("description").Value
                          }).Single();
                  }
                  catch
                  {
                      // Return null to signify failure.
                      return null;
                  }
              }
      
              /// <summary>
              /// Populates a book BookDetails class with the data for a book.
              /// </summary>
              private XElement[] FormatBookData(BookDetails book)
              {
                  XElement[] bookInfo =
                  {
                      new XElement("author", book.Author),
                      new XElement("title", book.Title),
                      new XElement("genre", book.Genre),
                      new XElement("price", book.Price.ToString()),
                      new XElement("publish_date", book.PublishDate.ToString()),
                      new XElement("description", book.Description)
                  };
                  return bookInfo;
              }
      
              /// <summary>
              /// Method to update an existing book in the catalog.
              /// Defines the implementation of the PUT method.
              /// </summary>
              public BookDetails UpdateBook(String id, BookDetails book)
              {
                  try
                  {
                      // Retrieve a specific book from the catalog.
                      XElement updateBook = xmlDocument.XPathSelectElement(String.Format("catalog/book[@id='{0}']", id));
                      // Verify that the book exists.
                      if (updateBook != null)
                      {
                          // Create elements for each of the book's data items.
                          XElement[] bookInfo = FormatBookData(book);
                          // Add the element to the book element.
                          updateBook.ReplaceNodes(bookInfo);
                          // Save the XML document.
                          xmlDocument.Save(xmlFilename);
                          // Return an object for the updated book.
                          return this.ReadBook(id);
                      }
                  }
                  catch (Exception ex)
                  {
                      // Rethrow the exception.
                      throw ex;
                  }
                  // Return null to signify failure.
                  return null;
              }
      
              /// <summary>
              /// Method to remove an existing book from the catalog.
              /// Defines the implementation of the DELETE method.
              /// </summary>
              public Boolean DeleteBook(String id)
              {
                  try
                  {
                      if (this.ReadBook(id) != null)
                      {
                          // Remove the specific child node from the catalog.
                          xmlDocument
                              .Elements("catalog")
                              .Elements("book")
                              .Where(x => x.Attribute("id").Value.Equals(id))
                              .Remove();
                          // Save the XML document.
                          xmlDocument.Save(xmlFilename);
                          // Return a success status.
                          return true;
                      }
                      else
                      {
                          // Return a failure status.
                          return false;
                      }
                  }
                  catch (Exception ex)
                  {
                      // Rethrow the exception.
                      throw ex;
                  }
              }
          }
      }
      
    4. 保存并关闭BookDetails.cs文件。Save and close the BookDetails.cs file.

  9. 将书店控制器添加到 Web API 项目:Add the bookstore controller to the Web API project:

    1. 右键单击 "解决方案资源管理器" 中的 "控制器" 文件夹,然后单击 "添加",然后单击 "控制器"。Right-click the Controllers folder in the solution explorer, then click Add, and then click Controller.

    2. 显示 "添加基架" 对话框时,突出显示 " Web API 2 控制器-空",然后单击 "添加"。When the Add Scaffold dialog box is displayed, highlight Web API 2 Controller - Empty, and then click Add.

    3. 显示 "添加控制器" 对话框后,将控制器命名为 " BooksController",然后单击 "添加"。When the Add Controller dialog box is displayed, name the controller BooksController, and then click Add.

    4. 打开BooksController.cs文件时,将文件中的代码替换为以下代码:When the BooksController.cs file is opened, replace the code in the file with the following:

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Net;
      using System.Net.Http;
      using System.Web.Http;
      using BookStore.Models;
      
      namespace BookStore.Controllers
      {
          public class BooksController : ApiController
          {
              private BookRepository repository = null;
      
              // Define the class constructor.
              public BooksController()
              {
                  this.repository = new BookRepository();
              }
      
              /// <summary>
              /// Method to retrieve all of the books in the catalog.
              /// Example: GET api/books
              /// </summary>
              [HttpGet]
              public HttpResponseMessage Get()
              {
                  IEnumerable<BookDetails> books = this.repository.ReadAllBooks();
                  if (books != null)
                  {
                      return Request.CreateResponse<IEnumerable<BookDetails>>(HttpStatusCode.OK, books);
                  }
                  else
                  {
                      return Request.CreateResponse(HttpStatusCode.NotFound);
                  }
              }
      
              /// <summary>
              /// Method to retrieve a specific book from the catalog.
              /// Example: GET api/books/5
              /// </summary>
              [HttpGet]
              public HttpResponseMessage Get(String id)
              {
                  BookDetails book = this.repository.ReadBook(id);
                  if (book != null)
                  {
                      return Request.CreateResponse<BookDetails>(HttpStatusCode.OK, book);
                  }
                  else
                  {
                      return Request.CreateResponse(HttpStatusCode.NotFound);
                  }
              }
      
              /// <summary>
              /// Method to add a new book to the catalog.
              /// Example: POST api/books
              /// </summary>
              [HttpPost]
              public HttpResponseMessage Post(BookDetails book)
              {
                  if ((this.ModelState.IsValid) && (book != null))
                  {
                      BookDetails newBook = this.repository.CreateBook(book);
                      if (newBook != null)
                      {
                          var httpResponse = Request.CreateResponse<BookDetails>(HttpStatusCode.Created, newBook);
                          string uri = Url.Link("DefaultApi", new { id = newBook.Id });
                          httpResponse.Headers.Location = new Uri(uri);
                          return httpResponse;
                      }
                  }
                  return Request.CreateResponse(HttpStatusCode.BadRequest);
              }
      
              /// <summary>
              /// Method to update an existing book in the catalog.
              /// Example: PUT api/books/5
              /// </summary>
              [HttpPut]
              public HttpResponseMessage Put(String id, BookDetails book)
              {
                  if ((this.ModelState.IsValid) && (book != null) && (book.Id.Equals(id)))
                  {
                      BookDetails modifiedBook = this.repository.UpdateBook(id, book);
                      if (modifiedBook != null)
                      {
                          return Request.CreateResponse<BookDetails>(HttpStatusCode.OK, modifiedBook);
                      }
                      else
                      {
                          return Request.CreateResponse(HttpStatusCode.NotFound);
                      }
                  }
                  return Request.CreateResponse(HttpStatusCode.BadRequest);
              }
      
              /// <summary>
              /// Method to remove an existing book from the catalog.
              /// Example: DELETE api/books/5
              /// </summary>
              [HttpDelete]
              public HttpResponseMessage Delete(String id)
              {
                  BookDetails book = this.repository.ReadBook(id);
                  if (book != null)
                  {
                      if (this.repository.DeleteBook(id))
                      {
                          return Request.CreateResponse(HttpStatusCode.OK);
                      }
                  }
                  else
                  {
                      return Request.CreateResponse(HttpStatusCode.NotFound);
                  }
                  return Request.CreateResponse(HttpStatusCode.BadRequest);
              }
          }
      }
      
    5. 保存并关闭BooksController.cs文件。Save and close the BooksController.cs file.

  10. 生成 Web API 应用程序以检查是否有错误。Build the Web API application to check for errors.

步骤2:添加 Windows Phone 8 书店目录项目Step 2: Adding the Windows Phone 8 Bookstore Catalog Project

此端到端方案的下一步是为 Windows Phone 8 创建目录应用程序。The next step of this end-to-end scenario is to create the catalog application for Windows Phone 8. 此应用程序将使用默认用户界面的Windows Phone 数据绑定应用程序模板,它将使用在本教程的第1步中创建的 Web API 应用程序作为数据源。This application will use the Windows Phone Databound App template for the default user interface, and it will use the Web API application that you created in Step 1 of this tutorial as the data source.

  1. 右键单击 "解决方案资源管理器" 中的 "书店解决方案",然后依次单击 "添加" 和 "新建项目"。Right-click the BookStore solution in the in the solution explorer, then click Add, and then New Project.

  2. 显示 "新建项目" 对话框时,依次展开 "已安装" 和 " C#Visual",然后Windows Phone"。When the New Project dialog box is displayed, expand Installed, then Visual C#, and then Windows Phone.

  3. 突出显示Windows Phone 的数据绑定应用程序,输入 " BookCatalog " 作为名称,然后单击 "确定"Highlight Windows Phone Databound App, enter BookCatalog for the name, and then click OK.

  4. 将 Json.NET NuGet 包添加到BookCatalog项目:Add the Json.NET NuGet package to the BookCatalog project:

    1. 在 "解决方案资源管理器" 中,右键单击BookCatalog项目的 "引用",然后单击 "管理 NuGet 包"。Right-click References for the BookCatalog project in the solution explorer, and then click Manage NuGet Packages.
    2. 当显示 "管理 NuGet 包" 对话框时,展开 "联机" 部分,然后突出显示 " nuget.org"。When the Manage NuGet Packages dialog box is displayed, expand the Online section, and highlight nuget.org.
    3. 在搜索字段中输入Json.NET ,然后单击搜索图标。Enter Json.NET in the search field and click the search icon.
    4. 突出显示搜索结果中的Json.NET ,然后单击 "安装"。Highlight Json.NET in the search results, and then click Install.
    5. 安装完成后,单击 "关闭"。When the installation has completed, click Close.
  5. BookDetails模型添加到BookCatalog项目;这包含书店类的通用模型:Add the BookDetails model to the BookCatalog project; this contains a generic model of the bookstore class:

    1. 右键单击解决方案资源管理器中的BookCatalog项目,然后单击 "添加",然后单击 "新建文件夹"。Right-click the BookCatalog project in the solution explorer, then click Add, and then click New Folder.

    2. 命名新文件夹模型Name the new folder Models.

    3. 在 "解决方案资源管理器" 中,右键单击 "模型" 文件夹,单击 "添加",然后单击 ""。Right-click the Models folder in the solution explorer, then click Add, and then click Class.

    4. 显示 "添加新项" 对话框时,将类文件命名为 " BookDetails.cs",然后单击 "添加"。When the Add New Item dialog box is displayed, name the class file BookDetails.cs, and then click Add.

    5. 打开BookDetails.cs文件时,将文件中的代码替换为以下代码:When the BookDetails.cs file is opened, replace the code in the file with the following:

      using System;
      using System.Text;
      
      namespace BookCatalog.Models
      {
          /// <summary>
          /// Define a class that will hold the detailed information for a book.
          /// </summary>
          public class BookDetails
          {
              public String Id { get; set; }
              public String Title { get; set; }
              public String Author { get; set; }
              public String Genre { get; set; }
              public Decimal Price { get; set; }
              public DateTime PublishDate { get; set; }
              public String Description { get; set; }
          }
      }
      
    6. 保存并关闭BookDetails.cs文件。Save and close the BookDetails.cs file.

  6. 更新MainViewModel.cs类,使其包含与书店 Web API 应用程序通信的功能:Update the MainViewModel.cs class to include the functionality to communicate with the BookStore Web API application:

    1. 展开 "解决方案资源管理器" 中的viewmodel文件夹,然后双击MainViewModel.cs文件。Expand the ViewModels folder in the solution explorer, and then double-click the MainViewModel.cs file.

    2. 在打开MainViewModel.cs文件时,将文件中的代码替换为以下代码:请注意,需要将 apiUrl 常量的值更新为你的 Web API 的实际 URL:When the MainViewModel.cs file is opened, replace the code in the file with the following; note that you will need to update the value of the apiUrl constant with the actual URL of your Web API:

      using System;
      using System.Collections.ObjectModel;
      using System.ComponentModel;
      using System.Net;
      using System.Net.NetworkInformation;
      using BookCatalog.Resources;
      using System.Collections.Generic;
      using Newtonsoft.Json;
      using BookCatalog.Models;
      
      namespace BookCatalog.ViewModels
      {
          public class MainViewModel : INotifyPropertyChanged
          {
              const string apiUrl = @"http://www.contoso.com/api/Books";
      
              public MainViewModel()
              {
                  this.Items = new ObservableCollection<ItemViewModel>();
              }
      
              /// <summary>
              /// A collection for ItemViewModel objects.
              /// </summary>
              public ObservableCollection<ItemViewModel> Items { get; private set; }
      
              public bool IsDataLoaded
              {
                  get;
                  private set;
              }
      
              /// <summary>
              /// Creates and adds a few ItemViewModel objects into the Items collection.
              /// </summary>
              public void LoadData()
              {
                  if (this.IsDataLoaded == false)
                  {
                      this.Items.Clear();
                      this.Items.Add(new ItemViewModel() { ID = "0", LineOne = "Please Wait...", LineTwo = "Please wait while the catalog is downloaded from the server.", LineThree = null });
                      WebClient webClient = new WebClient();
                      webClient.Headers["Accept"] = "application/json";
                      webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient_DownloadCatalogCompleted);
                      webClient.DownloadStringAsync(new Uri(apiUrl));
                  }
              }
      
              private void webClient_DownloadCatalogCompleted(object sender, DownloadStringCompletedEventArgs e)
              {
                  try
                  {
                      this.Items.Clear();
                      if (e.Result != null)
                      {
                          var books = JsonConvert.DeserializeObject<BookDetails[]>(e.Result);
                          int id = 0;
                          foreach (BookDetails book in books)
                          {
                              this.Items.Add(new ItemViewModel()
                              {
                                  ID = (id++).ToString(),
                                  LineOne = book.Title,
                                  LineTwo = book.Author,
                                  LineThree = book.Description.Replace("\n", " ")
                              });
                          }
                          this.IsDataLoaded = true;
                      }
                  }
                  catch (Exception ex)
                  {
                      this.Items.Add(new ItemViewModel()
                      {
                          ID = "0",
                          LineOne = "An Error Occurred",
                          LineTwo = String.Format("The following exception occured: {0}", ex.Message),
                          LineThree = String.Format("Additional inner exception information: {0}", ex.InnerException.Message)
                      });
                  }
              }
      
              public event PropertyChangedEventHandler PropertyChanged;
              private void NotifyPropertyChanged(String propertyName)
              {
                  PropertyChangedEventHandler handler = PropertyChanged;
                  if (null != handler)
                  {
                      handler(this, new PropertyChangedEventArgs(propertyName));
                  }
              }
          }
      }
      
    3. 保存并关闭MainViewModel.cs文件。Save and close the MainViewModel.cs file.

  7. 更新MainPage文件以自定义应用程序名称:Update the MainPage.xaml file to customize the application name:

    1. 双击 "解决方案资源管理器" 中的MainPage文件。Double-click the MainPage.xaml file in the solution explorer.

    2. 在打开MainPage文件时,找到以下代码行:When the MainPage.xaml file is opened, locate the following lines of code:

      <StackPanel Grid.Row="0" Margin="12,17,0,28">
          <TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/> 
          <TextBlock Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
      </StackPanel>
      
    3. 将这些行替换为以下内容:Replace those lines with the following:

      <StackPanel Grid.Row="0" Margin="12,17,0,28">
          <TextBlock Text="Book Store" Style="{StaticResource PhoneTextTitle1Style}"/> 
          <TextBlock Text="Current Catalog" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle2Style}"/>
      </StackPanel>
      
    4. 保存并关闭MainPage文件。Save and close the MainPage.xaml file.

  8. 更新DetailsPage文件以自定义显示的项:Update the DetailsPage.xaml file to customize the displayed items:

    1. 双击 "解决方案资源管理器" 中的DetailsPage文件。Double-click the DetailsPage.xaml file in the solution explorer.

    2. 在打开DetailsPage文件时,找到以下代码行:When the DetailsPage.xaml file is opened, locate the following lines of code:

      <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
          <TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
          <TextBlock Text="{Binding LineOne}" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
      </StackPanel>
      
    3. 将这些行替换为以下内容:Replace those lines with the following:

      <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
          <TextBlock Text="Book Store" Style="{StaticResource PhoneTextTitle1Style}"/>
          <TextBlock Text="{Binding LineOne}" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle2Style}"/>
      </StackPanel>
      
    4. 保存并关闭DetailsPage文件。Save and close the DetailsPage.xaml file.

  9. 构建 Windows Phone 应用程序以检查是否有错误。Build the Windows Phone application to check for errors.

步骤3:测试端到端解决方案Step 3: Testing the End-to-End Solution

如本教程的 "先决条件" 部分中所述,当你在本地系统上测试 Web API 与 Windows Phone 8 项目之间的连接时,你将需要按照将 Windows Phone 8 仿真程序连接到本地计算机上的 Web api 应用程序 一文中的说明设置你的测试环境。As mentioned in the Prerequisites section of this tutorial, when you are testing the connectivity between Web API and Windows Phone 8 projects on your local system, you will need to follow the instructions in the Connecting the Windows Phone 8 Emulator to Web API Applications on a Local Computer article to set up your testing environment.

配置测试环境后,需要将 Windows Phone 应用程序设置为启动项目。Once you have the testing environment configured, you will need to set the Windows Phone application as the startup project. 为此,请在 "解决方案资源管理器" 中突出显示BookCatalog应用程序,然后单击 "设为启动项目":To do so, highlight the BookCatalog application in the solution explorer, and then click Set as StartUp Project:

单击图像进行扩展Click image to expand

按 F5 时,Visual Studio 将启动 Windows Phone 模拟器,这将显示 "请等待" 消息,同时从 Web API 检索应用程序数据:When you press F5, Visual Studio will start both the Windows Phone Emulator, which will display a "Please Wait" message while the application data is retrieved from your Web API:

单击图像进行扩展Click image to expand

如果一切成功,应会看到目录显示:If everything is successful, you should see the catalog displayed:

单击图像进行扩展Click image to expand

如果点击任何书籍标题,应用程序会显示书籍说明:If you tap on any book title, the application will display the book description:

单击图像进行扩展Click image to expand

如果应用程序无法与 Web API 通信,则将显示一条错误消息:If the application cannot communicate with your Web API, an error message will be displayed:

单击图像进行扩展Click image to expand

如果点击错误消息,将显示有关错误的任何其他详细信息:If you tap on the error message, any additional details about the error will be displayed:

单击图像进行扩展Click image to expand