repackaging: all the file moves
This commit is contained in:
48
modules/@angular/http/src/backends/browser_jsonp.ts
Normal file
48
modules/@angular/http/src/backends/browser_jsonp.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import {Injectable} from 'angular2/core';
|
||||
import {global} from 'angular2/src/facade/lang';
|
||||
|
||||
let _nextRequestId = 0;
|
||||
export const JSONP_HOME = '__ng_jsonp__';
|
||||
var _jsonpConnections: {[key: string]: any} = null;
|
||||
|
||||
function _getJsonpConnections(): {[key: string]: any} {
|
||||
if (_jsonpConnections === null) {
|
||||
_jsonpConnections = (<{[key: string]: any}>global)[JSONP_HOME] = {};
|
||||
}
|
||||
return _jsonpConnections;
|
||||
}
|
||||
|
||||
// Make sure not to evaluate this in a non-browser environment!
|
||||
@Injectable()
|
||||
export class BrowserJsonp {
|
||||
// Construct a <script> element with the specified URL
|
||||
build(url: string): any {
|
||||
let node = document.createElement('script');
|
||||
node.src = url;
|
||||
return node;
|
||||
}
|
||||
|
||||
nextRequestID(): string { return `__req${_nextRequestId++}`; }
|
||||
|
||||
requestCallback(id: string): string { return `${JSONP_HOME}.${id}.finished`; }
|
||||
|
||||
exposeConnection(id: string, connection: any) {
|
||||
let connections = _getJsonpConnections();
|
||||
connections[id] = connection;
|
||||
}
|
||||
|
||||
removeConnection(id: string) {
|
||||
var connections = _getJsonpConnections();
|
||||
connections[id] = null;
|
||||
}
|
||||
|
||||
// Attach the <script> element to the DOM
|
||||
send(node: any) { document.body.appendChild(<Node>(node)); }
|
||||
|
||||
// Remove <script> element from the DOM
|
||||
cleanup(node: any) {
|
||||
if (node.parentNode) {
|
||||
node.parentNode.removeChild(<Node>(node));
|
||||
}
|
||||
}
|
||||
}
|
12
modules/@angular/http/src/backends/browser_xhr.ts
Normal file
12
modules/@angular/http/src/backends/browser_xhr.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import {Injectable} from 'angular2/core';
|
||||
|
||||
/**
|
||||
* A backend for http that uses the `XMLHttpRequest` browser API.
|
||||
*
|
||||
* Take care not to evaluate this in non-browser contexts.
|
||||
*/
|
||||
@Injectable()
|
||||
export class BrowserXhr {
|
||||
constructor() {}
|
||||
build(): any { return <any>(new XMLHttpRequest()); }
|
||||
}
|
148
modules/@angular/http/src/backends/jsonp_backend.ts
Normal file
148
modules/@angular/http/src/backends/jsonp_backend.ts
Normal file
@ -0,0 +1,148 @@
|
||||
import {ConnectionBackend, Connection} from '../interfaces';
|
||||
import {ReadyState, RequestMethod, ResponseType} from '../enums';
|
||||
import {Request} from '../static_request';
|
||||
import {Response} from '../static_response';
|
||||
import {ResponseOptions, BaseResponseOptions} from '../base_response_options';
|
||||
import {Injectable} from 'angular2/core';
|
||||
import {BrowserJsonp} from './browser_jsonp';
|
||||
import {makeTypeError} from 'angular2/src/facade/exceptions';
|
||||
import {StringWrapper, isPresent} from 'angular2/src/facade/lang';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {Observer} from 'rxjs/Observer';
|
||||
|
||||
const JSONP_ERR_NO_CALLBACK = 'JSONP injected script did not invoke callback.';
|
||||
const JSONP_ERR_WRONG_METHOD = 'JSONP requests must use GET request method.';
|
||||
|
||||
/**
|
||||
* Abstract base class for an in-flight JSONP request.
|
||||
*/
|
||||
export abstract class JSONPConnection implements Connection {
|
||||
/**
|
||||
* The {@link ReadyState} of this request.
|
||||
*/
|
||||
readyState: ReadyState;
|
||||
|
||||
/**
|
||||
* The outgoing HTTP request.
|
||||
*/
|
||||
request: Request;
|
||||
|
||||
/**
|
||||
* An observable that completes with the response, when the request is finished.
|
||||
*/
|
||||
response: Observable<Response>;
|
||||
|
||||
/**
|
||||
* Callback called when the JSONP request completes, to notify the application
|
||||
* of the new data.
|
||||
*/
|
||||
abstract finished(data?: any): void;
|
||||
}
|
||||
|
||||
export class JSONPConnection_ extends JSONPConnection {
|
||||
private _id: string;
|
||||
private _script: Element;
|
||||
private _responseData: any;
|
||||
private _finished: boolean = false;
|
||||
|
||||
constructor(req: Request, private _dom: BrowserJsonp,
|
||||
private baseResponseOptions?: ResponseOptions) {
|
||||
super();
|
||||
if (req.method !== RequestMethod.Get) {
|
||||
throw makeTypeError(JSONP_ERR_WRONG_METHOD);
|
||||
}
|
||||
this.request = req;
|
||||
this.response = new Observable<Response>((responseObserver: Observer<Response>) => {
|
||||
|
||||
this.readyState = ReadyState.Loading;
|
||||
let id = this._id = _dom.nextRequestID();
|
||||
|
||||
_dom.exposeConnection(id, this);
|
||||
|
||||
// Workaround Dart
|
||||
// url = url.replace(/=JSONP_CALLBACK(&|$)/, `generated method`);
|
||||
let callback = _dom.requestCallback(this._id);
|
||||
let url: string = req.url;
|
||||
if (url.indexOf('=JSONP_CALLBACK&') > -1) {
|
||||
url = StringWrapper.replace(url, '=JSONP_CALLBACK&', `=${callback}&`);
|
||||
} else if (url.lastIndexOf('=JSONP_CALLBACK') === url.length - '=JSONP_CALLBACK'.length) {
|
||||
url = url.substring(0, url.length - '=JSONP_CALLBACK'.length) + `=${callback}`;
|
||||
}
|
||||
|
||||
let script = this._script = _dom.build(url);
|
||||
|
||||
let onLoad = (event: Event) => {
|
||||
if (this.readyState === ReadyState.Cancelled) return;
|
||||
this.readyState = ReadyState.Done;
|
||||
_dom.cleanup(script);
|
||||
if (!this._finished) {
|
||||
let responseOptions =
|
||||
new ResponseOptions({body: JSONP_ERR_NO_CALLBACK, type: ResponseType.Error, url});
|
||||
if (isPresent(baseResponseOptions)) {
|
||||
responseOptions = baseResponseOptions.merge(responseOptions);
|
||||
}
|
||||
responseObserver.error(new Response(responseOptions));
|
||||
return;
|
||||
}
|
||||
|
||||
let responseOptions = new ResponseOptions({body: this._responseData, url});
|
||||
if (isPresent(this.baseResponseOptions)) {
|
||||
responseOptions = this.baseResponseOptions.merge(responseOptions);
|
||||
}
|
||||
|
||||
responseObserver.next(new Response(responseOptions));
|
||||
responseObserver.complete();
|
||||
};
|
||||
|
||||
let onError = (error: Error) => {
|
||||
if (this.readyState === ReadyState.Cancelled) return;
|
||||
this.readyState = ReadyState.Done;
|
||||
_dom.cleanup(script);
|
||||
let responseOptions = new ResponseOptions({body: error.message, type: ResponseType.Error});
|
||||
if (isPresent(baseResponseOptions)) {
|
||||
responseOptions = baseResponseOptions.merge(responseOptions);
|
||||
}
|
||||
responseObserver.error(new Response(responseOptions));
|
||||
};
|
||||
|
||||
script.addEventListener('load', onLoad);
|
||||
script.addEventListener('error', onError);
|
||||
|
||||
_dom.send(script);
|
||||
|
||||
return () => {
|
||||
this.readyState = ReadyState.Cancelled;
|
||||
script.removeEventListener('load', onLoad);
|
||||
script.removeEventListener('error', onError);
|
||||
if (isPresent(script)) {
|
||||
this._dom.cleanup(script);
|
||||
}
|
||||
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
finished(data?: any) {
|
||||
// Don't leak connections
|
||||
this._finished = true;
|
||||
this._dom.removeConnection(this._id);
|
||||
if (this.readyState === ReadyState.Cancelled) return;
|
||||
this._responseData = data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link ConnectionBackend} that uses the JSONP strategy of making requests.
|
||||
*/
|
||||
export abstract class JSONPBackend extends ConnectionBackend {}
|
||||
|
||||
@Injectable()
|
||||
export class JSONPBackend_ extends JSONPBackend {
|
||||
constructor(private _browserJSONP: BrowserJsonp, private _baseResponseOptions: ResponseOptions) {
|
||||
super();
|
||||
}
|
||||
|
||||
createConnection(request: Request): JSONPConnection {
|
||||
return new JSONPConnection_(request, this._browserJSONP, this._baseResponseOptions);
|
||||
}
|
||||
}
|
127
modules/@angular/http/src/backends/xhr_backend.ts
Normal file
127
modules/@angular/http/src/backends/xhr_backend.ts
Normal file
@ -0,0 +1,127 @@
|
||||
import {ConnectionBackend, Connection} from '../interfaces';
|
||||
import {ReadyState, RequestMethod, ResponseType} from '../enums';
|
||||
import {Request} from '../static_request';
|
||||
import {Response} from '../static_response';
|
||||
import {Headers} from '../headers';
|
||||
import {ResponseOptions, BaseResponseOptions} from '../base_response_options';
|
||||
import {Injectable} from 'angular2/core';
|
||||
import {BrowserXhr} from './browser_xhr';
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {Observer} from 'rxjs/Observer';
|
||||
import {isSuccess, getResponseURL} from '../http_utils';
|
||||
|
||||
/**
|
||||
* Creates connections using `XMLHttpRequest`. Given a fully-qualified
|
||||
* request, an `XHRConnection` will immediately create an `XMLHttpRequest` object and send the
|
||||
* request.
|
||||
*
|
||||
* This class would typically not be created or interacted with directly inside applications, though
|
||||
* the {@link MockConnection} may be interacted with in tests.
|
||||
*/
|
||||
export class XHRConnection implements Connection {
|
||||
request: Request;
|
||||
/**
|
||||
* Response {@link EventEmitter} which emits a single {@link Response} value on load event of
|
||||
* `XMLHttpRequest`.
|
||||
*/
|
||||
response: Observable<Response>;
|
||||
readyState: ReadyState;
|
||||
constructor(req: Request, browserXHR: BrowserXhr, baseResponseOptions?: ResponseOptions) {
|
||||
this.request = req;
|
||||
this.response = new Observable<Response>((responseObserver: Observer<Response>) => {
|
||||
let _xhr: XMLHttpRequest = browserXHR.build();
|
||||
_xhr.open(RequestMethod[req.method].toUpperCase(), req.url);
|
||||
// load event handler
|
||||
let onLoad = () => {
|
||||
// responseText is the old-school way of retrieving response (supported by IE8 & 9)
|
||||
// response/responseType properties were introduced in XHR Level2 spec (supported by
|
||||
// IE10)
|
||||
let body = isPresent(_xhr.response) ? _xhr.response : _xhr.responseText;
|
||||
|
||||
let headers = Headers.fromResponseHeaderString(_xhr.getAllResponseHeaders());
|
||||
|
||||
let url = getResponseURL(_xhr);
|
||||
|
||||
// normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
|
||||
let status: number = _xhr.status === 1223 ? 204 : _xhr.status;
|
||||
|
||||
// fix status code when it is 0 (0 status is undocumented).
|
||||
// Occurs when accessing file resources or on Android 4.1 stock browser
|
||||
// while retrieving files from application cache.
|
||||
if (status === 0) {
|
||||
status = body ? 200 : 0;
|
||||
}
|
||||
var responseOptions = new ResponseOptions({body, status, headers, url});
|
||||
if (isPresent(baseResponseOptions)) {
|
||||
responseOptions = baseResponseOptions.merge(responseOptions);
|
||||
}
|
||||
let response = new Response(responseOptions);
|
||||
if (isSuccess(status)) {
|
||||
responseObserver.next(response);
|
||||
// TODO(gdi2290): defer complete if array buffer until done
|
||||
responseObserver.complete();
|
||||
return;
|
||||
}
|
||||
responseObserver.error(response);
|
||||
};
|
||||
// error event handler
|
||||
let onError = (err: any) => {
|
||||
var responseOptions = new ResponseOptions({body: err, type: ResponseType.Error});
|
||||
if (isPresent(baseResponseOptions)) {
|
||||
responseOptions = baseResponseOptions.merge(responseOptions);
|
||||
}
|
||||
responseObserver.error(new Response(responseOptions));
|
||||
};
|
||||
|
||||
if (isPresent(req.headers)) {
|
||||
req.headers.forEach((values, name) => _xhr.setRequestHeader(name, values.join(',')));
|
||||
}
|
||||
|
||||
_xhr.addEventListener('load', onLoad);
|
||||
_xhr.addEventListener('error', onError);
|
||||
|
||||
_xhr.send(this.request.text());
|
||||
|
||||
return () => {
|
||||
_xhr.removeEventListener('load', onLoad);
|
||||
_xhr.removeEventListener('error', onError);
|
||||
_xhr.abort();
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link XHRConnection} instances.
|
||||
*
|
||||
* This class would typically not be used by end users, but could be
|
||||
* overridden if a different backend implementation should be used,
|
||||
* such as in a node backend.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* import {Http, MyNodeBackend, HTTP_PROVIDERS, BaseRequestOptions} from 'angular2/http';
|
||||
* @Component({
|
||||
* viewProviders: [
|
||||
* HTTP_PROVIDERS,
|
||||
* provide(Http, {useFactory: (backend, options) => {
|
||||
* return new Http(backend, options);
|
||||
* }, deps: [MyNodeBackend, BaseRequestOptions]})]
|
||||
* })
|
||||
* class MyComponent {
|
||||
* constructor(http:Http) {
|
||||
* http.request('people.json').subscribe(res => this.people = res.json());
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
**/
|
||||
@Injectable()
|
||||
export class XHRBackend implements ConnectionBackend {
|
||||
constructor(private _browserXHR: BrowserXhr, private _baseResponseOptions: ResponseOptions) {}
|
||||
createConnection(request: Request): XHRConnection {
|
||||
return new XHRConnection(request, this._browserXHR, this._baseResponseOptions);
|
||||
}
|
||||
}
|
153
modules/@angular/http/src/base_request_options.ts
Normal file
153
modules/@angular/http/src/base_request_options.ts
Normal file
@ -0,0 +1,153 @@
|
||||
import {isPresent, isString} from 'angular2/src/facade/lang';
|
||||
import {Headers} from './headers';
|
||||
import {RequestMethod} from './enums';
|
||||
import {RequestOptionsArgs} from './interfaces';
|
||||
import {Injectable} from 'angular2/core';
|
||||
import {URLSearchParams} from './url_search_params';
|
||||
import {normalizeMethodName} from './http_utils';
|
||||
|
||||
/**
|
||||
* Creates a request options object to be optionally provided when instantiating a
|
||||
* {@link Request}.
|
||||
*
|
||||
* This class is based on the `RequestInit` description in the [Fetch
|
||||
* Spec](https://fetch.spec.whatwg.org/#requestinit).
|
||||
*
|
||||
* All values are null by default. Typical defaults can be found in the {@link BaseRequestOptions}
|
||||
* class, which sub-classes `RequestOptions`.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/7Wvi3lfLq41aQPKlxB4O?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* import {RequestOptions, Request, RequestMethod} from 'angular2/http';
|
||||
*
|
||||
* var options = new RequestOptions({
|
||||
* method: RequestMethod.Post,
|
||||
* url: 'https://google.com'
|
||||
* });
|
||||
* var req = new Request(options);
|
||||
* console.log('req.method:', RequestMethod[req.method]); // Post
|
||||
* console.log('options.url:', options.url); // https://google.com
|
||||
* ```
|
||||
*/
|
||||
export class RequestOptions {
|
||||
/**
|
||||
* Http method with which to execute a {@link Request}.
|
||||
* Acceptable methods are defined in the {@link RequestMethod} enum.
|
||||
*/
|
||||
method: RequestMethod | string;
|
||||
/**
|
||||
* {@link Headers} to be attached to a {@link Request}.
|
||||
*/
|
||||
headers: Headers;
|
||||
/**
|
||||
* Body to be used when creating a {@link Request}.
|
||||
*/
|
||||
// TODO: support FormData, Blob, URLSearchParams
|
||||
body: string;
|
||||
/**
|
||||
* Url with which to perform a {@link Request}.
|
||||
*/
|
||||
url: string;
|
||||
/**
|
||||
* Search parameters to be included in a {@link Request}.
|
||||
*/
|
||||
search: URLSearchParams;
|
||||
constructor({method, headers, body, url, search}: RequestOptionsArgs = {}) {
|
||||
this.method = isPresent(method) ? normalizeMethodName(method) : null;
|
||||
this.headers = isPresent(headers) ? headers : null;
|
||||
this.body = isPresent(body) ? body : null;
|
||||
this.url = isPresent(url) ? url : null;
|
||||
this.search = isPresent(search) ? (isString(search) ? new URLSearchParams(<string>(search)) :
|
||||
<URLSearchParams>(search)) :
|
||||
null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of the `RequestOptions` instance, using the optional input as values to override
|
||||
* existing values. This method will not change the values of the instance on which it is being
|
||||
* called.
|
||||
*
|
||||
* Note that `headers` and `search` will override existing values completely if present in
|
||||
* the `options` object. If these values should be merged, it should be done prior to calling
|
||||
* `merge` on the `RequestOptions` instance.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/6w8XA8YTkDRcPYpdB9dk?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* import {RequestOptions, Request, RequestMethod} from 'angular2/http';
|
||||
*
|
||||
* var options = new RequestOptions({
|
||||
* method: RequestMethod.Post
|
||||
* });
|
||||
* var req = new Request(options.merge({
|
||||
* 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
|
||||
* ```
|
||||
*/
|
||||
merge(options?: RequestOptionsArgs): RequestOptions {
|
||||
return new RequestOptions({
|
||||
method: isPresent(options) && isPresent(options.method) ? options.method : this.method,
|
||||
headers: isPresent(options) && isPresent(options.headers) ? options.headers : this.headers,
|
||||
body: isPresent(options) && isPresent(options.body) ? options.body : this.body,
|
||||
url: isPresent(options) && isPresent(options.url) ? options.url : this.url,
|
||||
search: isPresent(options) && isPresent(options.search) ?
|
||||
(isString(options.search) ? new URLSearchParams(<string>(options.search)) :
|
||||
(<URLSearchParams>(options.search)).clone()) :
|
||||
this.search
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Subclass of {@link RequestOptions}, with default values.
|
||||
*
|
||||
* Default values:
|
||||
* * method: {@link RequestMethod RequestMethod.Get}
|
||||
* * headers: empty {@link Headers} object
|
||||
*
|
||||
* This class could be extended and bound to the {@link RequestOptions} class
|
||||
* when configuring an {@link Injector}, in order to override the default options
|
||||
* used by {@link Http} to create and send {@link Request Requests}.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/LEKVSx?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* import {provide} from 'angular2/core';
|
||||
* import {bootstrap} from 'angular2/platform/browser';
|
||||
* import {HTTP_PROVIDERS, Http, BaseRequestOptions, RequestOptions} from 'angular2/http';
|
||||
* import {App} from './myapp';
|
||||
*
|
||||
* class MyOptions extends BaseRequestOptions {
|
||||
* search: string = 'coreTeam=true';
|
||||
* }
|
||||
*
|
||||
* bootstrap(App, [HTTP_PROVIDERS, provide(RequestOptions, {useClass: MyOptions})]);
|
||||
* ```
|
||||
*
|
||||
* The options could also be extended when manually creating a {@link Request}
|
||||
* object.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/oyBoEvNtDhOSfi9YxaVb?p=preview))
|
||||
*
|
||||
* ```
|
||||
* import {BaseRequestOptions, Request, RequestMethod} from 'angular2/http';
|
||||
*
|
||||
* var options = new BaseRequestOptions();
|
||||
* var 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
|
||||
* ```
|
||||
*/
|
||||
@Injectable()
|
||||
export class BaseRequestOptions extends RequestOptions {
|
||||
constructor() { super({method: RequestMethod.Get, headers: new Headers()}); }
|
||||
}
|
153
modules/@angular/http/src/base_response_options.ts
Normal file
153
modules/@angular/http/src/base_response_options.ts
Normal file
@ -0,0 +1,153 @@
|
||||
import {Injectable} from 'angular2/core';
|
||||
import {isPresent, isJsObject} from 'angular2/src/facade/lang';
|
||||
import {Headers} from './headers';
|
||||
import {ResponseType} from './enums';
|
||||
import {ResponseOptionsArgs} from './interfaces';
|
||||
|
||||
/**
|
||||
* Creates a response options object to be optionally provided when instantiating a
|
||||
* {@link Response}.
|
||||
*
|
||||
* This class is based on the `ResponseInit` description in the [Fetch
|
||||
* Spec](https://fetch.spec.whatwg.org/#responseinit).
|
||||
*
|
||||
* All values are null by default. Typical defaults can be found in the
|
||||
* {@link BaseResponseOptions} class, which sub-classes `ResponseOptions`.
|
||||
*
|
||||
* This class may be used in tests to build {@link Response Responses} for
|
||||
* mock responses (see {@link MockBackend}).
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/P9Jkk8e8cz6NVzbcxEsD?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* import {ResponseOptions, Response} from 'angular2/http';
|
||||
*
|
||||
* var options = new ResponseOptions({
|
||||
* body: '{"name":"Jeff"}'
|
||||
* });
|
||||
* var res = new Response(options);
|
||||
*
|
||||
* console.log('res.json():', res.json()); // Object {name: "Jeff"}
|
||||
* ```
|
||||
*/
|
||||
export class ResponseOptions {
|
||||
// TODO: ArrayBuffer | FormData | Blob
|
||||
/**
|
||||
* String or Object representing the body of the {@link Response}.
|
||||
*/
|
||||
body: string | Object;
|
||||
/**
|
||||
* Http {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html status code}
|
||||
* associated with the response.
|
||||
*/
|
||||
status: number;
|
||||
/**
|
||||
* Response {@link Headers headers}
|
||||
*/
|
||||
headers: Headers;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
statusText: string;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
type: ResponseType;
|
||||
url: string;
|
||||
constructor({body, status, headers, statusText, type, url}: ResponseOptionsArgs = {}) {
|
||||
this.body = isPresent(body) ? body : null;
|
||||
this.status = isPresent(status) ? status : null;
|
||||
this.headers = isPresent(headers) ? headers : null;
|
||||
this.statusText = isPresent(statusText) ? statusText : null;
|
||||
this.type = isPresent(type) ? type : null;
|
||||
this.url = isPresent(url) ? url : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of the `ResponseOptions` instance, using the optional input as values to
|
||||
* override
|
||||
* existing values. This method will not change the values of the instance on which it is being
|
||||
* called.
|
||||
*
|
||||
* This may be useful when sharing a base `ResponseOptions` object inside tests,
|
||||
* where certain properties may change from test to test.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/1lXquqFfgduTFBWjNoRE?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* import {ResponseOptions, Response} from 'angular2/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
|
||||
* ```
|
||||
*/
|
||||
merge(options?: ResponseOptionsArgs): ResponseOptions {
|
||||
return new ResponseOptions({
|
||||
body: isPresent(options) && isPresent(options.body) ? options.body : this.body,
|
||||
status: isPresent(options) && isPresent(options.status) ? options.status : this.status,
|
||||
headers: isPresent(options) && isPresent(options.headers) ? options.headers : this.headers,
|
||||
statusText: isPresent(options) && isPresent(options.statusText) ? options.statusText :
|
||||
this.statusText,
|
||||
type: isPresent(options) && isPresent(options.type) ? options.type : this.type,
|
||||
url: isPresent(options) && isPresent(options.url) ? options.url : this.url,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclass of {@link ResponseOptions}, with default values.
|
||||
*
|
||||
* Default values:
|
||||
* * status: 200
|
||||
* * headers: empty {@link Headers} object
|
||||
*
|
||||
* This class could be extended and bound to the {@link ResponseOptions} class
|
||||
* when configuring an {@link Injector}, in order to override the default options
|
||||
* used by {@link Http} to create {@link Response Responses}.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/qv8DLT?p=preview))
|
||||
*
|
||||
* ```typescript
|
||||
* import {provide} from 'angular2/core';
|
||||
* import {bootstrap} from 'angular2/platform/browser';
|
||||
* import {HTTP_PROVIDERS, Headers, Http, BaseResponseOptions, ResponseOptions} from
|
||||
* 'angular2/http';
|
||||
* import {App} from './myapp';
|
||||
*
|
||||
* class MyOptions extends BaseResponseOptions {
|
||||
* headers:Headers = new Headers({network: 'github'});
|
||||
* }
|
||||
*
|
||||
* bootstrap(App, [HTTP_PROVIDERS, provide(ResponseOptions, {useClass: MyOptions})]);
|
||||
* ```
|
||||
*
|
||||
* The options could also be extended when manually creating a {@link Response}
|
||||
* object.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/VngosOWiaExEtbstDoix?p=preview))
|
||||
*
|
||||
* ```
|
||||
* import {BaseResponseOptions, Response} from 'angular2/http';
|
||||
*
|
||||
* var options = new BaseResponseOptions();
|
||||
* var res = new Response(options.merge({
|
||||
* body: 'Angular2',
|
||||
* headers: new Headers({framework: 'angular'})
|
||||
* }));
|
||||
* console.log('res.headers.get("framework"):', res.headers.get('framework')); // angular
|
||||
* console.log('res.text():', res.text()); // Angular2;
|
||||
* ```
|
||||
*/
|
||||
@Injectable()
|
||||
export class BaseResponseOptions extends ResponseOptions {
|
||||
constructor() {
|
||||
super({status: 200, statusText: 'Ok', type: ResponseType.Default, headers: new Headers()});
|
||||
}
|
||||
}
|
40
modules/@angular/http/src/enums.ts
Normal file
40
modules/@angular/http/src/enums.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
/**
|
||||
* Supported http methods.
|
||||
*/
|
||||
export enum RequestMethod {
|
||||
Get,
|
||||
Post,
|
||||
Put,
|
||||
Delete,
|
||||
Options,
|
||||
Head,
|
||||
Patch
|
||||
}
|
||||
|
||||
/**
|
||||
* All possible states in which a connection can be, based on
|
||||
* [States](http://www.w3.org/TR/XMLHttpRequest/#states) from the `XMLHttpRequest` spec, but with an
|
||||
* additional "CANCELLED" state.
|
||||
*/
|
||||
export enum ReadyState {
|
||||
Unsent,
|
||||
Open,
|
||||
HeadersReceived,
|
||||
Loading,
|
||||
Done,
|
||||
Cancelled
|
||||
}
|
||||
|
||||
/**
|
||||
* Acceptable response types to be associated with a {@link Response}, based on
|
||||
* [ResponseType](https://fetch.spec.whatwg.org/#responsetype) from the Fetch spec.
|
||||
*/
|
||||
export enum ResponseType {
|
||||
Basic,
|
||||
Cors,
|
||||
Default,
|
||||
Error,
|
||||
Opaque
|
||||
}
|
159
modules/@angular/http/src/headers.ts
Normal file
159
modules/@angular/http/src/headers.ts
Normal file
@ -0,0 +1,159 @@
|
||||
import {
|
||||
isPresent,
|
||||
isBlank,
|
||||
isJsObject,
|
||||
isType,
|
||||
StringWrapper,
|
||||
Json
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
import {
|
||||
isListLikeIterable,
|
||||
iterateListLike,
|
||||
Map,
|
||||
MapWrapper,
|
||||
StringMapWrapper,
|
||||
ListWrapper,
|
||||
} from 'angular2/src/facade/collection';
|
||||
|
||||
/**
|
||||
* Polyfill for [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers/Headers), as
|
||||
* specified in the [Fetch Spec](https://fetch.spec.whatwg.org/#headers-class).
|
||||
*
|
||||
* The only known difference between this `Headers` implementation and the spec is the
|
||||
* lack of an `entries` method.
|
||||
*
|
||||
* ### Example ([live demo](http://plnkr.co/edit/MTdwT6?p=preview))
|
||||
*
|
||||
* ```
|
||||
* import {Headers} from 'angular2/http';
|
||||
*
|
||||
* var firstHeaders = new Headers();
|
||||
* firstHeaders.append('Content-Type', 'image/jpeg');
|
||||
* console.log(firstHeaders.get('Content-Type')) //'image/jpeg'
|
||||
*
|
||||
* // Create headers from Plain Old JavaScript Object
|
||||
* var secondHeaders = new Headers({
|
||||
* 'X-My-Custom-Header': 'Angular'
|
||||
* });
|
||||
* console.log(secondHeaders.get('X-My-Custom-Header')); //'Angular'
|
||||
*
|
||||
* var thirdHeaders = new Headers(secondHeaders);
|
||||
* console.log(thirdHeaders.get('X-My-Custom-Header')); //'Angular'
|
||||
* ```
|
||||
*/
|
||||
export class Headers {
|
||||
/** @internal */
|
||||
_headersMap: Map<string, string[]>;
|
||||
constructor(headers?: Headers | {[key: string]: any}) {
|
||||
if (headers instanceof Headers) {
|
||||
this._headersMap = (<Headers>headers)._headersMap;
|
||||
return;
|
||||
}
|
||||
|
||||
this._headersMap = new Map<string, string[]>();
|
||||
|
||||
if (isBlank(headers)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// headers instanceof StringMap
|
||||
StringMapWrapper.forEach(headers, (v: any, k: string) => {
|
||||
this._headersMap.set(k, isListLikeIterable(v) ? v : [v]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Headers instance from the given DOMString of Response Headers
|
||||
*/
|
||||
static fromResponseHeaderString(headersString: string): Headers {
|
||||
return headersString.trim()
|
||||
.split('\n')
|
||||
.map(val => val.split(':'))
|
||||
.map(([key, ...parts]) => ([key.trim(), parts.join(':').trim()]))
|
||||
.reduce((headers, [key, value]) => !headers.set(key, value) && headers, new Headers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a header to existing list of header values for a given header name.
|
||||
*/
|
||||
append(name: string, value: string): void {
|
||||
var mapName = this._headersMap.get(name);
|
||||
var list = isListLikeIterable(mapName) ? mapName : [];
|
||||
list.push(value);
|
||||
this._headersMap.set(name, list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all header values for the given name.
|
||||
*/
|
||||
delete (name: string): void { this._headersMap.delete(name); }
|
||||
|
||||
forEach(fn: (values: string[], name: string, headers: Map<string, string[]>) => void): void {
|
||||
this._headersMap.forEach(fn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns first header that matches given name.
|
||||
*/
|
||||
get(header: string): string { return ListWrapper.first(this._headersMap.get(header)); }
|
||||
|
||||
/**
|
||||
* Check for existence of header by given name.
|
||||
*/
|
||||
has(header: string): boolean { return this._headersMap.has(header); }
|
||||
|
||||
/**
|
||||
* Provides names of set headers
|
||||
*/
|
||||
keys(): string[] { return MapWrapper.keys(this._headersMap); }
|
||||
|
||||
/**
|
||||
* Sets or overrides header value for given name.
|
||||
*/
|
||||
set(header: string, value: string | string[]): void {
|
||||
var list: string[] = [];
|
||||
|
||||
if (isListLikeIterable(value)) {
|
||||
var pushValue = (<string[]>value).join(',');
|
||||
list.push(pushValue);
|
||||
} else {
|
||||
list.push(<string>value);
|
||||
}
|
||||
|
||||
this._headersMap.set(header, list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns values of all headers.
|
||||
*/
|
||||
values(): string[][] { return MapWrapper.values(this._headersMap); }
|
||||
|
||||
/**
|
||||
* Returns string of all headers.
|
||||
*/
|
||||
toJSON(): {[key: string]: any} {
|
||||
let serializableHeaders = {};
|
||||
this._headersMap.forEach((values: string[], name: string) => {
|
||||
let list = [];
|
||||
|
||||
iterateListLike(values, val => list = ListWrapper.concat(list, val.split(',')));
|
||||
|
||||
serializableHeaders[name] = list;
|
||||
});
|
||||
return serializableHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of header values for a given name.
|
||||
*/
|
||||
getAll(header: string): string[] {
|
||||
var headers = this._headersMap.get(header);
|
||||
return isListLikeIterable(headers) ? headers : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is not implemented.
|
||||
*/
|
||||
entries() { throw new BaseException('"entries" method is not implemented on Headers class'); }
|
||||
}
|
200
modules/@angular/http/src/http.ts
Normal file
200
modules/@angular/http/src/http.ts
Normal file
@ -0,0 +1,200 @@
|
||||
import {isString, isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {makeTypeError} from 'angular2/src/facade/exceptions';
|
||||
import {Injectable} from 'angular2/core';
|
||||
import {RequestOptionsArgs, Connection, ConnectionBackend} from './interfaces';
|
||||
import {Request} from './static_request';
|
||||
import {Response} from './static_response';
|
||||
import {BaseRequestOptions, RequestOptions} from './base_request_options';
|
||||
import {RequestMethod} from './enums';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
|
||||
function httpRequest(backend: ConnectionBackend, request: Request): Observable<Response> {
|
||||
return backend.createConnection(request).response;
|
||||
}
|
||||
|
||||
function mergeOptions(defaultOpts: BaseRequestOptions, providedOpts: RequestOptionsArgs,
|
||||
method: RequestMethod, url: string): RequestOptions {
|
||||
var newOptions = defaultOpts;
|
||||
if (isPresent(providedOpts)) {
|
||||
// Hack so Dart can used named parameters
|
||||
return newOptions.merge(new RequestOptions({
|
||||
method: providedOpts.method || method,
|
||||
url: providedOpts.url || url,
|
||||
search: providedOpts.search,
|
||||
headers: providedOpts.headers,
|
||||
body: providedOpts.body
|
||||
}));
|
||||
}
|
||||
if (isPresent(method)) {
|
||||
return newOptions.merge(new RequestOptions({method: method, url: url}));
|
||||
} else {
|
||||
return newOptions.merge(new RequestOptions({url: url}));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs http requests using `XMLHttpRequest` as the default backend.
|
||||
*
|
||||
* `Http` is available as an injectable class, with methods to perform http requests. Calling
|
||||
* `request` returns an `Observable` which will emit a single {@link Response} when a
|
||||
* response is received.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```typescript
|
||||
* import {Http, HTTP_PROVIDERS} from 'angular2/http';
|
||||
* @Component({
|
||||
* selector: 'http-app',
|
||||
* viewProviders: [HTTP_PROVIDERS],
|
||||
* templateUrl: 'people.html'
|
||||
* })
|
||||
* class PeopleComponent {
|
||||
* constructor(http: Http) {
|
||||
* http.get('people.json')
|
||||
* // Call map on the response observable to get the parsed people object
|
||||
* .map(res => res.json())
|
||||
* // Subscribe to the observable to get the parsed people object and attach it to the
|
||||
* // component
|
||||
* .subscribe(people => this.people = people);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* http.get('people.json').observer({next: (value) => this.people = value});
|
||||
* ```
|
||||
*
|
||||
* The default construct used to perform requests, `XMLHttpRequest`, is abstracted as a "Backend" (
|
||||
* {@link XHRBackend} in this case), which could be mocked with dependency injection by replacing
|
||||
* the {@link XHRBackend} provider, as in the following example:
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```typescript
|
||||
* import {BaseRequestOptions, Http} from 'angular2/http';
|
||||
* import {MockBackend} from 'angular2/http/testing';
|
||||
* var injector = Injector.resolveAndCreate([
|
||||
* BaseRequestOptions,
|
||||
* MockBackend,
|
||||
* provide(Http, {useFactory:
|
||||
* function(backend, defaultOptions) {
|
||||
* return new Http(backend, defaultOptions);
|
||||
* },
|
||||
* deps: [MockBackend, BaseRequestOptions]})
|
||||
* ]);
|
||||
* var http = injector.get(Http);
|
||||
* http.get('request-from-mock-backend.json').subscribe((res:Response) => doSomething(res));
|
||||
* ```
|
||||
*
|
||||
**/
|
||||
@Injectable()
|
||||
export class Http {
|
||||
constructor(protected _backend: ConnectionBackend, protected _defaultOptions: RequestOptions) {}
|
||||
|
||||
/**
|
||||
* Performs any type of http request. First argument is required, and can either be a url or
|
||||
* a {@link Request} instance. If the first argument is a url, an optional {@link RequestOptions}
|
||||
* object can be provided as the 2nd argument. The options object will be merged with the values
|
||||
* of {@link BaseRequestOptions} before performing the request.
|
||||
*/
|
||||
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
|
||||
var responseObservable: any;
|
||||
if (isString(url)) {
|
||||
responseObservable = httpRequest(
|
||||
this._backend,
|
||||
new Request(mergeOptions(this._defaultOptions, options, RequestMethod.Get, <string>url)));
|
||||
} else if (url instanceof Request) {
|
||||
responseObservable = httpRequest(this._backend, url);
|
||||
} else {
|
||||
throw makeTypeError('First argument must be a url string or Request instance.');
|
||||
}
|
||||
return responseObservable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a request with `get` http method.
|
||||
*/
|
||||
get(url: string, options?: RequestOptionsArgs): Observable<Response> {
|
||||
return httpRequest(this._backend, new Request(mergeOptions(this._defaultOptions, options,
|
||||
RequestMethod.Get, url)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a request with `post` http method.
|
||||
*/
|
||||
post(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> {
|
||||
return httpRequest(
|
||||
this._backend,
|
||||
new Request(mergeOptions(this._defaultOptions.merge(new RequestOptions({body: body})),
|
||||
options, RequestMethod.Post, url)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a request with `put` http method.
|
||||
*/
|
||||
put(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> {
|
||||
return httpRequest(
|
||||
this._backend,
|
||||
new Request(mergeOptions(this._defaultOptions.merge(new RequestOptions({body: body})),
|
||||
options, RequestMethod.Put, url)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a request with `delete` http method.
|
||||
*/
|
||||
delete (url: string, options?: RequestOptionsArgs): Observable<Response> {
|
||||
return httpRequest(this._backend, new Request(mergeOptions(this._defaultOptions, options,
|
||||
RequestMethod.Delete, url)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a request with `patch` http method.
|
||||
*/
|
||||
patch(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> {
|
||||
return httpRequest(
|
||||
this._backend,
|
||||
new Request(mergeOptions(this._defaultOptions.merge(new RequestOptions({body: body})),
|
||||
options, RequestMethod.Patch, url)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a request with `head` http method.
|
||||
*/
|
||||
head(url: string, options?: RequestOptionsArgs): Observable<Response> {
|
||||
return httpRequest(this._backend, new Request(mergeOptions(this._defaultOptions, options,
|
||||
RequestMethod.Head, url)));
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class Jsonp extends Http {
|
||||
constructor(backend: ConnectionBackend, defaultOptions: RequestOptions) {
|
||||
super(backend, defaultOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs any type of http request. First argument is required, and can either be a url or
|
||||
* a {@link Request} instance. If the first argument is a url, an optional {@link RequestOptions}
|
||||
* object can be provided as the 2nd argument. The options object will be merged with the values
|
||||
* of {@link BaseRequestOptions} before performing the request.
|
||||
*/
|
||||
request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
|
||||
var responseObservable: any;
|
||||
if (isString(url)) {
|
||||
url =
|
||||
new Request(mergeOptions(this._defaultOptions, options, RequestMethod.Get, <string>url));
|
||||
}
|
||||
if (url instanceof Request) {
|
||||
if (url.method !== RequestMethod.Get) {
|
||||
makeTypeError('JSONP requests must use GET request method.');
|
||||
}
|
||||
responseObservable = httpRequest(this._backend, url);
|
||||
} else {
|
||||
throw makeTypeError('First argument must be a url string or Request instance.');
|
||||
}
|
||||
return responseObservable;
|
||||
}
|
||||
}
|
32
modules/@angular/http/src/http_utils.ts
Normal file
32
modules/@angular/http/src/http_utils.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import {isString} from 'angular2/src/facade/lang';
|
||||
import {RequestMethod} from './enums';
|
||||
import {makeTypeError} from 'angular2/src/facade/exceptions';
|
||||
import {Response} from './static_response';
|
||||
|
||||
export function normalizeMethodName(method: string | RequestMethod): RequestMethod {
|
||||
if (isString(method)) {
|
||||
var originalMethod = method;
|
||||
method = (<string>method)
|
||||
.replace(/(\w)(\w*)/g, (g0: string, g1: string, g2: string) =>
|
||||
g1.toUpperCase() + g2.toLowerCase());
|
||||
method = <number>(<{[key: string]: any}>RequestMethod)[method];
|
||||
if (typeof method !== 'number')
|
||||
throw makeTypeError(
|
||||
`Invalid request method. The method "${originalMethod}" is not supported.`);
|
||||
}
|
||||
return <RequestMethod>method;
|
||||
}
|
||||
|
||||
export const isSuccess = (status: number): boolean => (status >= 200 && status < 300);
|
||||
|
||||
export function getResponseURL(xhr: any): string {
|
||||
if ('responseURL' in xhr) {
|
||||
return xhr.responseURL;
|
||||
}
|
||||
if (/^X-Request-URL:/m.test(xhr.getAllResponseHeaders())) {
|
||||
return xhr.getResponseHeader('X-Request-URL');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
export {isJsObject} from 'angular2/src/facade/lang';
|
13
modules/@angular/http/src/index.ts
Normal file
13
modules/@angular/http/src/index.ts
Normal file
@ -0,0 +1,13 @@
|
||||
// Index to be used if Http is ever configured as a standalone npm package.
|
||||
// require('reflect-metadata');
|
||||
// require('es6-shim');
|
||||
// import {HTTP_PROVIDERS, JSONP_PROVIDERS, Http, Jsonp} from './http';
|
||||
// import {Injector} from 'angular2/core';
|
||||
// export * from './http';
|
||||
|
||||
// /**
|
||||
// * TODO(jeffbcross): export each as their own top-level file, to require as:
|
||||
// * require('angular2/http'); require('http/jsonp');
|
||||
// */
|
||||
// export var http = Injector.resolveAndCreate([HTTP_PROVIDERS]).get(Http);
|
||||
// export var jsonp = Injector.resolveAndCreate([JSONP_PROVIDERS]).get(Jsonp);
|
55
modules/@angular/http/src/interfaces.ts
Normal file
55
modules/@angular/http/src/interfaces.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import {ReadyState, RequestMethod, ResponseType} from './enums';
|
||||
import {Headers} from './headers';
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
import {EventEmitter} from 'angular2/src/facade/async';
|
||||
import {Request} from './static_request';
|
||||
import {URLSearchParams} from './url_search_params';
|
||||
|
||||
/**
|
||||
* Abstract class from which real backends are derived.
|
||||
*
|
||||
* The primary purpose of a `ConnectionBackend` is to create new connections to fulfill a given
|
||||
* {@link Request}.
|
||||
*/
|
||||
export abstract class ConnectionBackend { abstract createConnection(request: any): Connection; }
|
||||
|
||||
/**
|
||||
* Abstract class from which real connections are derived.
|
||||
*/
|
||||
export abstract class Connection {
|
||||
readyState: ReadyState;
|
||||
request: Request;
|
||||
response: any; // TODO: generic of <Response>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for options to construct a RequestOptions, based on
|
||||
* [RequestInit](https://fetch.spec.whatwg.org/#requestinit) from the Fetch spec.
|
||||
*/
|
||||
export interface RequestOptionsArgs {
|
||||
url?: string;
|
||||
method?: string | RequestMethod;
|
||||
search?: string | URLSearchParams;
|
||||
headers?: Headers;
|
||||
// TODO: Support Blob, ArrayBuffer, JSON, URLSearchParams, FormData
|
||||
body?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Required structure when constructing new Request();
|
||||
*/
|
||||
export interface RequestArgs extends RequestOptionsArgs { url: string; }
|
||||
|
||||
/**
|
||||
* Interface for options to construct a Response, based on
|
||||
* [ResponseInit](https://fetch.spec.whatwg.org/#responseinit) from the Fetch spec.
|
||||
*/
|
||||
export type ResponseOptionsArgs = {
|
||||
// TODO: Support Blob, ArrayBuffer, JSON
|
||||
body?: string | Object | FormData;
|
||||
status?: number;
|
||||
statusText?: string;
|
||||
headers?: Headers;
|
||||
type?: ResponseType;
|
||||
url?: string;
|
||||
}
|
15
modules/@angular/http/src/package.json
Normal file
15
modules/@angular/http/src/package.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "ngHttp",
|
||||
"version": "<%= packageJson.version %>",
|
||||
"description": "Http module for Angular 2",
|
||||
"homepage": "<%= packageJson.homepage %>",
|
||||
"bugs": "<%= packageJson.bugs %>",
|
||||
"contributors": <%= JSON.stringify(packageJson.contributors) %>,
|
||||
"license": "<%= packageJson.license %>",
|
||||
"repository": <%= JSON.stringify(packageJson.repository) %>,
|
||||
"devDependencies": <%= JSON.stringify(packageJson.defaultDevDependencies) %>,
|
||||
"peerDependencies": {
|
||||
"angular2": "<%= packageJson.version %>",
|
||||
"rxjs": "<%= packageJson.dependencies['rxjs'] %>"
|
||||
}
|
||||
}
|
88
modules/@angular/http/src/static_request.ts
Normal file
88
modules/@angular/http/src/static_request.ts
Normal file
@ -0,0 +1,88 @@
|
||||
import {RequestMethod} from './enums';
|
||||
import {RequestArgs} from './interfaces';
|
||||
import {Headers} from './headers';
|
||||
import {normalizeMethodName} from './http_utils';
|
||||
import {RegExpWrapper, isPresent, isJsObject, StringWrapper} from 'angular2/src/facade/lang';
|
||||
|
||||
// TODO(jeffbcross): properly implement body accessors
|
||||
/**
|
||||
* Creates `Request` instances from provided values.
|
||||
*
|
||||
* The Request's interface is inspired by the Request constructor defined in the [Fetch
|
||||
* Spec](https://fetch.spec.whatwg.org/#request-class),
|
||||
* but is considered a static value whose body can be accessed many times. There are other
|
||||
* differences in the implementation, but this is the most significant.
|
||||
*
|
||||
* `Request` instances are typically created by higher-level classes, like {@link Http} and
|
||||
* {@link Jsonp}, but it may occasionally be useful to explicitly create `Request` instances.
|
||||
* One such example is when creating services that wrap higher-level services, like {@link Http},
|
||||
* where it may be useful to generate a `Request` with arbitrary headers and search params.
|
||||
*
|
||||
* ```typescript
|
||||
* import {Injectable, Injector} from 'angular2/core';
|
||||
* import {HTTP_PROVIDERS, Http, Request, RequestMethod} from 'angular2/http';
|
||||
*
|
||||
* @Injectable()
|
||||
* class AutoAuthenticator {
|
||||
* constructor(public http:Http) {}
|
||||
* request(url:string) {
|
||||
* return this.http.request(new Request({
|
||||
* method: RequestMethod.Get,
|
||||
* url: url,
|
||||
* search: 'password=123'
|
||||
* }));
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* var injector = Injector.resolveAndCreate([HTTP_PROVIDERS, AutoAuthenticator]);
|
||||
* var authenticator = injector.get(AutoAuthenticator);
|
||||
* authenticator.request('people.json').subscribe(res => {
|
||||
* //URL should have included '?password=123'
|
||||
* console.log('people', res.json());
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export class Request {
|
||||
/**
|
||||
* Http method with which to perform the request.
|
||||
*/
|
||||
method: RequestMethod;
|
||||
/**
|
||||
* {@link Headers} instance
|
||||
*/
|
||||
headers: Headers;
|
||||
/** Url of the remote resource */
|
||||
url: string;
|
||||
// TODO: support URLSearchParams | FormData | Blob | ArrayBuffer
|
||||
private _body: string;
|
||||
constructor(requestOptions: RequestArgs) {
|
||||
// TODO: assert that url is present
|
||||
let url = requestOptions.url;
|
||||
this.url = requestOptions.url;
|
||||
if (isPresent(requestOptions.search)) {
|
||||
let search = requestOptions.search.toString();
|
||||
if (search.length > 0) {
|
||||
let prefix = '?';
|
||||
if (StringWrapper.contains(this.url, '?')) {
|
||||
prefix = (this.url[this.url.length - 1] == '&') ? '' : '&';
|
||||
}
|
||||
// TODO: just delete search-query-looking string in url?
|
||||
this.url = url + prefix + search;
|
||||
}
|
||||
}
|
||||
this._body = requestOptions.body;
|
||||
this.method = normalizeMethodName(requestOptions.method);
|
||||
// TODO(jeffbcross): implement behavior
|
||||
// Defaults to 'omit', consistent with browser
|
||||
// TODO(jeffbcross): implement behavior
|
||||
this.headers = new Headers(requestOptions.headers);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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() : ''; }
|
||||
}
|
117
modules/@angular/http/src/static_response.ts
Normal file
117
modules/@angular/http/src/static_response.ts
Normal file
@ -0,0 +1,117 @@
|
||||
import {ResponseType} from './enums';
|
||||
import {isString, isPresent, Json} from 'angular2/src/facade/lang';
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
import {Headers} from './headers';
|
||||
import {ResponseOptions} from './base_response_options';
|
||||
import {isJsObject} from './http_utils';
|
||||
|
||||
/**
|
||||
* Creates `Response` instances from provided values.
|
||||
*
|
||||
* Though this object isn't
|
||||
* usually instantiated by end-users, it is the primary object interacted with when it comes time to
|
||||
* add data to a view.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* http.request('my-friends.txt').subscribe(response => this.friends = response.text());
|
||||
* ```
|
||||
*
|
||||
* The Response's interface is inspired by the Response constructor defined in the [Fetch
|
||||
* Spec](https://fetch.spec.whatwg.org/#response-class), but is considered a static value whose body
|
||||
* can be accessed many times. There are other differences in the implementation, but this is the
|
||||
* most significant.
|
||||
*/
|
||||
export class Response {
|
||||
/**
|
||||
* One of "basic", "cors", "default", "error, or "opaque".
|
||||
*
|
||||
* Defaults to "default".
|
||||
*/
|
||||
type: ResponseType;
|
||||
/**
|
||||
* True if the response's status is within 200-299
|
||||
*/
|
||||
ok: boolean;
|
||||
/**
|
||||
* URL of response.
|
||||
*
|
||||
* Defaults to empty string.
|
||||
*/
|
||||
url: string;
|
||||
/**
|
||||
* Status code returned by server.
|
||||
*
|
||||
* Defaults to 200.
|
||||
*/
|
||||
status: number;
|
||||
/**
|
||||
* Text representing the corresponding reason phrase to the `status`, as defined in [ietf rfc 2616
|
||||
* section 6.1.1](https://tools.ietf.org/html/rfc2616#section-6.1.1)
|
||||
*
|
||||
* Defaults to "OK"
|
||||
*/
|
||||
statusText: string;
|
||||
/**
|
||||
* Non-standard property
|
||||
*
|
||||
* Denotes how many of the response body's bytes have been loaded, for example if the response is
|
||||
* the result of a progress event.
|
||||
*/
|
||||
bytesLoaded: number;
|
||||
/**
|
||||
* Non-standard property
|
||||
*
|
||||
* Denotes how many bytes are expected in the final response body.
|
||||
*/
|
||||
totalBytes: number;
|
||||
/**
|
||||
* Headers object based on the `Headers` class in the [Fetch
|
||||
* Spec](https://fetch.spec.whatwg.org/#headers-class).
|
||||
*/
|
||||
headers: Headers;
|
||||
// TODO: Support ArrayBuffer, JSON, FormData, Blob
|
||||
private _body: string | Object;
|
||||
constructor(responseOptions: ResponseOptions) {
|
||||
this._body = responseOptions.body;
|
||||
this.status = responseOptions.status;
|
||||
this.ok = (this.status >= 200 && this.status <= 299);
|
||||
this.statusText = responseOptions.statusText;
|
||||
this.headers = responseOptions.headers;
|
||||
this.type = responseOptions.type;
|
||||
this.url = responseOptions.url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Not yet implemented
|
||||
*/
|
||||
// TODO: Blob return type
|
||||
blob(): any { throw new BaseException('"blob()" method not implemented on Response superclass'); }
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
return jsonResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the body as a string, presuming `toString()` can be called on the response body.
|
||||
*/
|
||||
text(): string { return this._body.toString(); }
|
||||
|
||||
/**
|
||||
* Not yet implemented
|
||||
*/
|
||||
// TODO: ArrayBuffer return type
|
||||
arrayBuffer(): any {
|
||||
throw new BaseException('"arrayBuffer()" method not implemented on Response superclass');
|
||||
}
|
||||
}
|
130
modules/@angular/http/src/url_search_params.ts
Normal file
130
modules/@angular/http/src/url_search_params.ts
Normal file
@ -0,0 +1,130 @@
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {Map, MapWrapper, ListWrapper, isListLikeIterable} from 'angular2/src/facade/collection';
|
||||
|
||||
function paramParser(rawParams: string = ''): Map<string, string[]> {
|
||||
var map = new Map<string, string[]>();
|
||||
if (rawParams.length > 0) {
|
||||
var params: string[] = rawParams.split('&');
|
||||
params.forEach((param: string) => {
|
||||
var split: string[] = param.split('=');
|
||||
var key = split[0];
|
||||
var val = split[1];
|
||||
var list = isPresent(map.get(key)) ? map.get(key) : [];
|
||||
list.push(val);
|
||||
map.set(key, list);
|
||||
});
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map-like representation of url search parameters, based on
|
||||
* [URLSearchParams](https://url.spec.whatwg.org/#urlsearchparams) in the url living standard,
|
||||
* with several extensions for merging URLSearchParams objects:
|
||||
* - setAll()
|
||||
* - appendAll()
|
||||
* - replaceAll()
|
||||
*/
|
||||
export class URLSearchParams {
|
||||
paramsMap: Map<string, string[]>;
|
||||
constructor(public rawParams: string = '') { this.paramsMap = paramParser(rawParams); }
|
||||
|
||||
clone(): URLSearchParams {
|
||||
var clone = new URLSearchParams();
|
||||
clone.appendAll(this);
|
||||
return clone;
|
||||
}
|
||||
|
||||
has(param: string): boolean { return this.paramsMap.has(param); }
|
||||
|
||||
get(param: string): string {
|
||||
var storedParam = this.paramsMap.get(param);
|
||||
if (isListLikeIterable(storedParam)) {
|
||||
return ListWrapper.first(storedParam);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
getAll(param: string): string[] {
|
||||
var mapParam = this.paramsMap.get(param);
|
||||
return isPresent(mapParam) ? mapParam : [];
|
||||
}
|
||||
|
||||
set(param: string, val: string) {
|
||||
var mapParam = this.paramsMap.get(param);
|
||||
var list = isPresent(mapParam) ? mapParam : [];
|
||||
ListWrapper.clear(list);
|
||||
list.push(val);
|
||||
this.paramsMap.set(param, list);
|
||||
}
|
||||
|
||||
// A merge operation
|
||||
// For each name-values pair in `searchParams`, perform `set(name, values[0])`
|
||||
//
|
||||
// E.g: "a=[1,2,3], c=[8]" + "a=[4,5,6], b=[7]" = "a=[4], c=[8], b=[7]"
|
||||
//
|
||||
// TODO(@caitp): document this better
|
||||
setAll(searchParams: URLSearchParams) {
|
||||
searchParams.paramsMap.forEach((value, param) => {
|
||||
var mapParam = this.paramsMap.get(param);
|
||||
var list = isPresent(mapParam) ? mapParam : [];
|
||||
ListWrapper.clear(list);
|
||||
list.push(value[0]);
|
||||
this.paramsMap.set(param, list);
|
||||
});
|
||||
}
|
||||
|
||||
append(param: string, val: string): void {
|
||||
var mapParam = this.paramsMap.get(param);
|
||||
var list = isPresent(mapParam) ? mapParam : [];
|
||||
list.push(val);
|
||||
this.paramsMap.set(param, list);
|
||||
}
|
||||
|
||||
// A merge operation
|
||||
// For each name-values pair in `searchParams`, perform `append(name, value)`
|
||||
// for each value in `values`.
|
||||
//
|
||||
// E.g: "a=[1,2], c=[8]" + "a=[3,4], b=[7]" = "a=[1,2,3,4], c=[8], b=[7]"
|
||||
//
|
||||
// TODO(@caitp): document this better
|
||||
appendAll(searchParams: URLSearchParams) {
|
||||
searchParams.paramsMap.forEach((value, param) => {
|
||||
var mapParam = this.paramsMap.get(param);
|
||||
var list = isPresent(mapParam) ? mapParam : [];
|
||||
for (var i = 0; i < value.length; ++i) {
|
||||
list.push(value[i]);
|
||||
}
|
||||
this.paramsMap.set(param, list);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// A merge operation
|
||||
// For each name-values pair in `searchParams`, perform `delete(name)`,
|
||||
// followed by `set(name, values)`
|
||||
//
|
||||
// E.g: "a=[1,2,3], c=[8]" + "a=[4,5,6], b=[7]" = "a=[4,5,6], c=[8], b=[7]"
|
||||
//
|
||||
// TODO(@caitp): document this better
|
||||
replaceAll(searchParams: URLSearchParams) {
|
||||
searchParams.paramsMap.forEach((value, param) => {
|
||||
var mapParam = this.paramsMap.get(param);
|
||||
var list = isPresent(mapParam) ? mapParam : [];
|
||||
ListWrapper.clear(list);
|
||||
for (var i = 0; i < value.length; ++i) {
|
||||
list.push(value[i]);
|
||||
}
|
||||
this.paramsMap.set(param, list);
|
||||
});
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
var paramsList: string[] = [];
|
||||
this.paramsMap.forEach((values, k) => { values.forEach(v => paramsList.push(k + '=' + v)); });
|
||||
return paramsList.join('&');
|
||||
}
|
||||
|
||||
delete (param: string): void { this.paramsMap.delete(param); }
|
||||
}
|
Reference in New Issue
Block a user