feat: remove @angular/http dependency from @angular/platform-server (#29408)
PR Close #29408
This commit is contained in:
parent
c9810064cb
commit
9745f55a65
|
@ -16,7 +16,6 @@ ng_module(
|
||||||
"//packages/common/http",
|
"//packages/common/http",
|
||||||
"//packages/compiler",
|
"//packages/compiler",
|
||||||
"//packages/core",
|
"//packages/core",
|
||||||
"//packages/http",
|
|
||||||
"//packages/platform-browser",
|
"//packages/platform-browser",
|
||||||
"//packages/platform-browser-dynamic",
|
"//packages/platform-browser-dynamic",
|
||||||
"//packages/platform-browser/animations",
|
"//packages/platform-browser/animations",
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
"@angular/common": "0.0.0-PLACEHOLDER",
|
"@angular/common": "0.0.0-PLACEHOLDER",
|
||||||
"@angular/compiler": "0.0.0-PLACEHOLDER",
|
"@angular/compiler": "0.0.0-PLACEHOLDER",
|
||||||
"@angular/core": "0.0.0-PLACEHOLDER",
|
"@angular/core": "0.0.0-PLACEHOLDER",
|
||||||
"@angular/http": "0.0.0-PLACEHOLDER",
|
|
||||||
"@angular/platform-browser": "0.0.0-PLACEHOLDER",
|
"@angular/platform-browser": "0.0.0-PLACEHOLDER",
|
||||||
"@angular/platform-browser-dynamic": "0.0.0-PLACEHOLDER"
|
"@angular/platform-browser-dynamic": "0.0.0-PLACEHOLDER"
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,33 +6,20 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
const xhr2: any = require('xhr2');
|
const xhr2: any = require('xhr2');
|
||||||
|
|
||||||
import {Injectable, Injector, Optional, Provider, InjectFlags} from '@angular/core';
|
import {Injectable, Injector, Provider} from '@angular/core';
|
||||||
import {BrowserXhr, Connection, ConnectionBackend, Http, ReadyState, Request, RequestOptions, Response, XHRBackend, XSRFStrategy} from '@angular/http';
|
|
||||||
|
|
||||||
import {HttpEvent, HttpRequest, HttpHandler, HttpInterceptor, HTTP_INTERCEPTORS, HttpBackend, XhrFactory, ɵHttpInterceptingHandler as HttpInterceptingHandler} from '@angular/common/http';
|
import {HttpEvent, HttpRequest, HttpHandler, HttpBackend, XhrFactory, ɵHttpInterceptingHandler as HttpInterceptingHandler} from '@angular/common/http';
|
||||||
|
|
||||||
import {Observable, Observer, Subscription} from 'rxjs';
|
import {Observable, Observer, Subscription} from 'rxjs';
|
||||||
|
|
||||||
const isAbsoluteUrl = /^[a-zA-Z\-\+.]+:\/\//;
|
|
||||||
|
|
||||||
function validateRequestUrl(url: string): void {
|
|
||||||
if (!isAbsoluteUrl.test(url)) {
|
|
||||||
throw new Error(`URLs requested via Http on the server must be absolute. URL: ${url}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ServerXhr implements BrowserXhr {
|
export class ServerXhr implements XhrFactory {
|
||||||
build(): XMLHttpRequest { return new xhr2.XMLHttpRequest(); }
|
build(): XMLHttpRequest { return new xhr2.XMLHttpRequest(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class ServerXsrfStrategy implements XSRFStrategy {
|
|
||||||
configureRequest(req: Request): void {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class ZoneMacroTaskWrapper<S, R> {
|
export abstract class ZoneMacroTaskWrapper<S, R> {
|
||||||
wrap(request: S): Observable<R> {
|
wrap(request: S): Observable<R> {
|
||||||
return new Observable((observer: Observer<R>) => {
|
return new Observable((observer: Observer<R>) => {
|
||||||
|
@ -111,36 +98,6 @@ export abstract class ZoneMacroTaskWrapper<S, R> {
|
||||||
protected abstract delegate(request: S): Observable<R>;
|
protected abstract delegate(request: S): Observable<R>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ZoneMacroTaskConnection extends ZoneMacroTaskWrapper<Request, Response> implements
|
|
||||||
Connection {
|
|
||||||
response: Observable<Response>;
|
|
||||||
// TODO(issue/24571): remove '!'.
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ZoneMacroTaskBackend implements ConnectionBackend {
|
|
||||||
constructor(private backend: XHRBackend) {}
|
|
||||||
|
|
||||||
createConnection(request: any): ZoneMacroTaskConnection {
|
|
||||||
return new ZoneMacroTaskConnection(request, this.backend);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ZoneClientBackend extends
|
export class ZoneClientBackend extends
|
||||||
ZoneMacroTaskWrapper<HttpRequest<any>, HttpEvent<any>> implements HttpBackend {
|
ZoneMacroTaskWrapper<HttpRequest<any>, HttpEvent<any>> implements HttpBackend {
|
||||||
constructor(private backend: HttpBackend) { super(); }
|
constructor(private backend: HttpBackend) { super(); }
|
||||||
|
@ -152,19 +109,12 @@ export class ZoneClientBackend extends
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function httpFactory(xhrBackend: XHRBackend, options: RequestOptions) {
|
|
||||||
const macroBackend = new ZoneMacroTaskBackend(xhrBackend);
|
|
||||||
return new Http(macroBackend, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function zoneWrappedInterceptingHandler(backend: HttpBackend, injector: Injector) {
|
export function zoneWrappedInterceptingHandler(backend: HttpBackend, injector: Injector) {
|
||||||
const realBackend: HttpBackend = new HttpInterceptingHandler(backend, injector);
|
const realBackend: HttpBackend = new HttpInterceptingHandler(backend, injector);
|
||||||
return new ZoneClientBackend(realBackend);
|
return new ZoneClientBackend(realBackend);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SERVER_HTTP_PROVIDERS: Provider[] = [
|
export const SERVER_HTTP_PROVIDERS: Provider[] = [
|
||||||
{provide: Http, useFactory: httpFactory, deps: [XHRBackend, RequestOptions]},
|
|
||||||
{provide: BrowserXhr, useClass: ServerXhr}, {provide: XSRFStrategy, useClass: ServerXsrfStrategy},
|
|
||||||
{provide: XhrFactory, useClass: ServerXhr}, {
|
{provide: XhrFactory, useClass: ServerXhr}, {
|
||||||
provide: HttpHandler,
|
provide: HttpHandler,
|
||||||
useFactory: zoneWrappedInterceptingHandler,
|
useFactory: zoneWrappedInterceptingHandler,
|
||||||
|
|
|
@ -10,7 +10,6 @@ import {ɵAnimationEngine} from '@angular/animations/browser';
|
||||||
import {DOCUMENT, PlatformLocation, ViewportScroller, ɵNullViewportScroller as NullViewportScroller, ɵPLATFORM_SERVER_ID as PLATFORM_SERVER_ID} from '@angular/common';
|
import {DOCUMENT, PlatformLocation, ViewportScroller, ɵNullViewportScroller as NullViewportScroller, ɵPLATFORM_SERVER_ID as PLATFORM_SERVER_ID} from '@angular/common';
|
||||||
import {HttpClientModule} from '@angular/common/http';
|
import {HttpClientModule} from '@angular/common/http';
|
||||||
import {Injectable, InjectionToken, Injector, NgModule, NgZone, Optional, PLATFORM_ID, PLATFORM_INITIALIZER, PlatformRef, Provider, RendererFactory2, RootRenderer, StaticProvider, Testability, createPlatformFactory, platformCore, ɵALLOW_MULTIPLE_PLATFORMS as ALLOW_MULTIPLE_PLATFORMS} from '@angular/core';
|
import {Injectable, InjectionToken, Injector, NgModule, NgZone, Optional, PLATFORM_ID, PLATFORM_INITIALIZER, PlatformRef, Provider, RendererFactory2, RootRenderer, StaticProvider, Testability, createPlatformFactory, platformCore, ɵALLOW_MULTIPLE_PLATFORMS as ALLOW_MULTIPLE_PLATFORMS} from '@angular/core';
|
||||||
import {HttpModule} from '@angular/http';
|
|
||||||
import {BrowserModule, EVENT_MANAGER_PLUGINS, ɵSharedStylesHost as SharedStylesHost, ɵgetDOM as getDOM} from '@angular/platform-browser';
|
import {BrowserModule, EVENT_MANAGER_PLUGINS, ɵSharedStylesHost as SharedStylesHost, ɵgetDOM as getDOM} from '@angular/platform-browser';
|
||||||
import {ɵplatformCoreDynamic as platformCoreDynamic} from '@angular/platform-browser-dynamic';
|
import {ɵplatformCoreDynamic as platformCoreDynamic} from '@angular/platform-browser-dynamic';
|
||||||
import {NoopAnimationsModule, ɵAnimationRendererFactory} from '@angular/platform-browser/animations';
|
import {NoopAnimationsModule, ɵAnimationRendererFactory} from '@angular/platform-browser/animations';
|
||||||
|
@ -69,7 +68,7 @@ export const SERVER_RENDER_PROVIDERS: Provider[] = [
|
||||||
*/
|
*/
|
||||||
@NgModule({
|
@NgModule({
|
||||||
exports: [BrowserModule],
|
exports: [BrowserModule],
|
||||||
imports: [HttpModule, HttpClientModule, NoopAnimationsModule],
|
imports: [HttpClientModule, NoopAnimationsModule],
|
||||||
providers: [
|
providers: [
|
||||||
SERVER_RENDER_PROVIDERS,
|
SERVER_RENDER_PROVIDERS,
|
||||||
SERVER_HTTP_PROVIDERS,
|
SERVER_HTTP_PROVIDERS,
|
||||||
|
|
|
@ -13,8 +13,6 @@ ts_library(
|
||||||
"//packages/compiler",
|
"//packages/compiler",
|
||||||
"//packages/core",
|
"//packages/core",
|
||||||
"//packages/core/testing",
|
"//packages/core/testing",
|
||||||
"//packages/http",
|
|
||||||
"//packages/http/testing",
|
|
||||||
"//packages/platform-browser",
|
"//packages/platform-browser",
|
||||||
"//packages/platform-server",
|
"//packages/platform-server",
|
||||||
"//packages/private/testing",
|
"//packages/private/testing",
|
||||||
|
|
|
@ -12,8 +12,6 @@ import {HTTP_INTERCEPTORS, HttpClient, HttpClientModule, HttpEvent, HttpHandler,
|
||||||
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
||||||
import {ApplicationRef, CompilerFactory, Component, HostListener, Inject, Injectable, Input, NgModule, NgZone, PLATFORM_ID, PlatformRef, ViewEncapsulation, destroyPlatform, getPlatform} from '@angular/core';
|
import {ApplicationRef, CompilerFactory, Component, HostListener, Inject, Injectable, Input, NgModule, NgZone, PLATFORM_ID, PlatformRef, ViewEncapsulation, destroyPlatform, getPlatform} from '@angular/core';
|
||||||
import {async, inject} from '@angular/core/testing';
|
import {async, inject} from '@angular/core/testing';
|
||||||
import {Http, HttpModule, Response, ResponseOptions, XHRBackend} from '@angular/http';
|
|
||||||
import {MockBackend, MockConnection} from '@angular/http/testing';
|
|
||||||
import {BrowserModule, Title, TransferState, makeStateKey} from '@angular/platform-browser';
|
import {BrowserModule, Title, TransferState, makeStateKey} from '@angular/platform-browser';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
import {BEFORE_APP_SERIALIZED, INITIAL_CONFIG, PlatformState, ServerModule, ServerTransferStateModule, platformDynamicServer, renderModule, renderModuleFactory} from '@angular/platform-server';
|
import {BEFORE_APP_SERIALIZED, INITIAL_CONFIG, PlatformState, ServerModule, ServerTransferStateModule, platformDynamicServer, renderModule, renderModuleFactory} from '@angular/platform-server';
|
||||||
|
@ -29,10 +27,6 @@ class MyServerApp {
|
||||||
bootstrap: [MyServerApp],
|
bootstrap: [MyServerApp],
|
||||||
declarations: [MyServerApp],
|
declarations: [MyServerApp],
|
||||||
imports: [ServerModule],
|
imports: [ServerModule],
|
||||||
providers: [
|
|
||||||
MockBackend,
|
|
||||||
{provide: XHRBackend, useExisting: MockBackend},
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
class ExampleModule {
|
class ExampleModule {
|
||||||
}
|
}
|
||||||
|
@ -232,30 +226,6 @@ class MyStylesApp {
|
||||||
class ExampleStylesModule {
|
class ExampleStylesModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
bootstrap: [MyServerApp],
|
|
||||||
declarations: [MyServerApp],
|
|
||||||
imports: [HttpModule, ServerModule],
|
|
||||||
providers: [
|
|
||||||
MockBackend,
|
|
||||||
{provide: XHRBackend, useExisting: MockBackend},
|
|
||||||
]
|
|
||||||
})
|
|
||||||
export class HttpBeforeExampleModule {
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
bootstrap: [MyServerApp],
|
|
||||||
declarations: [MyServerApp],
|
|
||||||
imports: [ServerModule, HttpModule],
|
|
||||||
providers: [
|
|
||||||
MockBackend,
|
|
||||||
{provide: XHRBackend, useExisting: MockBackend},
|
|
||||||
]
|
|
||||||
})
|
|
||||||
export class HttpAfterExampleModule {
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
bootstrap: [MyServerApp],
|
bootstrap: [MyServerApp],
|
||||||
declarations: [MyServerApp],
|
declarations: [MyServerApp],
|
||||||
|
@ -774,108 +744,6 @@ class HiddenModule {
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('http', () => {
|
|
||||||
it('can inject Http', async(() => {
|
|
||||||
const platform = platformDynamicServer(
|
|
||||||
[{provide: INITIAL_CONFIG, useValue: {document: '<app></app>'}}]);
|
|
||||||
platform.bootstrapModule(ExampleModule).then(ref => {
|
|
||||||
expect(ref.injector.get(Http) instanceof Http).toBeTruthy();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('can make Http requests', async(() => {
|
|
||||||
const platform = platformDynamicServer(
|
|
||||||
[{provide: INITIAL_CONFIG, useValue: {document: '<app></app>'}}]);
|
|
||||||
platform.bootstrapModule(ExampleModule).then(ref => {
|
|
||||||
const mock = ref.injector.get(MockBackend);
|
|
||||||
const http = ref.injector.get(Http);
|
|
||||||
ref.injector.get<NgZone>(NgZone).run(() => {
|
|
||||||
NgZone.assertInAngularZone();
|
|
||||||
mock.connections.subscribe((mc: MockConnection) => {
|
|
||||||
NgZone.assertInAngularZone();
|
|
||||||
expect(mc.request.url).toBe('http://localhost/testing');
|
|
||||||
mc.mockRespond(new Response(new ResponseOptions({body: 'success!', status: 200})));
|
|
||||||
});
|
|
||||||
http.get('http://localhost/testing').subscribe(resp => {
|
|
||||||
NgZone.assertInAngularZone();
|
|
||||||
expect(resp.text()).toBe('success!');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('requests are macrotasks', async(() => {
|
|
||||||
const platform = platformDynamicServer(
|
|
||||||
[{provide: INITIAL_CONFIG, useValue: {document: '<app></app>'}}]);
|
|
||||||
platform.bootstrapModule(ExampleModule).then(ref => {
|
|
||||||
const mock = ref.injector.get(MockBackend);
|
|
||||||
const http = ref.injector.get(Http);
|
|
||||||
expect(ref.injector.get<NgZone>(NgZone).hasPendingMacrotasks).toBeFalsy();
|
|
||||||
ref.injector.get<NgZone>(NgZone).run(() => {
|
|
||||||
NgZone.assertInAngularZone();
|
|
||||||
mock.connections.subscribe((mc: MockConnection) => {
|
|
||||||
expect(ref.injector.get<NgZone>(NgZone).hasPendingMacrotasks).toBeTruthy();
|
|
||||||
mc.mockRespond(new Response(new ResponseOptions({body: 'success!', status: 200})));
|
|
||||||
});
|
|
||||||
http.get('http://localhost/testing').subscribe(resp => {
|
|
||||||
expect(resp.text()).toBe('success!');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('works when HttpModule is included before ServerModule', async(() => {
|
|
||||||
const platform = platformDynamicServer(
|
|
||||||
[{provide: INITIAL_CONFIG, useValue: {document: '<app></app>'}}]);
|
|
||||||
platform.bootstrapModule(HttpBeforeExampleModule).then(ref => {
|
|
||||||
const mock = ref.injector.get(MockBackend);
|
|
||||||
const http = ref.injector.get(Http);
|
|
||||||
expect(ref.injector.get<NgZone>(NgZone).hasPendingMacrotasks).toBeFalsy();
|
|
||||||
ref.injector.get<NgZone>(NgZone).run(() => {
|
|
||||||
NgZone.assertInAngularZone();
|
|
||||||
mock.connections.subscribe((mc: MockConnection) => {
|
|
||||||
expect(ref.injector.get<NgZone>(NgZone).hasPendingMacrotasks).toBeTruthy();
|
|
||||||
mc.mockRespond(new Response(new ResponseOptions({body: 'success!', status: 200})));
|
|
||||||
});
|
|
||||||
http.get('http://localhost/testing').subscribe(resp => {
|
|
||||||
expect(resp.text()).toBe('success!');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('works when HttpModule is included after ServerModule', async(() => {
|
|
||||||
const platform = platformDynamicServer(
|
|
||||||
[{provide: INITIAL_CONFIG, useValue: {document: '<app></app>'}}]);
|
|
||||||
platform.bootstrapModule(HttpAfterExampleModule).then(ref => {
|
|
||||||
const mock = ref.injector.get(MockBackend);
|
|
||||||
const http = ref.injector.get(Http);
|
|
||||||
expect(ref.injector.get<NgZone>(NgZone).hasPendingMacrotasks).toBeFalsy();
|
|
||||||
ref.injector.get<NgZone>(NgZone).run(() => {
|
|
||||||
NgZone.assertInAngularZone();
|
|
||||||
mock.connections.subscribe((mc: MockConnection) => {
|
|
||||||
expect(ref.injector.get<NgZone>(NgZone).hasPendingMacrotasks).toBeTruthy();
|
|
||||||
mc.mockRespond(new Response(new ResponseOptions({body: 'success!', status: 200})));
|
|
||||||
});
|
|
||||||
http.get('http://localhost/testing').subscribe(resp => {
|
|
||||||
expect(resp.text()).toBe('success!');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('throws when given a relative URL', async(() => {
|
|
||||||
const platform = platformDynamicServer(
|
|
||||||
[{provide: INITIAL_CONFIG, useValue: {document: '<app></app>'}}]);
|
|
||||||
platform.bootstrapModule(ExampleModule).then(ref => {
|
|
||||||
const http = ref.injector.get(Http);
|
|
||||||
expect(() => http.get('/testing'))
|
|
||||||
.toThrowError(
|
|
||||||
'URLs requested via Http on the server must be absolute. URL: /testing');
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('HttpClient', () => {
|
describe('HttpClient', () => {
|
||||||
it('can inject HttpClient', async(() => {
|
it('can inject HttpClient', async(() => {
|
||||||
const platform = platformDynamicServer(
|
const platform = platformDynamicServer(
|
||||||
|
|
Loading…
Reference in New Issue