review of server-communication to line 593

This commit is contained in:
Zhimin(Rex) YE 2016-06-14 23:43:52 +01:00
parent 7995cb4897
commit 04fd6f7102
1 changed files with 38 additions and 42 deletions

View File

@ -14,7 +14,7 @@ block includes
The [`WebSocket`](https://tools.ietf.org/html/rfc6455) protocol is another important communication technology; The [`WebSocket`](https://tools.ietf.org/html/rfc6455) protocol is another important communication technology;
we won't cover it in this chapter. we won't cover it in this chapter.
[`WebSocket`](https://tools.ietf.org/html/rfc6455)协议是另一种重要的通讯技术,但我们本章不会涉及它。 [`WebSocket`](https://tools.ietf.org/html/rfc6455)协议是另一种重要的通讯技术,但本章不会涉及它。
:marked :marked
Modern browsers support two HTTP-based APIs: Modern browsers support two HTTP-based APIs:
[XMLHttpRequest (XHR)](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) and [XMLHttpRequest (XHR)](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) and
@ -29,7 +29,7 @@ block includes
The !{_Angular_http_library} simplifies application programming of the **XHR** and **JSONP** APIs The !{_Angular_http_library} simplifies application programming of the **XHR** and **JSONP** APIs
as we'll learn in this chapter covering: as we'll learn in this chapter covering:
!{_Angular_http_libraryCn}简化了**XHR**和**JSONP**的编程,这就是本章所要讲的。 !{_Angular_http_libraryCn}简化了**XHR**和**JSONP** API的编程,这就是本章所要讲的。
ul ul
li #[a(href="#http-client") HTTP client sample overview] li #[a(href="#http-client") HTTP client sample overview]
@ -49,9 +49,9 @@ ul
li #[a(href="#update") 把数据发送到服务器] li #[a(href="#update") 把数据发送到服务器]
+ifDocsFor('ts') +ifDocsFor('ts')
li #[a(href="#promises") Promises instead of observables] li #[a(href="#promises") Promises instead of observables]
li #[a(href="#promises") 把承诺(Promise)换成可观察对象(Observable)] li #[a(href="#promises") 使用承诺(Promise)来取代可观察对象(Observable)]
li #[a(href="#cors") Cross-origin requests: Wikipedia example] li #[a(href="#cors") Cross-origin requests: Wikipedia example]
li #[a(href="#cors") 跨域请求:Wikipedia例] li #[a(href="#cors") 跨域请求Wikipedia例]
+ifDocsFor('ts') +ifDocsFor('ts')
ul ul
li #[a(href="#search-parameters") Set query string parameters] li #[a(href="#search-parameters") Set query string parameters]
@ -64,7 +64,7 @@ p.
We illustrate these topics with code that you can We illustrate these topics with code that you can
#[+liveExampleLink2('run live in a browser')]. #[+liveExampleLink2('run live in a browser')].
p. p.
讲解这些主题的代码,可以在#[+liveExampleLink2('浏览器中运行在线版')]. 我们在#[+liveExampleLink2('在线版')]中展示了这些主题,你可以在浏览器中运行它们。
.l-main-section .l-main-section
h1 Demos h1 Demos
@ -84,7 +84,7 @@ ul
:marked :marked
These demos are orchestrated by the root `AppComponent` These demos are orchestrated by the root `AppComponent`
这些演示由根组件`AppComponent`统一指挥 这些演示由根组件`AppComponent`统一演示。
+makeExample('server-communication/ts/app/app.component.ts', null, 'app/app.component.ts') +makeExample('server-communication/ts/app/app.component.ts', null, 'app/app.component.ts')
block rxjs-import block rxjs-import
@ -96,11 +96,11 @@ block rxjs-import
:marked :marked
We'll talk about that [below](#rxjs) when we're ready to explore observables. We'll talk about that [below](#rxjs) when we're ready to explore observables.
等准备好时,我们会在[后面](#rxjs)讲述可观察对象。 我们会在[后面](#rxjs)适当的时间来讲述可观察对象。
:marked :marked
First, we have to configure our application to use server communication facilities. First, we have to configure our application to use server communication facilities.
首先,我们必须配置应用程序才能使用服务器通讯设施。 首先,我们必须配置应用程序才能使用服务器通讯设施。
.l-main-section .l-main-section
h1#http-providers Providing HTTP Services h1#http-providers Providing HTTP Services
h1#http-providers 提供HTTP服务 h1#http-providers 提供HTTP服务
@ -108,8 +108,7 @@ h1#http-providers 提供HTTP服务
We use the !{_Angular_Http} client to communicate with a server using a familiar HTTP request/response protocol. We use the !{_Angular_Http} client to communicate with a server using a familiar HTTP request/response protocol.
The `#{_Http}` client is one of a family of services in the !{_Angular_http_library}. The `#{_Http}` client is one of a family of services in the !{_Angular_http_library}.
我们使用!{_Angular_Http}客户端来与服务器通讯它支持一组HTTP的请求(Request)/回应(Response)协议。 我们通过!{_Angular_Http}客户端使用熟悉的HTTP请求/回应协议与服务器通讯。`#{_Http}`客户端是!{_Angular_http_libraryCn}所提供的服务大家庭中的一员。
`#{_Http}`客户端是!{_Angular_http_libraryCn}所提供的服务大家庭中的一员。
block system-config-of-http block system-config-of-http
.l-sub-section .l-sub-section
:marked :marked
@ -128,7 +127,7 @@ block system-config-of-http
了解关于供应商的更多知识,参见[依赖注入](dependency-injection.html)一章。 了解关于供应商的更多知识,参见[依赖注入](dependency-injection.html)一章。
p In this demo, we register providers in the #[code bootstrap] method of #[code #[+adjExPath('app/main.ts')]]. p In this demo, we register providers in the #[code bootstrap] method of #[code #[+adjExPath('app/main.ts')]].
p 下面这个演示中,我们在#[code #[+adjExPath('app/main.ts')]]文件的#[code bootstrap]方法中注册供应商。 p 在这个例子中,我们在#[code #[+adjExPath('app/main.ts')]]文件的#[code bootstrap]方法中注册供应商。
+makeExample('server-communication/ts/app/main.ts', 'v1', 'app/main.ts (v1)')(format='.') +makeExample('server-communication/ts/app/main.ts', 'v1', 'app/main.ts (v1)')(format='.')
block http-providers block http-providers
@ -161,8 +160,7 @@ block http-providers
This replacement service is called the [*in-memory web api*](#in-mem-web-api). This replacement service is called the [*in-memory web api*](#in-mem-web-api).
因为这是一个*范例程序*,它不会跟真实的服务器打交道。 因为这是一个*范例程序*,它不会跟真实的服务器打交道。
我们准备用一个伪造的供应商来重新配置(通常不可见的)`XhrBackend`服务。这个伪造的供应商会从一个内存存储区中获取和保存范例数据。 我们准备用一个伪造的供应商[*内存Web API*](#in-mem-web-api)来重新配置(通常不可见的)`XhrBackend`服务。这个伪造的供应商会从一个内存存储区中获取和保存范例数据。
这个用于替换的服务称为[*内存Web API*](#in-mem-web-api)。
Such sleight-of-hand is something the root application component should *not* know about. Such sleight-of-hand is something the root application component should *not* know about.
For this reason, and this reason *only*, we hide it *above* the `AppComponent` in `main.ts`. For this reason, and this reason *only*, we hide it *above* the `AppComponent` in `main.ts`.
@ -180,7 +178,7 @@ h1#http-client 《英雄指南》#[i HTTP]客户端的演示
我们的第一个演示是《英雄指南(TOH)》教程的一个迷你版。 我们的第一个演示是《英雄指南(TOH)》教程的一个迷你版。
这个版本从服务器获取一些英雄,把它们显示在列表中,还允许我们添加新的英雄并将其保存到服务器。 这个版本从服务器获取一些英雄,把它们显示在列表中,还允许我们添加新的英雄并将其保存到服务器。
我们借助!{_Angular_Http}客户端,来通过`XMLHttpRequest (XHR)`与服务器通讯。 借助!{_Angular_Http}客户端,我们通过`XMLHttpRequest (XHR)`与服务器通讯。
It works like this. It works like this.
@ -203,7 +201,7 @@ figure.image-display
它使用`ngFor`来展现这个英雄列表。 它使用`ngFor`来展现这个英雄列表。
列表的下方是一个输入框和一个*Add Hero*按钮,在那里,我们可以输入新英雄的名字,并把他们加到数据库中。 列表的下方是一个输入框和一个*Add Hero*按钮,在那里,我们可以输入新英雄的名字,并把他们加到数据库中。
我们在`(click)`事件绑定中使用[模板引用变量](template-syntax.html#ref-vars)`newHeroName`来访问这个输入框的值。 在`(click)`事件绑定中,我们使用[模板引用变量](template-syntax.html#ref-vars)`newHeroName`来访问这个输入框的值。
当用户点击此按钮时,我们把这个值传给组件的`addHero`方法,然后清除它,以备输入新英雄的名字。 当用户点击此按钮时,我们把这个值传给组件的`addHero`方法,然后清除它,以备输入新英雄的名字。
Below the button is an area for an error message. Below the button is an area for an error message.
@ -234,14 +232,14 @@ a#HeroListComponent
This is a golden rule: **always delegate data access to a supporting service class**. This is a golden rule: **always delegate data access to a supporting service class**.
这是一条“黄金法则”:**总是把数据访问工作委托给一个提供支持服务类**。 这是一条“黄金法则”:**总是把数据访问工作委托给一个支持服务类**。
Although _at runtime_ the component requests heroes immediately after creation, Although _at runtime_ the component requests heroes immediately after creation,
we do **not** call the service's `get` method in the component's constructor. we do **not** call the service's `get` method in the component's constructor.
We call it inside the `ngOnInit` [lifecycle hook](lifecycle-hooks.html) instead We call it inside the `ngOnInit` [lifecycle hook](lifecycle-hooks.html) instead
and count on Angular to call `ngOnInit` when it instantiates this component. and count on Angular to call `ngOnInit` when it instantiates this component.
虽然_在运行期间_组件会在创建之后立刻请求这些英雄数据但我们**不**在组件的构造函数中调用此服务的`get`方法。 虽然_在运行期间_组件会在创建之后立刻请求这些英雄数据但我们**不**在组件的构造函数中调用此服务的`get`方法。
而是在`ngOnInit`[生命周期钩子](lifecycle-hooks.html)中调用它Angular会在初始化该组件时调用`ngOnInit`方法。 而是在`ngOnInit`[生命周期钩子](lifecycle-hooks.html)中调用它Angular会在初始化该组件时调用`ngOnInit`方法。
.l-sub-section .l-sub-section
:marked :marked
@ -249,8 +247,8 @@ a#HeroListComponent
Components are easier to test and debug when their constructors are simple and all real work Components are easier to test and debug when their constructors are simple and all real work
(especially calling a remote server) is handled in a separate method. (especially calling a remote server) is handled in a separate method.
这是一项*最佳实践*。 这是*最佳实践*。
当组件的构造函数足够简单,且所有真实的工作(尤其是调用远端服务器)都在一个独立的方法中处理时,组件会更加容易测试和调试。 当组件的构造函数足够简单,且所有真实的工作(尤其是调用远端服务器)都在一个独立的方法中处理时,组件会更加容易测试和调试。
block getheroes-and-addhero block getheroes-and-addhero
:marked :marked
The service's `getHeroes()` and `addHero()` methods return an `Observable` of hero data that the !{_Angular_Http} client fetched from the server. The service's `getHeroes()` and `addHero()` methods return an `Observable` of hero data that the !{_Angular_Http} client fetched from the server.
@ -260,8 +258,8 @@ block getheroes-and-addhero
*Observables* are a big topic, beyond the scope of this chapter. *Observables* are a big topic, beyond the scope of this chapter.
But we need to know a little about them to appreciate what is going on here. But we need to know a little about them to appreciate what is going on here.
*可观察对象(Observable)*是一个很大的主题,远超本章所能覆盖的范围之外 *可观察对象(Observable)*是一个很大的主题,远超本章所能覆盖的范围。
但我们还是要了解它们一点儿,这样才能理解它在本章中那些工作。 但我们还是要初略了解它们,这样才能理解它在本章实例中的作
We should think of an `Observable` as a stream of events published by some source. We should think of an `Observable` as a stream of events published by some source.
We listen for events in this stream by ***subscribing*** to the `Observable`. We listen for events in this stream by ***subscribing*** to the `Observable`.
@ -269,13 +267,13 @@ block getheroes-and-addhero
produces a success event (with the hero data in the event payload) or a fail event (with the error in the payload). produces a success event (with the hero data in the event payload) or a fail event (with the error in the payload).
我们可以把可观察对象`Observable`看做一个由某些“源”发布的事件流。 我们可以把可观察对象`Observable`看做一个由某些“源”发布的事件流。
我们通过***订阅***到可观察对象`Observable`来监听这个流中的事件。 通过***订阅***到可观察对象`Observable`,我们监听这个流中的事件。
在这些订阅中我们指定了当Web请求生成了一个成功事件(有效载荷是英雄数据)或失败事件(有效载荷是错误对象)时该如何采取行动。 在这些订阅中我们指定了当Web请求生成了一个成功事件(有效载荷是英雄数据)或失败事件(有效载荷是错误对象)时该如何采取行动。
:marked :marked
With our basic intuitions about the component squared away, we're ready to look inside the `HeroService`. With our basic intuitions about the component squared away, we're ready to look inside the `HeroService`.
关于组件的浅显讲解已经结束了,我们准备到`HeroService`的内部实现中看看。 关于组件的浅显讲解已经结束了,我们可以到`HeroService`的内部实现中看看。
.l-main-section .l-main-section
a#HeroService a#HeroService
@ -331,9 +329,8 @@ block rxjs
<a id="rxjs"></a> <a id="rxjs"></a>
返回值可能会让我们感到意外。 返回值可能会让我们感到意外。
如果按照很多人在现代JavaScript中所熟悉的那种异步调用方法`get`方法应该返回一个 对熟悉现代JavaScript中的异步调用方法的人来说我们期待`get`方法返回一个[承诺(promise)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)。
[承诺(promise)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)。 我们期待链接调用`then()`方法,并从中取得英雄列表。取而代之,这里调用了一个`map()`方法。
我们可能期待链接调用`then()`方法并从中取得英雄列表,取而代之,我们调用了一个`map()`方法。
显然,这并不是承诺(Promise)。 显然,这并不是承诺(Promise)。
In fact, the `http.get` method returns an **Observable** of HTTP Responses (`Observable<Response>`) from the RxJS library In fact, the `http.get` method returns an **Observable** of HTTP Responses (`Observable<Response>`) from the RxJS library
@ -356,9 +353,8 @@ block rxjs
We certainly need it now when working with the HTTP client. We certainly need it now when working with the HTTP client.
And we must take a critical extra step to make RxJS observables usable. And we must take a critical extra step to make RxJS observables usable.
这本开发指南中的所有例子都安装了RxJS的npm包而且都被`system.js`加载过了。这是因为可观察对象在Angular应用中使用非常广泛。 本开发指南中的所有例子都安装了RxJS的npm包而且都被`system.js`加载过了。这是因为可观察对象在Angular应用中使用非常广泛。
我们在使用HTTP客户端的时候当然就更需要它了。 HTTP客户端更需要它。经过一个关键步骤我们才能让RxJS可观察对象可用。
不过仍然得经过一个关键步骤能让RxJS可观察对象在此处可用。
### Enable RxJS Operators ### Enable RxJS Operators
### 启用RxJS操作符 ### 启用RxJS操作符
@ -367,8 +363,8 @@ block rxjs
We should include only those features that we actually need. We should include only those features that we actually need.
RxJS库实在是太大了。 RxJS库实在是太大了。
我们构建一个产品级应用,并且把它发布到移动设备上的时候,大小就会成为一个问题。 当构建一个产品级应用,并且把它发布到移动设备上的时候,大小就会成为一个问题。
我们应该只包含些我们确实需要的特性。 我们应该只包含些我们确实需要的特性。
Accordingly, Angular exposes a stripped down version of `Observable` in the `rxjs/Observable` module, Accordingly, Angular exposes a stripped down version of `Observable` in the `rxjs/Observable` module,
a version that lacks most of the operators including some we'd like to use here a version that lacks most of the operators including some we'd like to use here
@ -398,13 +394,13 @@ block rxjs
:marked :marked
If we forget an operator, the TypeScript compiler will warn that it's missing and we'll update this file. If we forget an operator, the TypeScript compiler will warn that it's missing and we'll update this file.
如果我们忘了导入某个操作符TypeScript编译器就会警告说找不到它那时候我们再来更新此文件。 如果忘了导入某个操作符TypeScript编译器就会警告说找不到它那时候我们再来更新此文件。
.l-sub-section .l-sub-section
:marked :marked
We don't need _all_ of these particular operators in the `HeroService` &mdash; just `map`, `catch` and `throw`. We don't need _all_ of these particular operators in the `HeroService` &mdash; just `map`, `catch` and `throw`.
We'll need the other operators later, in a *Wiki* example [below](#more-observables). We'll need the other operators later, in a *Wiki* example [below](#more-observables).
在`HeroService`中我们并不需要这里导入的_全部_操作符 —— 我们只用到了`map`、`catch`和`throw`。 在`HeroService`中,我们并不需要这里导入的_全部_操作符 —— 我们只用到了`map`、`catch`和`throw`。
我们稍后的[*Wiki*例子](#more-observables)中,还会用到其它操作符。 我们稍后的[*Wiki*例子](#more-observables)中,还会用到其它操作符。
:marked :marked
Finally, we import `rxjs-operator`_itself_ in our `app.component.ts`: Finally, we import `rxjs-operator`_itself_ in our `app.component.ts`:
@ -414,7 +410,7 @@ block rxjs
:marked :marked
Let's return to our study of the `HeroService`. Let's return to our study of the `HeroService`.
让我们回来继续学习`HeroService`。 让我们回来继续研究`HeroService`。
l-main-section l-main-section
a#extract-data a#extract-data
:marked :marked
@ -429,7 +425,7 @@ a#extract-data
To make it useful in our application we must parse the response data into a JSON object To make it useful in our application we must parse the response data into a JSON object
这个`response`对象并没有以一种我们能直接使用的格式来保存数据。 这个`response`对象并没有以一种我们能直接使用的格式来保存数据。
要让它在应用程序中可用,我们就必须把这个应数据解析成一个JSON对象。 要让它在应用程序中可用,我们就必须把这个应数据解析成一个JSON对象。
#### Parse to JSON #### Parse to JSON
#### 解析成JSON #### 解析成JSON
@ -449,7 +445,7 @@ block parse-json
That spec defines a `json()` method that parses the response body into a JavaScript object. That spec defines a `json()` method that parses the response body into a JavaScript object.
这不是Angular自己的设计。 这不是Angular自己的设计。
Angular HTTP客户端遵循ES2015规范来处理`Fetch`函数返回[响应对象](https://fetch.spec.whatwg.org/#response-class)。 Angular HTTP客户端遵循ES2015规范来处理`Fetch`函数返回[响应对象](https://fetch.spec.whatwg.org/#response-class)。
此规范中定义了一个`json()`函数来把响应体解析成JavaScript对象。 此规范中定义了一个`json()`函数来把响应体解析成JavaScript对象。
.l-sub-section .l-sub-section
@ -460,8 +456,8 @@ block parse-json
This is conventional web api behavior, driven by This is conventional web api behavior, driven by
[security concerns](https://www.owasp.org/index.php/OWASP_AJAX_Security_Guidelines#Always_return_JSON_with_an_Object_on_the_outside). [security concerns](https://www.owasp.org/index.php/OWASP_AJAX_Security_Guidelines#Always_return_JSON_with_an_Object_on_the_outside).
我们不期待解码后的JSON直接就是一个英雄数组。 我们不应该期待解码后的JSON直接就是一个英雄数组。
我们调用的这个服务器总会把JSON结果包装进一个带`data`属性的对象中。 调用的这个服务器总会把JSON结果包装进一个带`data`属性的对象中。
我们必须解开它才能得到英雄数组。这是一个约定俗成的Web API行为规范它是出于 我们必须解开它才能得到英雄数组。这是一个约定俗成的Web API行为规范它是出于
[安全方面的考虑](https://www.owasp.org/index.php/OWASP_AJAX_Security_Guidelines#Always_return_JSON_with_an_Object_on_the_outside)。 [安全方面的考虑](https://www.owasp.org/index.php/OWASP_AJAX_Security_Guidelines#Always_return_JSON_with_an_Object_on_the_outside)。
.alert.is-important .alert.is-important
@ -500,7 +496,7 @@ block parse-json
`#{_priv}http.get`**仍然没有发送请求!**这是因为可观察对象是 `#{_priv}http.get`**仍然没有发送请求!**这是因为可观察对象是
[*冷淡的*](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/creating.md#cold-vs-hot-observables) [*冷淡的*](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/creating.md#cold-vs-hot-observables)
这意味着,直到某人*订阅*了这个可观察对象时,这个请求才会被发出。 也就是说,只有当某人*订阅*了这个可观察对象时,这个请求才会被发出。
这个场景中的*某人*就是[HeroListComponent](#subscribe)。 这个场景中的*某人*就是[HeroListComponent](#subscribe)。
a#error-handling a#error-handling
@ -515,7 +511,7 @@ a#error-handling
一旦开始与I/O打交道我们就必须准备好接受墨菲定律如果一件倒霉事*可能*发生,它就*迟早会*发生。 一旦开始与I/O打交道我们就必须准备好接受墨菲定律如果一件倒霉事*可能*发生,它就*迟早会*发生。
我们可以在`HeroService`中捕获错误,并对它们做些处理。 我们可以在`HeroService`中捕获错误,并对它们做些处理。
在用户可以理解并采取相应行动的时候,我们也可以把错误信息传回到组件,让组件展示给最终用户。 只有在用户可以理解并采取相应行动的时候,我们把错误信息传回到组件,让组件展示给最终用户。
In this simple app we provide rudimentary error handling in both the service and the component. In this simple app we provide rudimentary error handling in both the service and the component.
@ -526,7 +522,7 @@ block error-handling
We haven't discussed so far how that actually works. We haven't discussed so far how that actually works.
眼尖的读者可能注意会到,我们联合使用了`catch`操作符和`handleError`方法。 眼尖的读者可能注意会到,我们联合使用了`catch`操作符和`handleError`方法。
我们还没讨论过它实际工作方式。 但还没讨论过它实际工作方式。
We use the Observable `catch` operator on the service level. We use the Observable `catch` operator on the service level.
It takes an error handling function with an error object as the argument. It takes an error handling function with an error object as the argument.
@ -551,7 +547,7 @@ block hlc-error-handling
It sets an `errorMessage` variable which we've bound conditionally in the `HeroListComponent` template. It sets an `errorMessage` variable which we've bound conditionally in the `HeroListComponent` template.
回到`HeroListComponent`,这里我们调用了`#{_priv}heroService.getHeroes()`。我们提供了`subscribe`函数的第二个参数来处理错误信息。 回到`HeroListComponent`,这里我们调用了`#{_priv}heroService.getHeroes()`。我们提供了`subscribe`函数的第二个参数来处理错误信息。
它设置了一个`errorMessage`变量,我们把它有条件的绑定到了`HeroListComponent`模板中。 它设置了一个`errorMessage`变量,有条件的绑定到了`HeroListComponent`模板中。
+makeExample('server-communication/ts/app/toh/hero-list.component.ts', 'getHeroes', 'app/toh/hero-list.component.ts (getHeroes)')(format=".") +makeExample('server-communication/ts/app/toh/hero-list.component.ts', 'getHeroes', 'app/toh/hero-list.component.ts (getHeroes)')(format=".")