AngularDart 4.0 高级-HTTP 客户端 顶

大多数前端应用程序使用HTTP协议与后端服务进行通信。 Dart网络应用程序通常使用XMLHttpRequest(XHR)API执行此操作,使用dart:html库中的HttpRequest或更高级别的API(例如http包提供的内容)。

以下演示使用http软件包来说明服务器通信:

试试主持两个演示的实例(查看源代码)。

提供HTTP服务

此页的demo使用了http包的Client接口. 下面的代码为Client注册了一个 factory provider (创建了一个 BrowserClient 实例) :

 web/main.dart (v1)

import 'package:angular/angular.dart';
import 'package:http/browser_client.dart';
import 'package:http/http.dart';
import 'package:server_communication/app_component.dart';

void main() {
  bootstrap(AppComponent, [
    provide(Client, useFactory: () => new BrowserClient(), deps: [])
  ]);
}

HTTP客户端演示:英雄之旅

此demo是Tour of Heroes应用程序的缩小版本. 它从服务中接收heroes并且在列表中展示它们.用户可以添加一个新的Hero并且保存到服务端.

下面是应用程序的UI:

此demo有一个单独的组件, HeroListComponent. 下面是它的模板:

lib/src/toh/hero_list_component.html

<h1>Tour of Heroes</h1>
<h3>Heroes:</h3>
<ul>
  <li *ngFor="let hero of heroes">{{hero.name}}</li>
</ul>

<label>New hero name: <input #newHeroName /></label>
<button (click)="addHero(newHeroName.value); newHeroName.value=''">Add Hero</button>

<p class="error" *ngIf="errorMessage != null">{{errorMessage}}</p>

模板的ngFor指令显示heroes列表.列表下面是输入框和Add Hero按钮,允许用户添加新的英雄.

一个模板引用变量, newHeroName, 赋予(click)事件绑定存取输入框的值. 当用户单击按钮时, 单击处理程序传递输入值到addHero()方法. 单击处理程序清空输入框.

按钮下面是错误消息区域.

HeroListComponent 类

这是组件类:lib/src/toh/hero_list_component.dart (class)

class HeroListComponent implements OnInit {
  final HeroService _heroService;
  String errorMessage;
  List<Hero> heroes = [];

  HeroListComponent(this._heroService);

  Future<Null> ngOnInit() => getHeroes();

  Future<Null> getHeroes() async {
    try {
      heroes = await _heroService.getHeroes();
    } catch (e) {
      errorMessage = e.toString();
    }
  }

  Future<Null> addHero(String name) async {
    name = name.trim();
    if (name.isEmpty) return;
    try {
      heroes.add(await _heroService.create(name));
    } catch (e) {
      errorMessage = e.toString();
    }
  }
}

Angular 注入 HeroService 到构造器,组件调用服务提取和保存数据.

组件不直接与Client作用.替而代之,它委派数据到HeroService.

始终将数据访问权委派给支持的服务类。

虽然 在运行时组件在创建之后立即请求heroes, 此请求 不在组件的构造器内. 替而代之,请求在ngOnInit生命周期钩子.

保持构造器简单。 当组件的构造器很简单时,组件更容易测试和调试,而所有真正的工作(如调用远程服务器)都是由单独的方法处理的。

hero 服务中的异步方法, getHeroes() 和 create(), 返回Future值(当前英雄列表和最近添加的英雄), 各自地. 英雄列表组件中的方法, getHeroes() 和addHero(), 指定当异步方法调用成功或失败时采取的操作.

关于Future的更多信息,查看 futures tutorial 资源在指导的最后.

获取数据

在之前的示例中,应用通过返回服务中的模拟英雄来伪造与服务器的交互:

import 'dart:async';

import 'package:angular/angular.dart';

import 'hero.dart';
import 'mock_heroes.dart';

@Injectable()
class HeroService {
  Future<List<Hero>> getHeroes() async => mockHeroes;
}

是时候获得真实的数据了。 以下代码使HeroService从服务器获取英雄:

lib/src/toh/hero_service.dart (revised)

import 'dart:async';
import 'dart:convert';

import 'package:angular/angular.dart';
import 'package:http/http.dart';

import 'hero.dart';

@Injectable()
class HeroService {
  static final _headers = {'Content-Type': 'application/json'};
  static const _heroesUrl = 'api/heroes'; // URL to web API
  final Client _http;

  HeroService(this._http);

  Future<List<Hero>> getHeroes() async {
    try {
      final response = await _http.get(_heroesUrl);
      final heroes = _extractData(response)
          .map((value) => new Hero.fromJson(value))
          .toList();
      return heroes;
    } catch (e) {
      throw _handleError(e);
    }
  }

