From 1e3b8a1354ed753cc3adf9b5ea7072d77e0f97d6 Mon Sep 17 00:00:00 2001 From: George Kalpakas Date: Wed, 8 Apr 2020 15:27:51 +0300 Subject: [PATCH] docs: remove unneeded code from `universal` example/guide (#36483) In the past, server-side rendered apps needed to convert URLs used in API requests to absolute when rendering on the server. Originally, this was handled in the `universal` guide and corresponding example app by modifying the `HeroService` to use `APP_BASE_HREF` to derive the absolute URL. In #28956, the guide was updated to show an improved method: Specifying an `HttpInterceptor` that took care of converting the URLs to absolute. That interceptor was only provided when rendering the app on the server. By mistake, the corresponding example app was not updated along with the guide. Since `@nguniversal/*` v7.1.0, it is no longer necessary to convert the URLs to absolute inside the app. This is handled in the `@nguniversal` libs (see angular/universal#897). This commit updates the example app to remove unnecessary code and modifies the guide to mention the issue with absolute URLs, but explain that developers only need to worry about it when not using one of the `@nguniversal/*-engine` packages. PR Close #36483 --- .../universal/src/app/app.server.module.ts | 2 +- .../universal/src/app/hero.service.ts | 10 +-- aio/content/guide/universal.md | 87 ++++--------------- 3 files changed, 22 insertions(+), 77 deletions(-) diff --git a/aio/content/examples/universal/src/app/app.server.module.ts b/aio/content/examples/universal/src/app/app.server.module.ts index c3f7a2fa39..a2cdc20c9b 100644 --- a/aio/content/examples/universal/src/app/app.server.module.ts +++ b/aio/content/examples/universal/src/app/app.server.module.ts @@ -10,7 +10,7 @@ import { AppComponent } from './app.component'; ServerModule, ], providers: [ - // Add universal-only providers here + // Add server-only providers here. ], bootstrap: [AppComponent], }) diff --git a/aio/content/examples/universal/src/app/hero.service.ts b/aio/content/examples/universal/src/app/hero.service.ts index 7376a820e4..22ee06a633 100644 --- a/aio/content/examples/universal/src/app/hero.service.ts +++ b/aio/content/examples/universal/src/app/hero.service.ts @@ -1,5 +1,4 @@ -import { Injectable, Inject, Optional } from '@angular/core'; -import { APP_BASE_HREF } from '@angular/common'; +import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Observable, of } from 'rxjs'; @@ -18,14 +17,9 @@ export class HeroService { headers: new HttpHeaders({ 'Content-Type': 'application/json' }) }; - // #docregion ctor constructor( private http: HttpClient, - private messageService: MessageService, - @Optional() @Inject(APP_BASE_HREF) origin?: string) { - this.heroesUrl = `${origin || ''}${this.heroesUrl}`; - } - // #enddocregion ctor + private messageService: MessageService) { } /** GET heroes from the server */ getHeroes(): Observable { diff --git a/aio/content/guide/universal.md b/aio/content/guide/universal.md index 7257d6fceb..8ac0f40efd 100644 --- a/aio/content/guide/universal.md +++ b/aio/content/guide/universal.md @@ -187,72 +187,6 @@ Similarly, without mouse or keyboard events, a server-side app can't rely on a u The app must determine what to render based solely on the incoming client request. This is a good argument for making the app [routable](guide/router). -{@a http-urls} -### Using absolute URLs for server requests - -The tutorial's `HeroService` and `HeroSearchService` delegate to the Angular `HttpClient` module to fetch application data. -These services send requests to _relative_ URLs such as `api/heroes`. -In a Universal app, HTTP URLs must be _absolute_ (for example, `https://my-server.com/api/heroes`). -This means you need to change your services to make requests with absolute URLs when running on the server and with relative -URLs when running in the browser. - -One solution is to provide the full URL to your application on the server, and write an interceptor that can retrieve this -value and prepend it to the request URL. If you're using the `ngExpressEngine`, as shown in the example in this guide, half -the work is already done. We'll assume this is the case, but it's trivial to provide the same functionality. - -Start by creating an [HttpInterceptor](api/common/http/HttpInterceptor). - - - -import {Injectable, Inject, Optional} from '@angular/core'; -import {HttpInterceptor, HttpHandler, HttpRequest, HttpHeaders} from '@angular/common/http'; -import {Request} from 'express'; -import {REQUEST} from '@nguniversal/express-engine/tokens'; - -@Injectable() -export class UniversalInterceptor implements HttpInterceptor { - - constructor(@Optional() @Inject(REQUEST) protected request?: Request) {} - - intercept(req: HttpRequest, next: HttpHandler) { - let serverReq: HttpRequest = req; - if (this.request) { - let newUrl = `${this.request.protocol}://${this.request.get('host')}`; - if (!req.url.startsWith('/')) { - newUrl += '/'; - } - newUrl += req.url; - serverReq = req.clone({url: newUrl}); - } - return next.handle(serverReq); - } -} - - - -Next, provide the interceptor in the providers for the server `AppModule`. - - - -... -import {HTTP_INTERCEPTORS} from '@angular/common/http'; -import {UniversalInterceptor} from './universal-interceptor'; - -@NgModule({ - ... - providers: [{ - provide: HTTP_INTERCEPTORS, - useClass: UniversalInterceptor, - multi: true - }], -}) -export class AppServerModule {} - - - -Now, on every HTTP request made on the server, this interceptor will fire and replace the request URL with the absolute -URL provided in the Express `Request` object. - {@a universal-engine} ### Universal template engine @@ -267,8 +201,6 @@ requests into server-rendered HTML pages. It accepts an object with the followin * `bootstrap`: The root `NgModule` or `NgModule` factory to use for bootstraping the app when rendering on the server. For the example app, it is `AppServerModule`. It's the bridge between the Universal server-side renderer and the Angular application. * `extraProviders`: This is optional and lets you specify dependency providers that apply only when rendering the app on the server. You can do this when your app needs information that can only be determined by the currently running server instance. -One example could be the running server's *origin*, which could be used to [calculate absolute HTTP URLs](#http-urls) if -not using the `Request` token as shown above. The `ngExpressEngine()` function returns a `Promise` callback that resolves to the rendered page. It's up to the engine to decide what to do with that page. This engine's `Promise` callback returns the rendered page to the web server, @@ -334,3 +266,22 @@ The following Node.js Express code routes all remaining requests to `/dist`, and file isn't found. + +### Using absolute URLs for HTTP (data) requests on the server + +The tutorial's `HeroService` and `HeroSearchService` delegate to the Angular `HttpClient` module to fetch application data. +These services send requests to _relative_ URLs such as `api/heroes`. +In a server-side rendered app, HTTP URLs must be _absolute_ (for example, `https://my-server.com/api/heroes`). +This means that the URLs must be somehow converted to absolute when running on the server and be left relative when running in the browser. + +If you are using one of the `@nguniversal/*-engine` packages (such as `@nguniversal/express-engine`), this is taken care for you automatically. +You don't need to do anything to make relative URLs work on the server. + +If, for some reason, you are not using an `@nguniversal/*-engine` package, you may need to handle it yourself. + +The recommended solution is to pass the full request URL to the `options` argument of [renderModule()](api/platform-server/renderModule) or [renderModuleFactory()](api/platform-server/renderModuleFactory) (depending on what you use to render `AppServerModule` on the server). +This option is the least intrusive as it does not require any changes to the app. +Here, "request URL" refers to the URL of the request as a response to which the app is being rendered on the server. +For example, if the client requested `https://my-server.com/dashboard` and you are rendering the app on the server to respond to that request, `options.url` should be set to `https://my-server.com/dashboard`. + +Now, on every HTTP request made as part of rendering the app on the server, Angular can correctly resolve the request URL to an absolute URL, using the provided `options.url`.