BREAKING CHANGE:
The Http module previously would return RxJS Observables from method calls
of the Http class. In order to support Dart, the module was refactored to
return the EventEmitter abstraction instead, which does not contain the same
combinators or subscription semantics as an RxJS Observable. However, the
EventEmitter provides a toRx() method which will return an RxJS Subject,
providing the same subscription and combinator conveniences as were
available prior to this refactor.
This is temporary, until issue #2794 is resolved, when Observables will
again be returned directly from Http class methods.
179 lines
6.7 KiB
TypeScript
179 lines
6.7 KiB
TypeScript
import {isString, isPresent, isBlank} from 'angular2/src/facade/lang';
|
|
import {Injectable} from 'angular2/src/di/decorators';
|
|
import {IRequestOptions, Connection, ConnectionBackend} from './interfaces';
|
|
import {Request} from './static_request';
|
|
import {BaseRequestOptions, RequestOptions} from './base_request_options';
|
|
import {RequestMethods} from './enums';
|
|
import {EventEmitter} from 'angular2/src/facade/async';
|
|
|
|
function httpRequest(backend: ConnectionBackend, request: Request): EventEmitter {
|
|
return backend.createConnection(request).response;
|
|
}
|
|
|
|
function mergeOptions(defaultOpts, providedOpts, method, url): RequestOptions {
|
|
var newOptions = defaultOpts;
|
|
if (isPresent(providedOpts)) {
|
|
// Hack so Dart can used named parameters
|
|
newOptions = newOptions.merge(new RequestOptions({
|
|
method: providedOpts.method,
|
|
url: providedOpts.url,
|
|
headers: providedOpts.headers,
|
|
body: providedOpts.body,
|
|
mode: providedOpts.mode,
|
|
credentials: providedOpts.credentials,
|
|
cache: providedOpts.cache
|
|
}));
|
|
}
|
|
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 {@link EventEmitter} which will emit a single {@link Response} when a
|
|
* response is received.
|
|
*
|
|
*
|
|
* ## Breaking Change
|
|
*
|
|
* Previously, methods of `Http` would return an RxJS Observable directly. For now,
|
|
* the `toRx()` method of {@link EventEmitter} needs to be called in order to get the RxJS
|
|
* Subject. `EventEmitter` does not provide combinators like `map`, and has different semantics for
|
|
* subscribing/observing. This is temporary; the result of all `Http` method calls will be either an
|
|
* Observable
|
|
* or Dart Stream when [issue #2794](https://github.com/angular/angular/issues/2794) is resolved.
|
|
*
|
|
* #Example
|
|
*
|
|
* ```
|
|
* import {Http, httpInjectables} from 'angular2/http';
|
|
* @Component({selector: 'http-app', appInjector: [httpInjectables]})
|
|
* @View({templateUrl: 'people.html'})
|
|
* class PeopleComponent {
|
|
* constructor(http: Http) {
|
|
* http.get('people.json')
|
|
* //Get the RxJS Subject
|
|
* .toRx()
|
|
* // 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);
|
|
* }
|
|
* }
|
|
* ```
|
|
*
|
|
* To use the {@link EventEmitter} returned by `Http`, simply pass a generator (See "interface
|
|
*Generator" in the Async Generator spec: https://github.com/jhusain/asyncgenerator) to the
|
|
*`observer` method of the returned emitter, with optional methods of `next`, `throw`, and `return`.
|
|
*
|
|
* #Example
|
|
*
|
|
* ```
|
|
* http.get('people.json').observer({next: (value) => this.people = people});
|
|
* ```
|
|
*
|
|
* 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} binding, as in the following example:
|
|
*
|
|
* #Example
|
|
*
|
|
* ```
|
|
* import {MockBackend, BaseRequestOptions, Http} from 'angular2/http';
|
|
* var injector = Injector.resolveAndCreate([
|
|
* BaseRequestOptions,
|
|
* MockBackend,
|
|
* bind(Http).toFactory(
|
|
* function(backend, defaultOptions) {
|
|
* return new Http(backend, defaultOptions);
|
|
* },
|
|
* [MockBackend, BaseRequestOptions])
|
|
* ]);
|
|
* var http = injector.get(Http);
|
|
* http.get('request-from-mock-backend.json').toRx().subscribe((res:Response) => doSomething(res));
|
|
* ```
|
|
*
|
|
**/
|
|
@Injectable()
|
|
export class Http {
|
|
constructor(private _backend: ConnectionBackend, private _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?: IRequestOptions): EventEmitter {
|
|
var responseObservable: EventEmitter;
|
|
if (isString(url)) {
|
|
responseObservable = httpRequest(
|
|
this._backend,
|
|
new Request(mergeOptions(this._defaultOptions, options, RequestMethods.GET, url)));
|
|
} else if (url instanceof Request) {
|
|
responseObservable = httpRequest(this._backend, url);
|
|
}
|
|
return responseObservable;
|
|
}
|
|
|
|
/**
|
|
* Performs a request with `get` http method.
|
|
*/
|
|
get(url: string, options?: IRequestOptions): EventEmitter {
|
|
return httpRequest(this._backend, new Request(mergeOptions(this._defaultOptions, options,
|
|
RequestMethods.GET, url)));
|
|
}
|
|
|
|
/**
|
|
* Performs a request with `post` http method.
|
|
*/
|
|
post(url: string, body: string, options?: IRequestOptions): EventEmitter {
|
|
return httpRequest(
|
|
this._backend,
|
|
new Request(mergeOptions(this._defaultOptions.merge(new RequestOptions({body: body})),
|
|
options, RequestMethods.POST, url)));
|
|
}
|
|
|
|
/**
|
|
* Performs a request with `put` http method.
|
|
*/
|
|
put(url: string, body: string, options?: IRequestOptions): EventEmitter {
|
|
return httpRequest(
|
|
this._backend,
|
|
new Request(mergeOptions(this._defaultOptions.merge(new RequestOptions({body: body})),
|
|
options, RequestMethods.PUT, url)));
|
|
}
|
|
|
|
/**
|
|
* Performs a request with `delete` http method.
|
|
*/
|
|
delete (url: string, options?: IRequestOptions): EventEmitter {
|
|
return httpRequest(this._backend, new Request(mergeOptions(this._defaultOptions, options,
|
|
RequestMethods.DELETE, url)));
|
|
}
|
|
|
|
/**
|
|
* Performs a request with `patch` http method.
|
|
*/
|
|
patch(url: string, body: string, options?: IRequestOptions): EventEmitter {
|
|
return httpRequest(
|
|
this._backend,
|
|
new Request(mergeOptions(this._defaultOptions.merge(new RequestOptions({body: body})),
|
|
options, RequestMethods.PATCH, url)));
|
|
}
|
|
|
|
/**
|
|
* Performs a request with `head` http method.
|
|
*/
|
|
head(url: string, options?: IRequestOptions): EventEmitter {
|
|
return httpRequest(this._backend, new Request(mergeOptions(this._defaultOptions, options,
|
|
RequestMethods.HEAD, url)));
|
|
}
|
|
}
|