  Future<Hero> create(String name) async {
    try {
      final response = await _http.post(_heroesUrl,
          headers: _headers, body: JSON.encode({'name': name}));
      return new Hero.fromJson(_extractData(response));
    } catch (e) {
      throw _handleError(e);
    }
  }

使用一个 Client 对象

此demo 使用一个Client对象,注入到HeroService构造器中:

HeroService(this._http);

下面的代码使用client的get()方法取得数据:

lib/src/toh/hero_service.dart (getHeroes)

static const _heroesUrl = 'api/heroes'; // URL to web API
Future<List<Hero>> getHeroes() async {
  try {
    final response = await _http.get(_heroesUrl);
    final heroes = _extractData(response)
        .map((value) => new Hero.fromJson(value))
        .toList();
    return heroes;
  } catch (e) {
    throw _handleError(e);
  }
}

get()方法取得资源URL, 用来连接返回英雄数据的服务器.

模拟服务器

如果还没有服务器存在,或者想要在测试期间避免网络可靠性问题,请不要将BrowserClient作为Client对象。 相反,您可以通过使用内存中的Web API来模拟服务器,这是实例源代码)的作用。

或者,使用JSON文件:

static const _heroesUrl = 'heroes.json'; // URL to JSON file

处理response 对象

getHeroes()方法使用 _extractData() 助手方法映射 _http.get()响应对象到 heroes:

lib/src/toh/hero_service.dart (excerpt)

dynamic _extractData(Response resp) => JSON.decode(resp.body)['data'];

response对象不能在表单中持有数据应用程序能立即使用.使用响应数据, 首先要解码它.

解码JSON

响应数据采用JSON字符串形式。 您必须将该字符串反序列化为对象,您可以通过调用dart:convert库中的JSON.decode()方法来执行此操作。 有关解码和编码JSON的示例,请参阅Dart库游览的dart:convert部分。

码后的JSON不会列出英雄。 相反,服务器将JSON结果封装到具有数据属性的对象中。 这是传统的Web API行为,受安全问题驱动。

不要假设服务器API。 并非所有的服务器都返回一个带有数据属性的对象

不要返回响应对象

尽管getHeroes()有可能返回HTTP响应,但这不是一个好习惯。 数据服务的重点在于隐藏消费者的服务器交互细节。 调用HeroService的组件只需要heroes。 它与负责获取数据的代码以及响应对象分离。

始终处理错误

处理I / O的一个重要部分是通过准备捕捉它们并与它们做某些事情来预测错误。 处理错误的一种方法是将错误消息传回组件,以便呈现给用户,但前提是该消息是用户可以理解并采取行动的内容。

这个简单的应用程序处理getHeroes()错误,如下所示:

lib/src/toh/hero_service.dart (excerpt)

Future<List<Hero>> getHeroes() async {
  try {
    final response = await _http.get(_heroesUrl);
    final heroes = _extractData(response)
        .map((value) => new Hero.fromJson(value))
        .toList();
    return heroes;
  } catch (e) {
    throw _handleError(e);
  }
}

Exception _handleError(dynamic e) {
  print(e); // for demo purposes only
  return new Exception('Server error; cause: $e');
}

HeroListComponent 错误处理

在HeroListComponent中, _heroService.getHeroes()在一个try子句中, errorMessage 变量有条件的绑定在模板中.errorMessage 变量将被指定一个值:

lib/src/toh/hero_list_component.dart (getHeroes)

Future<Null> getHeroes() async {
  try {
    heroes = await _heroService.getHeroes();
  } catch (e) {
    errorMessage = e.toString();
  }
}

要创建失败场景,请在HeroService中将API端点重置为错误值。 之后,请记住恢复其原始值。

发送数据到服务器

已经知道了如何使用远程HTTP服务恢复数据.下一项任务是添加增加英雄并保存到后端的能力.

首先, 服务需要一个组件能够调用来创建和保存一个英雄的方法. 对于此demo, 方法叫做 create() 并且接收新英雄的name:

