feat(common): new HttpClient API
HttpClient is an evolution of the existing Angular HTTP API, which exists alongside of it in a separate package, @angular/common/http. This structure ensures that existing codebases can slowly migrate to the new API. The new API improves significantly on the ergonomics and features of the legacy API. A partial list of new features includes: * Typed, synchronous response body access, including support for JSON body types * JSON is an assumed default and no longer needs to be explicitly parsed * Interceptors allow middleware logic to be inserted into the pipeline * Immutable request/response objects * Progress events for both request upload and response download * Post-request verification & flush based testing framework
This commit is contained in:

committed by
Jason Aden

parent
2a7ebbe982
commit
37797e2b4e
@ -8,9 +8,11 @@
|
||||
|
||||
const xhr2: any = require('xhr2');
|
||||
|
||||
import {Injectable, Provider} from '@angular/core';
|
||||
import {Injectable, Optional, Provider} from '@angular/core';
|
||||
import {BrowserXhr, Connection, ConnectionBackend, Http, ReadyState, Request, RequestOptions, Response, XHRBackend, XSRFStrategy} from '@angular/http';
|
||||
|
||||
import {HttpClient, HttpRequest, HttpHandler, HttpInterceptor, HttpResponse, HTTP_INTERCEPTORS, HttpBackend, XhrFactory, ɵinterceptingHandler as interceptingHandler} from '@angular/common/http';
|
||||
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {Observer} from 'rxjs/Observer';
|
||||
import {Subscription} from 'rxjs/Subscription';
|
||||
@ -33,13 +35,9 @@ export class ServerXsrfStrategy implements XSRFStrategy {
|
||||
configureRequest(req: Request): void {}
|
||||
}
|
||||
|
||||
export class ZoneMacroTaskConnection implements Connection {
|
||||
response: Observable<Response>;
|
||||
lastConnection: Connection;
|
||||
|
||||
constructor(public request: Request, backend: XHRBackend) {
|
||||
validateRequestUrl(request.url);
|
||||
this.response = new Observable((observer: Observer<Response>) => {
|
||||
export abstract class ZoneMacroTaskWrapper<S, R> {
|
||||
wrap(request: S): Observable<R> {
|
||||
return new Observable((observer: Observer<R>) => {
|
||||
let task: Task = null !;
|
||||
let scheduled: boolean = false;
|
||||
let sub: Subscription|null = null;
|
||||
@ -50,25 +48,26 @@ export class ZoneMacroTaskConnection implements Connection {
|
||||
task = _task;
|
||||
scheduled = true;
|
||||
|
||||
this.lastConnection = backend.createConnection(request);
|
||||
sub = (this.lastConnection.response as Observable<Response>)
|
||||
.subscribe(
|
||||
res => savedResult = res,
|
||||
err => {
|
||||
if (!scheduled) {
|
||||
throw new Error('invoke twice');
|
||||
}
|
||||
savedError = err;
|
||||
scheduled = false;
|
||||
task.invoke();
|
||||
},
|
||||
() => {
|
||||
if (!scheduled) {
|
||||
throw new Error('invoke twice');
|
||||
}
|
||||
scheduled = false;
|
||||
task.invoke();
|
||||
});
|
||||
const delegate = this.delegate(request);
|
||||
sub = delegate.subscribe(
|
||||
res => savedResult = res,
|
||||
err => {
|
||||
if (!scheduled) {
|
||||
throw new Error(
|
||||
'An http observable was completed twice. This shouldn\'t happen, please file a bug.');
|
||||
}
|
||||
savedError = err;
|
||||
scheduled = false;
|
||||
task.invoke();
|
||||
},
|
||||
() => {
|
||||
if (!scheduled) {
|
||||
throw new Error(
|
||||
'An http observable was completed twice. This shouldn\'t happen, please file a bug.');
|
||||
}
|
||||
scheduled = false;
|
||||
task.invoke();
|
||||
});
|
||||
};
|
||||
|
||||
const cancelTask = (_task: Task) => {
|
||||
@ -91,11 +90,11 @@ export class ZoneMacroTaskConnection implements Connection {
|
||||
}
|
||||
};
|
||||
|
||||
// MockBackend is currently synchronous, which means that if scheduleTask is by
|
||||
// MockBackend for Http is synchronous, which means that if scheduleTask is by
|
||||
// scheduleMacroTask, the request will hit MockBackend and the response will be
|
||||
// sent, causing task.invoke() to be called.
|
||||
const _task = Zone.current.scheduleMacroTask(
|
||||
'ZoneMacroTaskConnection.subscribe', onComplete, {}, () => null, cancelTask);
|
||||
'ZoneMacroTaskWrapper.subscribe', onComplete, {}, () => null, cancelTask);
|
||||
scheduleTask(_task);
|
||||
|
||||
return () => {
|
||||
@ -111,6 +110,25 @@ export class ZoneMacroTaskConnection implements Connection {
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract delegate(request: S): Observable<R>;
|
||||
}
|
||||
|
||||
export class ZoneMacroTaskConnection extends ZoneMacroTaskWrapper<Request, Response> implements
|
||||
Connection {
|
||||
response: Observable<Response>;
|
||||
lastConnection: Connection;
|
||||
|
||||
constructor(public request: Request, private backend: XHRBackend) {
|
||||
super();
|
||||
validateRequestUrl(request.url);
|
||||
this.response = this.wrap(request);
|
||||
}
|
||||
|
||||
delegate(request: Request): Observable<Response> {
|
||||
this.lastConnection = this.backend.createConnection(request);
|
||||
return this.lastConnection.response as Observable<Response>;
|
||||
}
|
||||
|
||||
get readyState(): ReadyState {
|
||||
return !!this.lastConnection ? this.lastConnection.readyState : ReadyState.Unsent;
|
||||
}
|
||||
@ -124,13 +142,34 @@ export class ZoneMacroTaskBackend implements ConnectionBackend {
|
||||
}
|
||||
}
|
||||
|
||||
export class ZoneClientBackend extends
|
||||
ZoneMacroTaskWrapper<HttpRequest<any>, HttpResponse<any>> implements HttpBackend {
|
||||
constructor(private backend: HttpBackend) { super(); }
|
||||
|
||||
handle(request: HttpRequest<any>): Observable<HttpResponse<any>> { return this.wrap(request); }
|
||||
|
||||
protected delegate(request: HttpRequest<any>): Observable<HttpResponse<any>> {
|
||||
return this.backend.handle(request);
|
||||
}
|
||||
}
|
||||
|
||||
export function httpFactory(xhrBackend: XHRBackend, options: RequestOptions) {
|
||||
const macroBackend = new ZoneMacroTaskBackend(xhrBackend);
|
||||
return new Http(macroBackend, options);
|
||||
}
|
||||
|
||||
export function zoneWrappedInterceptingHandler(
|
||||
backend: HttpBackend, interceptors: HttpInterceptor[] | null) {
|
||||
const realBackend: HttpBackend = interceptingHandler(backend, interceptors);
|
||||
return new ZoneClientBackend(realBackend);
|
||||
}
|
||||
|
||||
export const SERVER_HTTP_PROVIDERS: Provider[] = [
|
||||
{provide: Http, useFactory: httpFactory, deps: [XHRBackend, RequestOptions]},
|
||||
{provide: BrowserXhr, useClass: ServerXhr},
|
||||
{provide: XSRFStrategy, useClass: ServerXsrfStrategy},
|
||||
{provide: BrowserXhr, useClass: ServerXhr}, {provide: XSRFStrategy, useClass: ServerXsrfStrategy},
|
||||
{
|
||||
provide: HttpHandler,
|
||||
useFactory: zoneWrappedInterceptingHandler,
|
||||
deps: [HttpBackend, [new Optional(), HTTP_INTERCEPTORS]]
|
||||
}
|
||||
];
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
import {ɵAnimationEngine} from '@angular/animations/browser';
|
||||
import {PlatformLocation, ɵPLATFORM_SERVER_ID as PLATFORM_SERVER_ID} from '@angular/common';
|
||||
import {HttpClientModule} from '@angular/common/http';
|
||||
import {platformCoreDynamic} from '@angular/compiler';
|
||||
import {Injectable, InjectionToken, Injector, NgModule, NgZone, PLATFORM_ID, PLATFORM_INITIALIZER, PlatformRef, Provider, RendererFactory2, RootRenderer, Testability, createPlatformFactory, isDevMode, platformCore, ɵALLOW_MULTIPLE_PLATFORMS as ALLOW_MULTIPLE_PLATFORMS} from '@angular/core';
|
||||
import {HttpModule} from '@angular/http';
|
||||
@ -62,7 +63,7 @@ export const SERVER_RENDER_PROVIDERS: Provider[] = [
|
||||
*/
|
||||
@NgModule({
|
||||
exports: [BrowserModule],
|
||||
imports: [HttpModule, NoopAnimationsModule],
|
||||
imports: [HttpModule, HttpClientModule, NoopAnimationsModule],
|
||||
providers: [
|
||||
SERVER_RENDER_PROVIDERS,
|
||||
SERVER_HTTP_PROVIDERS,
|
||||
|
@ -12,6 +12,7 @@
|
||||
"@angular/animations/browser": ["../../dist/packages/animations/browser"],
|
||||
"@angular/core": ["../../dist/packages/core"],
|
||||
"@angular/common": ["../../dist/packages/common"],
|
||||
"@angular/common/http": ["../../dist/packages/common/http"],
|
||||
"@angular/compiler": ["../../dist/packages/compiler"],
|
||||
"@angular/http": ["../../dist/packages/http"],
|
||||
"@angular/platform-browser": ["../../dist/packages/platform-browser"],
|
||||
|
Reference in New Issue
Block a user