refactor(http): share 'body' logic between Request and Response

This commit is contained in:
Damien Cassan 2016-05-27 00:47:20 +02:00 committed by Jeff Cross
parent 1266460386
commit e7a8e2757b
14 changed files with 211 additions and 211 deletions

View File

@ -28,7 +28,7 @@ export {JSONPBackend, JSONPConnection} from './src/backends/jsonp_backend';
export {CookieXSRFStrategy, XHRBackend, XHRConnection} from './src/backends/xhr_backend'; export {CookieXSRFStrategy, XHRBackend, XHRConnection} from './src/backends/xhr_backend';
export {BaseRequestOptions, RequestOptions} from './src/base_request_options'; export {BaseRequestOptions, RequestOptions} from './src/base_request_options';
export {BaseResponseOptions, ResponseOptions} from './src/base_response_options'; export {BaseResponseOptions, ResponseOptions} from './src/base_response_options';
export {ReadyState, RequestMethod, ResponseBuffer, ResponseType} from './src/enums'; export {ReadyState, RequestMethod, ResponseContentType, ResponseType} from './src/enums';
export {Headers} from './src/headers'; export {Headers} from './src/headers';
export {Http, Jsonp} from './src/http'; export {Http, Jsonp} from './src/http';
export {Connection, ConnectionBackend, RequestOptionsArgs, ResponseOptionsArgs, XSRFStrategy} from './src/interfaces'; export {Connection, ConnectionBackend, RequestOptionsArgs, ResponseOptionsArgs, XSRFStrategy} from './src/interfaces';

View File

