专栏名称: 前端JavaScript
分享 | 学习 | 交流 | 原创 分享是学习的开始;学习不必要从头开始,是从现在开始;交流能沟通你我,提高你的学识;期待你的加入!!! web前端技术交流,JavaScript,HTML5,CSS3……
目录
相关文章推荐
51好读  ›  专栏  ›  前端JavaScript

【第3章第336】Angular 4.x HttpModule 揭秘

前端JavaScript  · 公众号  · Javascript  · 2017-05-02 07:27

正文

HttpModule

Angular Orgs Members - Http 示例

app.component.ts


import { Component, OnInit } from '@angular/core';

import { Http } from '@angular/http'; // (1)

import 'rxjs/add/operator/map'; // (2)


interface Member {

id: string;

login: string;

avatar_url: string;

}


@Component({

selector: 'exe-app',

template: `

Angular Orgs Members

  • ID: {{member.id}}

    Name: {{member.login}}

`

})

export class AppComponent implements OnInit {

members: Member[];


constructor(private http: Http) { } // (3)


ngOnInit() {

this.http.get(`https://api.github.com/orgs/angular/members?page=1&per_page=5`) // (4)

.map(res => res.json()) // (5)

.subscribe(data => {

if (data) this.members = data; // (6)

});

}

}

示例说明:


(1) 从 @angular/http 模块中导入 Http 类


(2) 导入 RxJS 中的 map 操作符


(3) 使用 DI 方式注入 http 服务


(4) 调用 http 服务的 get() 方法,设置请求地址并发送 HTTP 请求


(5) 调用 Response 对象的 json() 方法,把响应体转成 JSON 对象


(6) 把请求的结果,赋值给 members 属性


是不是感觉上面的示例太简单了,请深吸一口气,我们再来看一下如果没有使用 HttpModule ,应该如何实现上述的功能。


Angular Orgs Members - XMLHttpRequest 示例

app.component.ts


import { Component, OnInit } from '@angular/core';


interface Member {

id: string;

login: string;

avatar_url: string;

}


@Component({

selector: 'exe-app',

template: `

Angular Orgs Members

  • ID: {{member.id}}

    Name: {{member.login}}

`

})

export class AppComponent implements OnInit {

members: Member[];


getMembers() {

let MEMBERS_URL = `https://api.github.com/orgs/angular/members?page=1&per_page=5`;

let xhr = new XMLHttpRequest(); // (1)

xhr.open("GET", MEMBERS_URL); // (2)

xhr.onreadystatechange = () => { // (3)

if (xhr.readyState == 4 && xhr.status == 200) { // (4)

if (xhr.responseText) {

try {

this.members = JSON.parse(xhr.responseText); // (5)

} catch (error) {

throw error;

}

}

}

};

xhr.send(null); // (6)

}


ngOnInit() {

this.getMembers();

}

}

示例说明:


(1) 创建 XMLHttpRequest 对象


(2) 设置请求方式和请求 URL 地址


(3) 监听 readyState 状态变化


(4) 判断请求是否完成且请求成功


(5) 把响应体转换为 JSON 对象,并赋值给 members 属性


(6) 发送 HTTP 请求


虽然使用 XMLHttpRequest API 我们也实现了同样的功能,但使用 HttpModule 给我们带来的好处,一目了然。其实 HttpModule 底层实现也是基于 XMLHttpRequest API,只是它对 XMLHttpRequest API 进行了封装,抽象出了 Body、Request、Headers 和 Response 等对象。


HttpModule

请求与响应

HTTP 协议是基于请求与响应,通过 XMLHttpRequest API,我们可以方便的发送 HTTP 请求。相信很多读者已经用过了以下一款或多款 Fiddler、Paw (macOS)、Postman 、Advanced REST client HTTP 客户端,通过它们我们也可以方便的发送 HTTP 请求。其实不管是使用上面的那些 HTTP 客户端还是使用 XMLHttpRequest API,我们最终都是要构造 HTTP 请求报文,然后向服务器发送 HTTP 请求,接着我们就需要接收和解析服务器返回的 HTTP 响应报文,最后根据不同的响应类型解析响应体,进而进行页面渲染。


接下来我们来分析一下前面 Angular Orgs Members - Http 示例中的代码:


export class AppComponent implements OnInit {

members: Member[];

constructor(private http: Http) { }

ngOnInit() {

this.http.get(`https://api.github.com/orgs/angular/members?page=1&per_page=5`)

.map(res => res.json())

.subscribe(data => {

if (data) this.members = data;

});

}

}

首先我们先来分析一下通过构造注入方式,注入的 Http 对象:


constructor(private http: Http) { }

如何创建 Http 对象

angular2/packages/http/src/http_module.ts


@NgModule({

providers: [

{provide: Http, useFactory: httpFactory, deps: [XHRBackend, RequestOptions]},

BrowserXhr,

{provide: RequestOptions, useClass: BaseRequestOptions},

{provide: ResponseOptions, useClass: BaseResponseOptions},

XHRBackend,

{provide: XSRFStrategy, useFactory: _createDefaultCookieXSRFStrategy},

],

})

export class HttpModule { }

httpFactory 工厂函数


export function httpFactory(

xhrBackend: XHRBackend,

requestOptions: RequestOptions): Http {

return new Http(xhrBackend, requestOptions); // 创建Http对象

}

Http 类构造函数


// angular2/packages/http/src/http.ts 片段

@Injectable()

export class Http {

constructor(protected _backend: ConnectionBackend,

protected _defaultOptions: RequestOptions) {}

}

创建 Http 对象


1.创建 XHRBackend 对象


2.创建 RequestOptions 对象


如何创建 XHRBackend 对象

angular2/packages/http/src/http_module.ts


