【ASP.NET MVC 5】第27章 Web API与单页应用程序
2020-12-13 02:33
YPE html>
标签:des style class blog code java
注:《精通ASP.NET
MVC
3框架》受到了出版社和广大读者的充分肯定,这让本人深感欣慰。目前该书的第4版不日即将出版,现在又已开始第5版的翻译,这里先贴出该书的最后一章译稿,仅供大家参考。
去年,除了翻译《Pro ASP.NET MVC 4》之外,另外还翻译了两本书。一本是《HTML5+CSS3开发实战》(亚马逊、京东),由清华大学出版社出版。另一本是《ASP.NET MVC
4实战》(亚马逊、京东),也由人民邮电出版社出版。
CHAPTER 27
第27章
■ ■ ■
27 Web API and Single-page Applications
27 Web API与单页应用程序
In this chapter, I describe the Web API feature, which is a relatively new
addition to the ASP.NET platform that allows you to quickly and easily create
Web services that provide an API to HTTP clients, known as Web
APIs.
本章描述Web
API特性,它是新添加到ASP.NET平台的,让你能够快速而方便地创建Web服务,以便为HTTP客户端提供API,称之为Web
API。
The Web API feature is based on the same foundation as the MVC Framework
applications, but is not part of the MVC Framework. Instead, Microsoft has taken
some key classes and characteristics that are associated with the System.Web.Mvc
namespace and duplicated them in the System.Web.Http
namespace. The idea is that Web API is part of the core ASP.NET platform and can
be used in other types of Web applications or used as a stand-alone Web services
engine. I have included Web API in this book because one of the main uses for it
is to create single-page applications (SPAs) by combining the
Web API with MVC Framework features you have seen in previous chapters. I‘ll
explain what SPAs are and how they work later in the chapter.
Web
API特性建立在MVC框架应用程序的同样基础之上,但不是MVC框架的一部分。微软从System.Web.Mvc命名空间提取了一些关键的类和特征,并将它们复制到了System.Web.Http命名空间。其思想是,Web
API是核心ASP.NET平台的一部分,因而可以将其用于其他类型的Web应用程序,或者作为独立的Web服务引擎。本书之所以包含Web
API内容,是因为它的主要用途是,你可以将这种Web
API与前几章所看到的MVC框架特性相结合,从而创建一种单页应用程序(SPA,Single-Page
Application)。本章的后面部分将解释这种SPA及其工作机制。
That is not to take away from the way that Web API simplifies creating Web
services. It is a huge improvement over the other Microsoft Web service
technologies that have been appearing over the last decade or so. I like the Web
API and you should use it for your projects, not least because it is simple and
built on the same design that the MVC Framework uses.
这并非是在贬低Web
API简化创建Web服务的方式。它是对微软早在十多年前就已出现的其他Web服务技术的巨大改进。我喜欢这种Web
API,你也应该将它用于自己的项目,尤其因为它简单,而且建立在MVC框架所使用的同样设计模式之上。
I start this chapter by creating a regular MVC Framework application and then
using the Web API to transform it into a single-page application. This is a
surprisingly simple example, so I have treated the process like an extended
example and applied some of the relevant techniques from earlier chapters
because you can never have enough examples. Table 27-1 provides the summary for
this chapter.
本章首先从创建一个常规的MVC框架应用程序开始,然后使用Web
API将其转换成一个单页应用程序。这是一个相当简单的示例,因此,我将这一创建过程处理成一个逐步扩展的示例,并运用前几章的一些相关技术,因为示例是不可能嫌多的。表27-1描述了本章概要。
Problem 问题 |
Solution 解决方案 |
Listing 清单号 |
---|---|---|
Create a RESTful web service 创建REST化的Web服务 |
Add a Web API controller to an MVC Framework application. 在MVC框架的应用程序中添加一个Web API控制器 |
1–10 |
Map between HTTP methods and action names
in a Web API controller Web API控制器中HTTP方法与动作名之间的映射 |
Apply attributes such as HttpPut and HttpPost to the methods. 在动作方法上运用HttpPut、HttpPost等注解属性。 |
11 |
Create a single-page application 创建单页应用程序 |
Use Knockout and jQuery to obtain data via Ajax and bind it to HTML
elements. 使用Knockout和jQuery,以便通过Ajax获取数据,并将这些数据绑定到HTML元素。 |
12–17 |
27.1 Understanding Single-page Applications
27.1 理解单页应用程序
The term single-page application (SPA) is a broadly applied
term. The most consistently-used definition is a web application whose initial
content is delivered as a combination of HTML and JavaScript and whose
subsequent operations are performed using a RESTful web service that delivers
data via JSON in response to Ajax requests.
术语单页应用程序(SPA)是一个宽泛的用语。最惯用的定义指,这是一种Web应用程序,其呈现的最初内容由HTML和JavaScript所组成,而它的后继操作是使用REST化的Web服务执行的,这种服务对Ajax请求进行响应,并通过JSON提供数据。
This differs from the kind of application I have been building in most of the
chapters of this book, where operations performed by the user result in new HTML
documents being generated in response to synchronous HTTP requests, which I will
refer to as round-trip applications (RTAs).
这与本书大部分章节所建立的应用程序不同,在那些应用程序中,用户执行的操作所产生的结果是,对HTTP请求以同步响应的方式生成新的HTML文档,我将其称为往返式应用程序(RTA,Round-Trip
Application)。
The advantages of a SPA are that less bandwidth is required and that the user
receives a smoother experience. The disadvantages are that the smoother
experience can be hard to achieve and that the complexity of the JavaScript code
required for a SPA demands careful design and testing.
SPA的优点是所需的带宽较少,而且用户可以得到更为流畅的体验。缺点是这种流畅的体验难以实现,而且,SPA所需的JavaScript代码较为复杂,需要进行小心的设计与测试。
Most applications mix and match SPA and RTA techniques, where each major
functional area of the application is delivered as a SPA, and navigation between
functional areas is managed using standard HTTP requests that create a new HTML
document.
大多数应用程序会混合和搭配SPA与RTA技术,其中的主要功能区由SPA实现,而各功能区之间的导航,则使用标准的HTTP请求创建新的HTML文档进行管理。
27.2 Preparing the Example Application
27.2 准备示例项目
For this chapter, I created a new ASP.NET project called WebServices using the Empty
template. I checked the options to add the folders and references for both MVC
and Web API applications, as shown in Figure 27-1.
本章使用Empty模板创建了一个新的ASP.NET项目,名称为WebServices,并选中了一些选项,以便同时为MVC和Web
API应用程序添加文件夹和引用,如图27-1所示。
Figure 27-1. Creating the project with the MVC and Web API references
图
27-1. 创建带有MVC和Web API引用的项目
I will use this project to create a regular MVC Framework application and
then use the Web API to create a web service. Once the web service is complete,
I‘ll return to the MVC Framework application and make it into a single-page
application.
本章将用该项目创建一个常规的MVC框架应用程序,然后使用Web
API创建一个Web服务。Web服务完成后,将回到MVC框架应用程序,将其改成单页应用程序。
27.2.1 Creating the Model
27.2.1 创建模型
This application will create and maintain a series of reservations. I want to
keep the application simple so that I can focus on the mechanics of the features
I describe, and so these reservations will consist of just a name and a
location. I added a class file called Reservation.cs
to the Models folder, the contents of which are shown
in Listing 27-1.
该应用程序将创建并维护一系列预约(Reservation)。为保持应用程序简单,以便将注意力集中于本章所描述的特性机制,因此这些预约将只包含姓名和地点。为此,在Models文件夹中添加了一个类文件,名称为Reservation.cs,其内容如清单27-1所示。
Listing 27-1. The Contents of the Reservation.cs File
清单 27-1.
Reservation.cs文件的内容
namespace WebServices.Models { public class Reservation { public int ReservationId { get; set; } public string ClientName { get; set; } public string Location { get; set; } } }
I am going to create a simple in-memory collection of Reservation objects to act as the model repository. I don‘t
want to go to the trouble of setting up a database, but I do need to be able to
perform CRUD operations on a collection of model objects so that I can
demonstrate some important aspects of the Web API. I added a class file called
ReservationRepository.cs to the Models folder and you can see the contents of the new file
in Listing 27-2.
我打算创建一个简单的Reservation对象的内存集合,以此充当模型存储库。我不想介入建立数据库的麻烦,但又确实需要能够对模型对象集合执行CRUD操作,以便能够演示Web
API的一些重要方面。于是在Models文件夹中添加了一个名称为ReservationRepository.cs的类文件,可以从清单27-2看到其内容。
Listing 27-2. The Contents of the ReservationRespository.cs File
清单 27-2.
ReservationRespository.cs文件的内容
using System.Collections.Generic; using System.Linq;
namespace WebServices.Models { public class ReservationRespository { private static ReservationRespository repo = new ReservationRespository();
public static ReservationRespository Current { get { return repo; } }
private Listdata = new List { new Reservation { ReservationId = 1, ClientName = "Adam", Location = "Board Room"}, new Reservation { ReservationId = 2, ClientName = "Jacqui", Location = "Lecture Hall"}, new Reservation { ReservationId = 3, ClientName = "Russell", Location = "Meeting Room 1"}, };
public IEnumerableGetAll() { return data; }
public Reservation Get(int id) { return data.Where(r => r.ReservationId == id).FirstOrDefault(); }
public Reservation Add(Reservation item) { item.ReservationId = data.Count + 1; data.Add(item); return item; }
public void Remove(int id) { Reservation item = Get(id); if (item != null) { data.Remove(item); } }
public bool Update(Reservation item) { Reservation storedItem = Get(item.ReservationId); if (storedItem != null) { storedItem.ClientName = item.ClientName; storedItem.Location = item.Location; return true; } else { return false; } } } }
■Tip In a real project, I would be concerned about tight
coupling between classes and introduce interfaces and dependency injection into
the application. My focus in this chapter is just on the Web API and SPA
applications, so I am going to take some shortcuts when it comes to other
standard techniques.
提示:在一个实际项目中,我会关注类与类之间的紧偶合情况,并在应用程序中引入接口和依赖性注入。但本章的焦点只是Web
API和SPA应用程序,因此,在涉及其他标准技术时,我会采取一些简化。
The repository class has an initial list of three Reservation objects and defines methods that allow me to
view, add, delete and update the collection. Since there is no persistent
storage, any changes that are made to the repository will be lost when the
application is stopped or restarted, but this example is all about the way in
which content can be delivered and not how it is stored by the server. To ensure
that there is some persistence between requests, I have created a static
instance of the ReservationRespository class, which
is accessible through the Current property.
上述存储库类是一个有三个初始Reservation对象的列表,并定义了能够对该集合进行浏览、添加、删除和更新的方法。由于没有持久化存储,当应用程序停止或重启时,对存储库所做的修改都会丢失,但该示例只关系到递送内容的方式,而不是如何用服务器进行存储。为了确保能够在请求之间表现一些持久化的情况,这里创建了一个静态的ReservationRespository类实例,可以通过Current属性进行访问。
27.2.2 Adding the NuGet Packages
27.2.2 添加NuGet包
I am going to rely on three NuGet packages in this chapter: jQuery, Bootstrap
and Knockout. I have already described and used jQuery and Bootstrap in earlier
chapters. Knockout is the library that Microsoft has adopted for single-page
applications. It was created by Steve Sanderson, whom I worked with on an
earlier edition of this book and who works for the Microsoft ASP.NET team. Even
though Steve works for Microsoft, the Knockout package is open source and widely
used and you can learn more about it at http://knockoutjs.com. I‘ll explain how
Knockout works later in the chapter, but for the moment I just need to install
the NuGet packages. Select Package Manager Console
from the Visual Studio Tools → Library Package Manager menu and enter the following
commands:
本章打算依靠三个NuGet包:jQuery、Bootstrap和Knockout。在前面几章中已经描述并使用过jQuery和Bootstrap。Knockout是微软为单页应用程序而采纳的一个库。它是Steve
Sanderson所创建的,他曾和我一起著作过本书的早期版本,现在微软的ASP.NET团队工作。尽管Steve为微软工作,但Knockout包是开源的,而且被广泛使用,你可以从http://knockoutjs.com了解更多情况。本章稍后将解释Knockout如何工作,但此刻只需要安装这一NuGet包。在Visual
Studio中选择“Tools(工具)” →“Library
Package Manager(库包管理器)”,以进入“Package Manager
Console(包管理器控制台)”,然后输入如下命令:
Install-Package jquery –version 1.10.2 Install-Package bootstrap –version 3.0.0 Install-Package knockoutjs –version 3.0.0
27.2.3 Adding the Controller
27.2.3 添加控制器
I added a controller called Home to the example
project, the definition of which you can see in Listing 27-3.
我在项目中添加一个Home控制器,其定义如清单27-3所示。
Listing 27-3. The Contents of the HomeController.cs File
清单 27-3.
HomeController.cs文件的内容
using System.Web.Mvc; using WebServices.Models;
namespace WebServices.Controllers {
public class HomeController : Controller { private ReservationRespository repo = ReservationRespository.Current;
public ViewResult Index() { return View(repo.GetAll()); }
public ActionResult Add(Reservation item) { if (ModelState.IsValid) { repo.Add(item); return RedirectToAction("Index"); } else { return View("Index"); } }
public ActionResult Remove(int id) { repo.Remove(id); return RedirectToAction("Index"); }
public ActionResult Update(Reservation item) { if (ModelState.IsValid && repo.Update(item)) { return RedirectToAction("Index"); } else { return View("Index"); } } } }
This is a fairly typical controller for such a simple application. Each of
the action methods corresponds directly to one of the methods in the repository
and the only value that the controller adds is to perform model validation, to
select views, and perform redirections. In a real project, there would be more
business domain logic, of course, but because the example application I am using
is so basic, the controller ends up being little more than a wrapper around the
repository.
对于这样一个简单的应用程序,这是一个相当典型的控制器。其中的每一个方法直接对应于存储库中的相应方法,而且,控制器所添加的值只不过是为了执行模型验证、选择视图,或者执行重定向。当然,在一个实际项目中,应用程序会有较多的业务逻辑,但由于本章的应用程序如此简单,以致该控制器最终只比存储库封装程序多了一点点内容。
27.2.4 Adding the Layout and Views
27.2.4 添加布局和视图
To generate the content for the application, I started by creating the Views/Shared folder and adding a view file called _Layout.cshtml to it, the contents of which are shown by
Listing 27-4.
为了生成应用程序的内容,我首先创建了Views/Shared文件夹,并添加了一个名称为_Layout.cshtml的视图文件,如清单27-4所示。
Listing 27-4. The Contents of the _Layout.cshtml File
清单 27-4.
_Layout.cshtml文件的内容
@{ Layout = null; }@ViewBag.Title @RenderSection("Scripts") @RenderSection("Body")
This is a basic layout that has link elements for
the Bootstrap CSS files. I have defined two layout sections, Scripts and Body, that I will
use to insert content into the layout. My next step was to create the top-level
view for the application. Although I am going through the process of creating a
regular MVC Framework application, I know that I am going to end up with a
single-page application and the transformation will be made easier if I create a
single view that contains all the HTML that the application will require, even
if it results in an odd appearance initially. I added a view file called Index.cshtml to the Views/Home
folder, the contents of which you can see in Listing 27-5.
这是一个基本的布局,它具有引用Bootstrap CSS文件(Bootstrap的样式表文件——译者注)的link元素。其中定义了Scripts和Body两个布局片段,用于将内容插入该布局。下一步是创建应用程序的顶级视图。虽然中间会经过创建常规MVC框架应用程序的过程,但我知道,最终这是一个单页应用程序,而且,如果创建一个单一的视图,其中包含该应用程序所需要的全部HTML,后面的转换会更为容易,尽管该视图最初的外观有点怪异。为此,在Views/Home文件夹中添加了一个名称为Index.cshtml的视图,如清单27-5所示。
Listing 27-5. The Contents of the Index.cshtml File
清单 27-5.
Index.cshtml文件的内容
@using WebServices.Models
@model IEnumerable
@{ ViewBag.Title = "Reservations"; Layout = "~/Views/Shared/_Layout.cshtml"; }
@section Scripts { }
@section Body {@Html.Partial("Summary", Model)@Html.Partial("Editor", new Reservation())}
The view model for this view is an enumeration of Reservation objects and I rely on two partial views to
provide the functional building blocks that the user will see. The first partial
view file is called Summary.cshtml. I created the
file in the Views/Home folder and you can see the
contents of the file in Listing 27-6.
该视图的视图模型是一个Reservation对象的枚举,我依靠两个分部分视图提供了用户可见的功能块。第一个分部视图文件叫做Summary.cshtml。这是在Views/Home文件夹中创建的,可以从清单27-6看到其内容。
Listing 27-6. The Contents of the Summary.cshtml File
清单 27-6.
Summary.cshtml文件的内容
@model IEnumerable Reservation Summary
@foreach (var item in Model) { ID Name Location } @item.ReservationId @item.ClientName @item.Location @Html.ActionLink("Remove", "Remove", new { id = item.ReservationId }, new { @class = "btn btn-xs btn-primary" })
The view model for the partial view is the same enumeration of Reservation
object and I use it to generate a Bootstrap-styled table element that displays
the object property values. I use the Html.ActionLink
helper method to generate a link that will invoke the Remove action on the Home
controller and use Bootstrap to style it as a button.
用于该分部视图的视图模型同样是一个Reservation枚举对象,用它生成了一个Bootstrap风格的table元素,在其中显示各个对象的属性值。这里用Html.ActionLink辅助器方法生成了一个链接,它将调用Home控制器中的Remove动作,并使用Bootstrap使其成为一个按钮样式。
The other partial view is called Editor.cshtml and
I put this in the Views/Home folder as well. Listing
27-7 shows the contents of this file. This partial view contains a form that can
be used to create new reservations. Submitting the form invokes the Add
action on the Home controller.
另一个分部视图叫做Editor.cshtml,也放在Views/Home文件夹中。清单27-7显示了该文件的内容。该分部视图含有一个表单,用它创建新的预约。递交该表单会调用Home控制器中的Add动作。
Listing 27-7. The Contents of the Editor.cshtml File
清单 27-7.
Editor.cshtml文件的内容
@model WebServices.Models.ReservationCreate Reservation@using(Html.BeginForm("Add", "Home")) {@Html.TextBoxFor(m => m.ClientName, new {@class = "form-control" })@Html.TextBoxFor(m => m.Location, new { @class = "form-control" })}
27.2.5 Setting the Start Location and Testing the Example Application
27.2.5 设置启动位置并测试示例应用程序
The last preparatory step is to set the location that Visual Studio will
navigate to when the application is started. Select WebServices Properties from the Visual Studio Project
menu, switch to the Web tab and check the Specific Page option in the Start
Action section. You don‘t have to provide a value. Just checking the
option is enough. To test the application in its classic MVC Framework form,
select Start Debugging from the Visual Studio Debug menu. You will see the (slightly odd) all-in-one
layout that provides the user with a list of the current reservations and the
ability to create and delete items, as shown in Figure 27-2.
最后一项准备工作是设置启动位置,应用程序启动时,Visual Studio将导航到该位置。在Visual Studio的“Project(项目)”菜单中选择“WebServices
Properties(WebServices属性)”,切换到“
Web”选项卡,并选中“ Start Action(启动行为)”小节中的“ Specific
Page(特定页面)”选项。不必为其提供一个值,只需选中该选项即可。为了对这一传统形式的MVC框架表单应用程序进行测试,可从Visual
Studio的“ Debug(调试)”菜单中选择“ Start
Debugging(开始调试)”。这将看到一个一体化的布局(有点怪),它给用户提供了一个当前预约的列表,并能够创建和删除条目,如图27-2所示。
Figure 27-2. Testing the example application
图 27-2. 测试示例应用程序
27.3 Using Web API
27.3 使用Web API
The Web API feature is based on adding a special kind of controller to an MVC
Framework application. This kind of controller, called an API
Controller, has two distinctive characteristics:
Web
API特性是在MVC框架应用程序基础上添加一种特殊的控制器,这种控制叫做API控制器(API
Controller),它有两个明显的特征:
- Action methods return model, rather than ActionResult, objects.
动作方法返回的是模型对象,而不是ActionResult对象。 - Action methods are selected based on the HTTP method used in the
request.
动作方法是根据请求所使用的HTTP方法来选择的。
The model objects that are returned from an API controller action method are
encoded as JSON and sent to the client. API controllers are designed to deliver
Web data services, so they do not support views, layouts, or any of the other
features that I used to generate HTML in the example application.
API控制器的动作方法所返回的模型对象被编码成JSON,并发送给客户端。API控制器的设计目的是提供Web的数据服务,因此,它们不支持视图、布局,也不支持用来在示例应用程序中生成HTML的任何其它特性。
■Tip The inability of an API controller to generate HTML
from views is the reason that single-page applications combine standard MVC
Framework techniques with the Web API. The MVC Framework performs the steps
required to deliver HTML content to the user (including authentication,
authorization, and selecting and rendering a view). Once the HTML is delivered
to the browser, the Ajax requests generated by the JavaScript it contains are
handled by the Web API controller.
提示:API控制无法通过视图生成HTML,这正是单页应用程序需要将标准的MVC框架技术与Web
API相结合的原因。MVC框架执行的一些步骤是向用户投递HTML内容(包括认证、授权,以及选择并渲染视图等)。一旦给浏览器提供了HTML,由其中包含的JavaScript生成的Ajax请求,便可以由Web
API控制器来处理了。
从这里可以看出,Web API的职责其实很简单,就是在服务器端对客户端发送过来的请求(通常为Ajax请求)进行响应,并为该请求准备数据,然后将数据回递给客户端。说得更简单一点,就是为客户端提供Web服务。
另一方面,为了实现这种Web服务,客户端需要做两件事:第一,“要求服务”。客户端要向服务器发送要求服务的请求,这通常为Ajax请求。因此,客户端通常需要使用JavaScript代码形成这种请求(通常会使用jQuery库,或渐近式Ajax库)。客户端要做的第二件事是,“处理服务”。客户端要对Web API回发过来的数据进行处理,这通常又需要用JavaScript代码对这些数据(通常为JSON格式的数据)进行解析,并将这些数据显示出来。这两件事都是在客户端发生的——译者注
As I demonstrated in Chapter 23, you can create action methods in regular
controllers that return JSON data to support Ajax, but the API controller offers
an alternative approach that separates the data-related actions in your
application from the view-related actions, and makes creating a general-purpose
Web API quick and simple.
正如第23章所演示的,你可&am
文章标题:【ASP.NET MVC 5】第27章 Web API与单页应用程序
文章链接:http://soscw.com/essay/25820.html