@ -12,7 +12,7 @@ import {Observable} from 'rxjs/Observable';
import {Observer} from 'rxjs/Observer'; import {Observer} from 'rxjs/Observer';
import {ResponseOptions} from '../base_response_options'; import {ResponseOptions} from '../base_response_options';
import {ContentType, ReadyState, RequestMethod, ResponseType, ResponseBuffer} from '../enums'; import {ContentType, ReadyState, RequestMethod, ResponseContentType, ResponseType} from '../enums';
import {isPresent, isString} from '../facade/lang'; import {isPresent, isString} from '../facade/lang';
import {Headers} from '../headers'; import {Headers} from '../headers';
import {getResponseURL, isSuccess} from '../http_utils'; import {getResponseURL, isSuccess} from '../http_utils';
@ -24,7 +24,6 @@ import {BrowserXhr} from './browser_xhr';
const XSSI_PREFIX = /^\)\]\}',?\n/; const XSSI_PREFIX = /^\)\]\}',?\n/;
/** /**
* Creates connections using `XMLHttpRequest`. Given a fully-qualified * Creates connections using `XMLHttpRequest`. Given a fully-qualified
* request, an `XHRConnection` will immediately create an `XMLHttpRequest` object and send the * request, an `XHRConnection` will immediately create an `XMLHttpRequest` object and send the
@ -52,7 +51,7 @@ export class XHRConnection implements Connection {
_xhr.withCredentials = req.withCredentials; _xhr.withCredentials = req.withCredentials;
} }
// load event handler // load event handler
let onLoad = () => { let onLoad = () => {
// responseText is the old-school way of retrieving response (supported by IE8 & 9) // responseText is the old-school way of retrieving response (supported by IE8 & 9)
// response/responseType properties were introduced in XHR Level2 spec (supported by // response/responseType properties were introduced in XHR Level2 spec (supported by
// IE10) // IE10)
@ -110,18 +109,21 @@ export class XHRConnection implements Connection {
} }
// Select the correct buffer type to store the response // Select the correct buffer type to store the response
if (isPresent(req.buffer) && isPresent(_xhr.responseType)) switch (req.buffer) { if (isPresent(req.responseType) && isPresent(_xhr.responseType)) {
case ResponseBuffer.ArrayBuffer: switch (req.responseType) {
_xhr.responseType = "arraybuffer"; case ResponseContentType.ArrayBuffer:
_xhr.responseType = 'arraybuffer';
break; break;
case ResponseBuffer.Json: case ResponseContentType.Json:
_xhr.responseType = "json"; _xhr.responseType = 'json';
break;
case ResponseContentType.Text:
_xhr.responseType = 'text';
break; break;
default: default:
case ResponseBuffer.Text: throw new Error('The selected responseType is not supported');
_xhr.responseType = "text";
break;
} }
}
_xhr.addEventListener('load', onLoad); _xhr.addEventListener('load', onLoad);
_xhr.addEventListener('error', onError); _xhr.addEventListener('error', onError);

View File

@ -10,7 +10,7 @@ import {Injectable} from '@angular/core';
import {isPresent, isString} from '../src/facade/lang'; import {isPresent, isString} from '../src/facade/lang';
import {RequestMethod, ResponseBuffer} from './enums'; import {RequestMethod, ResponseContentType} from './enums';
import {Headers} from './headers'; import {Headers} from './headers';
import {normalizeMethodName} from './http_utils'; import {normalizeMethodName} from './http_utils';
import {RequestOptionsArgs} from './interfaces'; import {RequestOptionsArgs} from './interfaces';
@ -72,10 +72,11 @@ export class RequestOptions {
/* /*
* Select a buffer to store the response, such as ArrayBuffer, Blob, Json (or Document) * Select a buffer to store the response, such as ArrayBuffer, Blob, Json (or Document)
*/ */
buffer: ResponseBuffer; responseType: ResponseContentType;
constructor({method, headers, body, url, search, withCredentials, buffer}: RequestOptionsArgs = {}) {
constructor(
{method, headers, body, url, search, withCredentials,
responseType}: RequestOptionsArgs = {}) {
this.method = isPresent(method) ? normalizeMethodName(method) : null; this.method = isPresent(method) ? normalizeMethodName(method) : null;
this.headers = isPresent(headers) ? headers : null; this.headers = isPresent(headers) ? headers : null;
this.body = isPresent(body) ? body : null; this.body = isPresent(body) ? body : null;
@ -84,7 +85,7 @@ export class RequestOptions {
(isString(search) ? new URLSearchParams(<string>(search)) : <URLSearchParams>(search)) : (isString(search) ? new URLSearchParams(<string>(search)) : <URLSearchParams>(search)) :
null; null;
this.withCredentials = isPresent(withCredentials) ? withCredentials : null; this.withCredentials = isPresent(withCredentials) ? withCredentials : null;
this.buffer = isPresent(buffer) ? buffer : null; this.responseType = isPresent(responseType) ? responseType : null;
} }
/** /**
@ -124,8 +125,9 @@ export class RequestOptions {
this.search, this.search,
withCredentials: isPresent(options) && isPresent(options.withCredentials) ? withCredentials: isPresent(options) && isPresent(options.withCredentials) ?
options.withCredentials : options.withCredentials :
this.withCredentials, this.withCredentials,
buffer: isPresent(options) && isPresent(options.buffer) ? options.buffer : this.buffer responseType: isPresent(options) && isPresent(options.responseType) ? options.responseType :
this.responseType
}); });
} }
} }

View File

@ -48,7 +48,7 @@ export class ResponseOptions {
/** /**
* String, Object, ArrayBuffer representing the body of the {@link Response}. * String, Object, ArrayBuffer representing the body of the {@link Response}.
*/ */
body: string | Object | ArrayBuffer; body: string|Object|ArrayBuffer;
/** /**
* Http {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html status code} * Http {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html status code}
* associated with the response. * associated with the response.

View File

@ -0,0 +1,84 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Json, isString} from '../src/facade/lang';
import {isJsObject, stringToArrayBuffer} from './http_utils';
import {URLSearchParams} from './url_search_params';
/**
* HTTP request body used by both {@link Request} and {@link Response}
* https://fetch.spec.whatwg.org/#body
*/
export abstract class Body {
/**
* @internal
*/
protected _body: any;
/**
* Attempts to return body as parsed `JSON` object, or raises an exception.
*/
json(): any {
if (isString(this._body)) {
return Json.parse(<string>this._body);
}
if (this._body instanceof ArrayBuffer) {
return Json.parse(this.text());
}
return this._body;
}
/**
* Returns the body as a string, presuming `toString()` can be called on the response body.
*/
text(): string {
if (this._body instanceof URLSearchParams) {
return this._body.toString();
}
if (this._body instanceof ArrayBuffer) {
return String.fromCharCode.apply(null, new Uint16Array(<ArrayBuffer>this._body));
}
if (isJsObject(this._body)) {
return Json.stringify(this._body);
}
return this._body.toString();
}
/**
* Return the body as an ArrayBuffer
*/
arrayBuffer(): ArrayBuffer {
if (this._body instanceof ArrayBuffer) {
return <ArrayBuffer>this._body;
}
return stringToArrayBuffer(this.text());
}
/**
* Returns the request's body as a Blob, assuming that body exists.
*/
blob(): Blob {
if (this._body instanceof Blob) {
return <Blob>this._body;
}
if (this._body instanceof ArrayBuffer) {
return new Blob([this._body]);
}
throw new Error('The request body isn\'t either a blob or an array buffer');
}
}

View File

@ -62,10 +62,11 @@ export enum ContentType {
ARRAY_BUFFER ARRAY_BUFFER
} }
/* /**
* Define which buffer to use to store the response * Define which buffer to use to store the response
* @experimental
*/ */
export enum ResponseBuffer { export enum ResponseContentType {
ArrayBuffer, ArrayBuffer,
Json, Json,
Text Text

View File

@ -36,7 +36,7 @@ function mergeOptions(
headers: providedOpts.headers, headers: providedOpts.headers,
body: providedOpts.body, body: providedOpts.body,
withCredentials: providedOpts.withCredentials, withCredentials: providedOpts.withCredentials,
buffer: providedOpts.buffer responseType: providedOpts.responseType
})); }));
} }
if (isPresent(method)) { if (isPresent(method)) {

View File

@ -38,10 +38,10 @@ export function getResponseURL(xhr: any): string {
return; return;
} }
export function stringToArrayBuffer(string: String): ArrayBuffer { export function stringToArrayBuffer(input: String): ArrayBuffer {
let view = new Uint16Array(string.length); let view = new Uint16Array(input.length);
for (var i = 0, strLen = string.length; i < strLen; i++) { for (var i = 0, strLen = input.length; i < strLen; i++) {
view[i] = string.charCodeAt(i); view[i] = input.charCodeAt(i);
} }
return view.buffer; return view.buffer;
} }

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {ReadyState, RequestMethod, ResponseType, ResponseBuffer} from './enums'; import {ReadyState, RequestMethod, ResponseContentType, ResponseType} from './enums';
import {Headers} from './headers'; import {Headers} from './headers';
import {Request} from './static_request'; import {Request} from './static_request';
import {URLSearchParams} from './url_search_params'; import {URLSearchParams} from './url_search_params';
@ -52,7 +52,7 @@ export interface RequestOptionsArgs {
headers?: Headers; headers?: Headers;
body?: any; body?: any;
withCredentials?: boolean; withCredentials?: boolean;
buffer?: ResponseBuffer; responseType?: ResponseContentType;
} }
/** /**
@ -68,7 +68,8 @@ export interface RequestArgs extends RequestOptionsArgs { url: string; }
*/ */
export type ResponseOptionsArgs = { export type ResponseOptionsArgs = {
// TODO: Support Blob, JSON // TODO: Support Blob, JSON
body?: string | Object | FormData | ArrayBuffer; status?: number; statusText?: string; headers?: Headers; body?: string | Object | FormData | ArrayBuffer; status?: number; statusText?: string;
headers?: Headers;
type?: ResponseType; type?: ResponseType;
url?: string; url?: string;
} }