Future<Hero> create(String name) async {

实现这个方法,你需要知道创建英雄服务的API. 这个简单的数据服务遵循典型的REST指导方针. 它支持一个POST请求 和GET heroes使用了同样的端点. 新英雄数据必须在请求体中,结构如同一个Hero 实体但是没有id 属性.下面是例子的请求体:

{ "name": "Windstorm" }

服务器生成id并返回新英雄的JSON表示,包括生成的ID。 英雄在一个拥有自己data属性的响应对象中。

现在你已经知道了服务器的API,下面是create()的实现:

lib/src/toh/hero_service.dart (create)

Future<Hero> create(String name) async {
  try {
    final response = await _http.post(_heroesUrl,
        headers: _headers, body: JSON.encode({'name': name}));
    return new Hero.fromJson(_extractData(response));
  } catch (e) {
    throw _handleError(e);
  }
}

Headers

在_headers对象中, Content-Type指定响应体使用JSON数据格式.

JSON 结果

如同在getHeroes()中, _extractData() 帮助器从response中提取数据.

返回到HeroListComponent中, addHero() 方法 等待服务的异步方法create() 创建一个英雄. 当 create() 执行完成时, addHero() 添加一个新英雄到 heroes 列表:

lib/src/toh/hero_list_component.dart (addHero)

Future<Null> addHero(String name) async {
  name = name.trim();
  if (name.isEmpty) return;
  try {
    heroes.add(await _heroService.create(name));
  } catch (e) {
    errorMessage = e.toString();
  }
}

跨域请求: Wikipedia 例子

尽管在Dart web 应用程序中使用XMLHttpRequests (通常使用助手API, 例如 BrowserClient)进行服务器通信是一种常见的方法,但此方法并不总是合适.

考虑到安全因素, 浏览器阻止XHR访问远程服务器(与web页不在同一个源). 源 是URI 方案, 主机名, 和端口号组成的. 被称作same-origin方针.

如果服务器支持CORS协议,现代浏览器允许来自不同来源的服务器的XHR请求。 您可以在请求标头中启用用户凭据。

一些服务器不支持CORS但支持旧的形式, 只读的JSONP.

有关JSONP的更多信息,请参阅Stack Overflow

搜索 Wikipedia

下面的例子展示Wikipedia用户在文本框中打字:

Wikipedia 提议了一个CORS API 和一个兼容的 JSONP 搜索 API.

本页面正在建设中。 现在,请参阅演示源代码以获取使用Wikipedia的JSONP API的示例。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏安富莱嵌入式技术分享

【RL-TCPnet网络教程】第30章 RL-TCPnet之SNTP网络时间获取

本章节为大家讲解RL-TCPnet的SNTP应用,学习本章节前,务必要优先学习第29章的NTP基础知识。有了这些基础知识之后,再搞本章节会有事半功倍的效果。

12920
来自专栏Seebug漏洞平台

CVE-2017-16943 Exim UAF漏洞分析——后续

上一篇分析出来后,经过@orange的提点,得知了meh公布的PoC是需要特殊配置才能触发,所以我上一篇分析文章最后的结论应该改成,在默认配置情况下,meh提供...

43480
来自专栏腾讯大数据的专栏

网卡收包流程

0.前言 为提升信鸽基础服务质量,笔者就网络收包全流程进行了内容整理。 网络编程中我们接触得比较多的是socket api和epoll模型,对于系统内核和网卡驱...

2.3K140
来自专栏芋道源码1024

Java中高级面试题(4)

这里选了几道高频面试题以及一些解答。不一定全部正确,有一些是没有固定答案的,如果发现有错误的欢迎纠正,如果有更好的回答,热烈欢迎留言探讨。

20300
来自专栏hbbliyong

由一道面试题来了解进程间的通信

周末面试碰到一个面试题,题目是: 在MMO游戏中,服务器采用Linux操作系统,网络通信与游戏逻辑处理进程一般是分离的。 例如:GameSvr进程处理游戏逻辑,...

87070
来自专栏Java学习网

Java 常用开发工具介绍,重点是Eclipse的使用及注意事项,很实用

1、Java 开发工具 ( 常见开发工具介绍 ) A: 操作系统自带的记事本软件 B: 高级记事本软件 C: 集成开发环境 IDE (Integrated De...

35480
来自专栏Java职业技术分享

分布式 | Dubbo 架构设计详解

Dubbo是Alibaba开源的分布式服务框架,它最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合(或者最大限度地松耦合)。从服务模型的角度...

27700
来自专栏Golang语言社区

【Go 语言社区】用Go实现的简易TCP通信框架--转

接触到GO之后,GO的网络支持非常令人喜欢。GO实现了在语法层面上可以保持同步语义,但是却又没有牺牲太多性能,底层一样使用了IO路径复用,比如在LINUX下用了...

422100
来自专栏有趣的django

Django REST framework+Vue 打造生鲜超市(十二) 十三、首页、商品数量、缓存和限速功能开发

十三、首页、商品数量、缓存和限速功能开发  13.1.轮播图接口实现 首先把pycharm环境改成本地的,vue中local_host也改成本地  (1)goo...

74170
来自专栏北京马哥教育

一万两千字长文,六大问题为你解读计算机

1描述计算机的组成及其功能 电子计算机,亦称电脑,是一种利用电子学原理,根据一系列指令对数据进行处理的工具 计算机及其组成 计算机是什么       电子计...

389100

扫码关注云+社区

领取腾讯云代金券