@NgModule({

providers: [

...,

BrowserXhr, // 等价于 {provide: BrowserXhr, useClass: BrowserXhr}

{provide: ResponseOptions, useClass: BaseResponseOptions},

XHRBackend, // 等价于 {provide: XHRBackend, useClass: XHRBackend}

{provide: XSRFStrategy, useFactory: _createDefaultCookieXSRFStrategy},

],

})

export class HttpModule { }

XHRBackend 类


// angular2/packages/http/src/backends/xhr_backend.ts 片段

@Injectable()

export class XHRBackend implements ConnectionBackend {

constructor(

private _browserXHR: BrowserXhr,

private _baseResponseOptions: ResponseOptions,

private _xsrfStrategy: XSRFStrategy) {}

}

ConnectionBackend 抽象类


export abstract class ConnectionBackend {

abstract createConnection(request: any): Connection;  // 用于创建连接

}

(备注:该抽象类中包含了抽象方法,不能直接用于实例化)


Connection 抽象类


export abstract class Connection {

readyState: ReadyState; // 请求状态

request: Request; // 请求对象

response: any; // 响应对象

}

创建 XHRBackend 对象


1.创建 BrowserXhr 对象


2.创建 BaseResponseOptions 对象


3.创建 XSRFStrategy 对象


如何创建 BrowserXhr 对象

angular2/packages/http/src/http_module.ts


@NgModule({

providers: [

...,

BrowserXhr, // 等价于 {provide: BrowserXhr, useClass: BrowserXhr}

],

})

export class HttpModule { }

BrowserXhr 类


@Injectable()

export class BrowserXhr {

constructor() {}

build(): any { return (new XMLHttpRequest()); }

}

如何创建 ResponseOptions 对象

angular2/packages/http/src/http_module.ts


@NgModule({

providers: [

...,

{provide: ResponseOptions, useClass: BaseResponseOptions},

...

],

})

export class HttpModule { }

BaseResponseOptions 类


@Injectable()

export class BaseResponseOptions extends ResponseOptions {

constructor() {

super({status: 200, statusText: 'Ok', type: ResponseType.Default,

headers: new Headers()});

}

}

ResponseOptions 类


export class ResponseOptions {

body: string|Object|ArrayBuffer|Blob; // 响应体的类型

status: number; // 请求的响应状态码

headers: Headers; // 请求头

statusText: string; // 请求的响应状态信息

type: ResponseType; // 响应类型:Basic|Cors|Default|Error|Opaque

url: string; // 响应的URL


constructor({body, status, headers, statusText, type, url}: ResponseOptionsArgs = {}) {

this.body = body != null ? body : null;

this.status = status != null ? status : null;

this.headers = headers != null ? headers : null;

this.statusText = statusText != null ? statusText : null;

this.type = type != null ? type : null;

this.url = url != null ? url : null;

}


// 合并响应参数

merge(options?: ResponseOptionsArgs): ResponseOptions {

return new ResponseOptions({

body: options && options.body != null ? options.body : this.body,

status: options && options.status != null ? options.status : this.status,

headers: options && options.headers != null ? options.headers : this.headers,

statusText: options && options.statusText != null ?

options.statusText : this.statusText,

type: options && options.type != null ? options.type : this.type,

url: options && options.url != null ? options.url : this.url,

});

}

}


// 使用示例

import {ResponseOptions, Response} from '@angular/http';


var options = new ResponseOptions({

body: '{"name":"Jeff"}'

});

var res = new Response(options.merge({

url: 'https://google.com'

}));


console.log('options.url:', options.url); // null

console.log('res.json():', res.json()); // Object {name: "Jeff"}

console.log('res.url:', res.url); // https://google.com

如何创建 XSRFStrategy 对象

angular2/packages/http/src/http_module.ts


@NgModule({

providers: [

...,

{provide: XSRFStrategy, useFactory: _createDefaultCookieXSRFStrategy},

],

})

export class HttpModule { }

_createDefaultCookieXSRFStrategy 函数


// 创建基于Cookie的防止XSRF(Cross Site Request Forgery - 跨域请求伪造)的策略

export function _createDefaultCookieXSRFStrategy() {

return new CookieXSRFStrategy();

}

CookieXSRFStrategy 类


// https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)

export class CookieXSRFStrategy implements XSRFStrategy {

constructor(

private _cookieName: string = 'XSRF-TOKEN',

private _headerName: string = 'X-XSRF-TOKEN') {}


// 配置请求对象

configureRequest(req: Request): void {

// 从Cookie中获取_cookieName对应的xsrfToken值

const xsrfToken = getDOM().getCookie(this._cookieName);

if (xsrfToken) {

// 请求头添加_headerName请求头,key为_headerName,value为xsrfToken

req.headers.set(this._headerName, xsrfToken);

}

}

}

XSRFStrategy 抽象类


export abstract class XSRFStrategy {

abstract configureRequest(req: Request): void;

}

如何创建 RequestOptions 对象

angular2/packages/http/src/http_module.ts


@NgModule({

providers: [

...,

{provide: RequestOptions, useClass: BaseRequestOptions},

...,

],

})

export class HttpModule { }

BaseRequestOptions 类


@Injectable()

export class BaseRequestOptions extends RequestOptions {

constructor() { super({method: RequestMethod.Get, headers: new Headers()}); }

}


// 使用示例

import {BaseRequestOptions, Request, RequestMethod} from '@angular/http';


const options = new BaseRequestOptions();

const req = new Request(options.merge({

method: RequestMethod.Post,

url: 'https://google.com'

}));

console.log('req.method:', RequestMethod[req.method]); // Post

console.log('options.url:', options.url); // null

console.log('req.url:', req.url); // https://google.com

RequestOptions 类







请到「今天看啥」查看全文