View File

@ -8,7 +8,8 @@
import {StringWrapper, isPresent} from '../src/facade/lang'; import {StringWrapper, isPresent} from '../src/facade/lang';
import {ContentType, RequestMethod, ResponseBuffer} from './enums'; import {Body} from './body';
import {ContentType, RequestMethod, ResponseContentType} from './enums';
import {Headers} from './headers'; import {Headers} from './headers';
import {normalizeMethodName} from './http_utils'; import {normalizeMethodName} from './http_utils';
import {RequestArgs} from './interfaces'; import {RequestArgs} from './interfaces';
@ -55,7 +56,7 @@ import {URLSearchParams} from './url_search_params';
* *
* @experimental * @experimental
*/ */
export class Request { export class Request extends Body {
/** /**
* Http method with which to perform the request. * Http method with which to perform the request.
*/ */
@ -66,15 +67,14 @@ export class Request {
headers: Headers; headers: Headers;
/** Url of the remote resource */ /** Url of the remote resource */
url: string; url: string;
/** Body of the request **/
private _body: any;
/** Type of the request body **/ /** Type of the request body **/
private contentType: ContentType; private contentType: ContentType;
/** Enable use credentials */ /** Enable use credentials */
withCredentials: boolean; withCredentials: boolean;
/* Select a buffer to store the response */ /** Buffer to store the response */
buffer: ResponseBuffer; responseType: ResponseContentType;
constructor(requestOptions: RequestArgs) { constructor(requestOptions: RequestArgs) {
super();
// TODO: assert that url is present // TODO: assert that url is present
let url = requestOptions.url; let url = requestOptions.url;
this.url = requestOptions.url; this.url = requestOptions.url;
@ -97,43 +97,7 @@ export class Request {
// TODO(jeffbcross): implement behavior // TODO(jeffbcross): implement behavior
this.headers = new Headers(requestOptions.headers); this.headers = new Headers(requestOptions.headers);
this.withCredentials = requestOptions.withCredentials; this.withCredentials = requestOptions.withCredentials;
this.buffer = requestOptions.buffer; this.responseType = requestOptions.responseType;
}
/**
* Returns the request's body as string, assuming that body exists. If body is undefined, return
* empty
* string.
*/
text(): string { return isPresent(this._body) ? this._body.toString() : ''; }
/**
* Returns the request's body as JSON string, assuming that body exists. If body is undefined,
* return
* empty
* string.
*/
json(): string { return isPresent(this._body) ? JSON.stringify(this._body) : ''; }
/**
* Returns the request's body as array buffer, assuming that body exists. If body is undefined,
* return
* null.
*/
arrayBuffer(): ArrayBuffer {
if (this._body instanceof ArrayBuffer) return <ArrayBuffer>this._body;
throw 'The request body isn\'t an array buffer';
}
/**
* Returns the request's body as blob, assuming that body exists. If body is undefined, return
* null.
*/
blob(): Blob {
if (this._body instanceof Blob) return <Blob>this._body;
if (this._body instanceof ArrayBuffer) return new Blob([this._body]);
throw 'The request body isn\'t either a blob or an array buffer';
} }
/** /**

View File

@ -10,9 +10,10 @@ import {BaseException} from '../src/facade/exceptions';
import {Json, isString} from '../src/facade/lang'; import {Json, isString} from '../src/facade/lang';
import {ResponseOptions} from './base_response_options'; import {ResponseOptions} from './base_response_options';
import {Body} from './body';
import {ResponseType} from './enums'; import {ResponseType} from './enums';
import {Headers} from './headers'; import {Headers} from './headers';
import {isJsObject, stringToArrayBuffer} from './http_utils'; import {isJsObject} from './http_utils';
/** /**
@ -35,7 +36,7 @@ import {isJsObject, stringToArrayBuffer} from './http_utils';
* *
* @experimental * @experimental
*/ */
export class Response { export class Response extends Body {
/** /**
* One of "basic", "cors", "default", "error, or "opaque". * One of "basic", "cors", "default", "error, or "opaque".
* *
@ -84,10 +85,8 @@ export class Response {
*/ */
headers: Headers; headers: Headers;
// TODO: Support FormData, Blob
private _body: string | Object | ArrayBuffer;
constructor(responseOptions: ResponseOptions) { constructor(responseOptions: ResponseOptions) {
super();
this._body = responseOptions.body; this._body = responseOptions.body;
this.status = responseOptions.status; this.status = responseOptions.status;
this.ok = (this.status >= 200 && this.status <= 299); this.ok = (this.status >= 200 && this.status <= 299);
@ -97,60 +96,7 @@ export class Response {
this.url = responseOptions.url; this.url = responseOptions.url;
} }
/**
* Attempts to return body as parsed `JSON` object, or raises an exception.
*/
json(): any {
var jsonResponse: string|Object;
if (isJsObject(this._body)) {
jsonResponse = this._body;
} else if (isString(this._body)) {
jsonResponse = Json.parse(<string>this._body);
} else if (this._body instanceof ArrayBuffer) {
jsonResponse = Json.parse(this.text());
} else {
jsonResponse = this._body;
}
return jsonResponse;
}
/**
* Returns the body as a string, presuming `toString()` can be called on the response body.
*/
text(): string {
var textResponse: string;
if (this._body instanceof ArrayBuffer) {
textResponse = String.fromCharCode.apply(null, new Uint16Array(<ArrayBuffer>this._body));
} else if (isJsObject(this._body)) {
textResponse = Json.stringify(this._body);
} else {
textResponse = this._body.toString();
}
return textResponse;
}
/**
* Return the body as an ArrayBuffer
*/
arrayBuffer(): ArrayBuffer {
var bufferResponse: ArrayBuffer;
if (this._body instanceof ArrayBuffer) {
bufferResponse = <ArrayBuffer>this._body;
} else {
bufferResponse = stringToArrayBuffer(this.text());
}
return bufferResponse;
}
toString(): string { toString(): string {
return `Response with status: ${this.status} ${this.statusText} for URL: ${this.url}`; return `Response with status: ${this.status} ${this.statusText} for URL: ${this.url}`;
} }
/**
* Not yet implemented
*/
// TODO: Blob return type
blob(): any { throw new BaseException('"blob()" method not implemented on Response superclass'); }
} }

View File

@ -256,7 +256,7 @@ export function main() {
var connection = new XHRConnection( var connection = new XHRConnection(
new Request(base.merge(new RequestOptions({body: body}))), new MockBrowserXHR()); new Request(base.merge(new RequestOptions({body: body}))), new MockBrowserXHR());
connection.response.subscribe(); connection.response.subscribe();
expect(sendSpy).toHaveBeenCalledWith(JSON.stringify(body)); expect(sendSpy).toHaveBeenCalledWith(body);
expect(setRequestHeaderSpy).toHaveBeenCalledWith('Content-Type', 'application/json'); expect(setRequestHeaderSpy).toHaveBeenCalledWith('Content-Type', 'application/json');
}); });

View File

@ -10,12 +10,12 @@ import {Injector, ReflectiveInjector, provide} from '@angular/core';
import {afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; import {afterEach, beforeEach, ddescribe, describe, expect, iit, inject, it, xit} from '@angular/core/testing/testing_internal';
import {AsyncTestCompleter} from '@angular/core/testing/testing_internal'; import {AsyncTestCompleter} from '@angular/core/testing/testing_internal';
import {Observable} from 'rxjs/Observable'; import {Observable} from 'rxjs/Observable';
import 'rxjs/add/observable/zip';
import {Subject} from 'rxjs/Subject'; import {Subject} from 'rxjs/Subject';
import {stringToArrayBuffer} from "../src/http_utils"; import {zip} from 'rxjs/observable/zip';
import {Json} from "../src/facade/lang";
import {BaseRequestOptions, ConnectionBackend, HTTP_PROVIDERS, Http, JSONPBackend, JSONP_PROVIDERS, Jsonp, Request, RequestMethod, RequestOptions, Response, ResponseBuffer, ResponseOptions, URLSearchParams, XHRBackend} from '../http'; import {BaseRequestOptions, ConnectionBackend, HTTP_PROVIDERS, Http, JSONPBackend, JSONP_PROVIDERS, Jsonp, Request, RequestMethod, RequestOptions, Response, ResponseContentType, ResponseOptions, URLSearchParams, XHRBackend} from '../http';
import {Json} from '../src/facade/lang';
import {stringToArrayBuffer} from '../src/http_utils';
import {MockBackend, MockConnection} from '../testing/mock_backend'; import {MockBackend, MockConnection} from '../testing/mock_backend';
export function main() { export function main() {
@ -388,116 +388,114 @@ export function main() {
it('should attach the provided buffer to the response', it('should attach the provided buffer to the response',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
backend.connections.subscribe((c: MockConnection) => { backend.connections.subscribe((c: MockConnection) => {
expect(c.request.buffer).toBe(ResponseBuffer.ArrayBuffer); expect(c.request.responseType).toBe(ResponseContentType.ArrayBuffer);
c.mockRespond(new Response(new ResponseOptions({body: new ArrayBuffer(32)}))); c.mockRespond(new Response(new ResponseOptions({body: new ArrayBuffer(32)})));
async.done(); async.done();
}); });
http.get('https://www.google.com', http.get(
new RequestOptions({buffer: ResponseBuffer.ArrayBuffer})) 'https://www.google.com',
new RequestOptions({responseType: ResponseContentType.ArrayBuffer}))
.subscribe((res: Response) => {}); .subscribe((res: Response) => {});
})); }));
it('should be able to consume a buffer containing a String as any response type', it('should be able to consume a buffer containing a String as any response type',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
backend.connections.subscribe((c: MockConnection) => c.mockRespond(baseResponse)); backend.connections.subscribe((c: MockConnection) => c.mockRespond(baseResponse));
http.get('https://www.google.com') http.get('https://www.google.com').subscribe((res: Response) => {
.subscribe((res: Response) => { expect(res.arrayBuffer()).toBeAnInstanceOf(ArrayBuffer);
expect(res.arrayBuffer()).toBeAnInstanceOf(ArrayBuffer); expect(res.text()).toBe('base response');
expect(res.text()).toBe("base response"); async.done();
async.done(); });
});
})); }));
it('should be able to consume a buffer containing an ArrayBuffer as any response type', it('should be able to consume a buffer containing an ArrayBuffer as any response type',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
let arrayBuffer = stringToArrayBuffer('{"response": "ok"}'); let arrayBuffer = stringToArrayBuffer('{"response": "ok"}');
backend.connections.subscribe((c: MockConnection) => c.mockRespond(new Response( backend.connections.subscribe(
new ResponseOptions({body: arrayBuffer})))); (c: MockConnection) =>
http.get('https://www.google.com') c.mockRespond(new Response(new ResponseOptions({body: arrayBuffer}))));
.subscribe((res: Response) => { http.get('https://www.google.com').subscribe((res: Response) => {
expect(res.arrayBuffer()).toBe(arrayBuffer); expect(res.arrayBuffer()).toBe(arrayBuffer);
expect(res.text()).toEqual('{"response": "ok"}'); expect(res.text()).toEqual('{"response": "ok"}');
expect(res.json()).toEqual({response: "ok"}); expect(res.json()).toEqual({response: 'ok'});
async.done(); async.done();
}); });
})); }));
it('should be able to consume a buffer containing an Object as any response type', it('should be able to consume a buffer containing an Object as any response type',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
let simpleObject = {"content": "ok"}; let simpleObject = {'content': 'ok'};
backend.connections.subscribe((c: MockConnection) => c.mockRespond(new Response( backend.connections.subscribe(
new ResponseOptions({body: simpleObject})))); (c: MockConnection) =>
http.get('https://www.google.com') c.mockRespond(new Response(new ResponseOptions({body: simpleObject}))));
.subscribe((res: Response) => { http.get('https://www.google.com').subscribe((res: Response) => {
expect(res.arrayBuffer()).toBeAnInstanceOf(ArrayBuffer); expect(res.arrayBuffer()).toBeAnInstanceOf(ArrayBuffer);
expect(res.text()).toEqual(Json.stringify(simpleObject)); expect(res.text()).toEqual(Json.stringify(simpleObject));
expect(res.json()).toBe(simpleObject); expect(res.json()).toBe(simpleObject);
async.done(); async.done();
}); });
})); }));
it('should preserve encoding of ArrayBuffer response', it('should preserve encoding of ArrayBuffer response',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
let message = "é@θЂ"; let message = 'é@θЂ';
let arrayBuffer = stringToArrayBuffer(message); let arrayBuffer = stringToArrayBuffer(message);
backend.connections.subscribe((c: MockConnection) => c.mockRespond(new Response( backend.connections.subscribe(
new ResponseOptions({body: arrayBuffer})))); (c: MockConnection) =>
http.get('https://www.google.com') c.mockRespond(new Response(new ResponseOptions({body: arrayBuffer}))));
.subscribe((res: Response) => { http.get('https://www.google.com').subscribe((res: Response) => {
expect(res.arrayBuffer()).toBeAnInstanceOf(ArrayBuffer); expect(res.arrayBuffer()).toBeAnInstanceOf(ArrayBuffer);
expect(res.text()).toEqual(message); expect(res.text()).toEqual(message);
async.done(); async.done();
}); });
})); }));
it('should preserve encoding of String response', it('should preserve encoding of String response',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
let message = "é@θЂ"; let message = 'é@θЂ';
backend.connections.subscribe((c: MockConnection) => c.mockRespond( backend.connections.subscribe(
new Response(new ResponseOptions({body: message})))); (c: MockConnection) =>
http.get('https://www.google.com') c.mockRespond(new Response(new ResponseOptions({body: message}))));
.subscribe((res: Response) => { http.get('https://www.google.com').subscribe((res: Response) => {
expect(res.arrayBuffer()).toEqual(stringToArrayBuffer(message)); expect(res.arrayBuffer()).toEqual(stringToArrayBuffer(message));
async.done(); async.done();
}); });
})); }));
it('should have an equivalent response independently of the buffer used', it('should have an equivalent response independently of the buffer used',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
let message = {"param": "content"}; let message = {'param': 'content'};
backend.connections.subscribe((c: MockConnection) => { backend.connections.subscribe((c: MockConnection) => {
let body = (): any => { let body = (): any => {
switch (c.request.buffer) { switch (c.request.responseType) {
case ResponseBuffer.Text: case ResponseContentType.Text:
return Json.stringify(message); return Json.stringify(message);
case ResponseBuffer.Json: case ResponseContentType.Json:
return message; return message;
case ResponseBuffer.ArrayBuffer: case ResponseContentType.ArrayBuffer:
return stringToArrayBuffer(Json.stringify(message)); return stringToArrayBuffer(Json.stringify(message));
} }
}; };
c.mockRespond(new Response(new ResponseOptions({body: body()}))) c.mockRespond(new Response(new ResponseOptions({body: body()})))
}); });
Observable.zip(http.get('https://www.google.com', zip(http.get(
new RequestOptions({buffer: ResponseBuffer.Text})), 'https://www.google.com',
http.get('https://www.google.com', new RequestOptions({responseType: ResponseContentType.Text})),
new RequestOptions({buffer: ResponseBuffer.Json})), http.get(
http.get('https://www.google.com', 'https://www.google.com',
new RequestOptions({buffer: ResponseBuffer.ArrayBuffer})), new RequestOptions({responseType: ResponseContentType.Json})),
(x, y, z) => [x, y, z]) http.get(
.subscribe((res: Array<Response>) => { 'https://www.google.com',
new RequestOptions({responseType: ResponseContentType.ArrayBuffer})))
.subscribe((res: Array<any>) => {
expect(res[0].text()).toEqual(res[1].text()); expect(res[0].text()).toEqual(res[1].text());
expect(res[1].text()).toEqual(res[2].text()); expect(res[1].text()).toEqual(res[2].text());
async.done(); async.done();
}); });
})); }));
}); });
}); });
} }

View File

@ -117,18 +117,15 @@ export declare enum ReadyState {
} }
/** @experimental */ /** @experimental */
export declare class Request { export declare class Request extends Body {
headers: Headers; headers: Headers;
method: RequestMethod; method: RequestMethod;
responseType: ResponseContentType;
url: string; url: string;
withCredentials: boolean; withCredentials: boolean;
constructor(requestOptions: RequestArgs); constructor(requestOptions: RequestArgs);
arrayBuffer(): ArrayBuffer;
blob(): Blob;
detectContentType(): ContentType; detectContentType(): ContentType;
getBody(): any; getBody(): any;
json(): string;
text(): string;
} }
/** @experimental */ /** @experimental */
@ -147,10 +144,11 @@ export declare class RequestOptions {
body: any; body: any;
headers: Headers; headers: Headers;
method: RequestMethod | string; method: RequestMethod | string;
responseType: ResponseContentType;
search: URLSearchParams; search: URLSearchParams;
url: string; url: string;
withCredentials: boolean; withCredentials: boolean;
constructor({method, headers, body, url, search, withCredentials}?: RequestOptionsArgs); constructor({method, headers, body, url, search, withCredentials, responseType}?: RequestOptionsArgs);
merge(options?: RequestOptionsArgs): RequestOptions; merge(options?: RequestOptionsArgs): RequestOptions;
} }
@ -159,13 +157,14 @@ export interface RequestOptionsArgs {
body?: any; body?: any;
headers?: Headers; headers?: Headers;
method?: string | RequestMethod; method?: string | RequestMethod;
responseType?: ResponseContentType;
search?: string | URLSearchParams; search?: string | URLSearchParams;
url?: string; url?: string;
withCredentials?: boolean; withCredentials?: boolean;
} }
/** @experimental */ /** @experimental */
export declare class Response { export declare class Response extends Body {
bytesLoaded: number; bytesLoaded: number;
headers: Headers; headers: Headers;
ok: boolean; ok: boolean;
@ -175,16 +174,19 @@ export declare class Response {
type: ResponseType; type: ResponseType;
url: string; url: string;
constructor(responseOptions: ResponseOptions); constructor(responseOptions: ResponseOptions);
arrayBuffer(): any;
blob(): any;
json(): any;
text(): string;
toString(): string; toString(): string;
} }
/** @experimental */
export declare enum ResponseContentType {
ArrayBuffer = 0,
Json = 1,
Text = 2,
}
/** @experimental */ /** @experimental */
export declare class ResponseOptions { export declare class ResponseOptions {
body: string | Object; body: string | Object | ArrayBuffer;
headers: Headers; headers: Headers;
status: number; status: number;
url: string; url: string;
@ -194,7 +196,7 @@ export declare class ResponseOptions {
/** @experimental */ /** @experimental */
export declare type ResponseOptionsArgs = { export declare type ResponseOptionsArgs = {
body?: string | Object | FormData; body?: string | Object | FormData | ArrayBuffer;
status?: number; status?: number;
statusText?: string; statusText?: string;
headers?: Headers; headers?: Headers;