How to get full base URL (including server, port and protocol) in Angular Universal?

后端 未结 4 2344
说谎
说谎 2020-12-11 03:15

I need to get the full base URL (e.g. http://localhost:5000 or https://productionserver.com) of my Angular 2 app so that I can pass it along to a 3rd party service in the co

相关标签:
4条回答
  • 2020-12-11 03:42

    You’ll find that all content coming from Http requests won’t be pre-rendered: it’s because Universal needs absolute URLs.

    As your development and production server won’t have the same URL, it’s quite painful to manage it on your own.

    My solution to automate this : using the new HttpClient interceptor feature of Angular 4.3, combined with the Express engine.

    The interceptor catches all requests when in server context to prepend the full URL.

    import { Injectable, Inject, Optional } from '@angular/core';
     import { HttpInterceptor, HttpHandler, HttpRequest } from'@angular/common/http';
     @Injectable()
     export class UniversalInterceptor implements HttpInterceptor {
      constructor(@Optional() @Inject('serverUrl') protected serverUrl: string) {}
      intercept(req: HttpRequest<any>, next: HttpHandler) {
        const serverReq = !this.serverUrl ? req : req.clone({
          url: ``${this.serverUrl}${req.url}``
        });
        return next.handle(serverReq);
      }
    }
    

    Then provide it in your AppServerModule :

    import { NgModule } from '@angular/core';
    import { ServerModule } from '@angular/platform-server';
    import { HTTP_INTERCEPTORS } from '@angular/common/http';
    import { AppModule } from './app.module';
    import { AppComponent } from './app.component';
    import { UniversalInterceptor } from './universal.interceptor';
    @NgModule({
      imports: [
        AppModule,
        ServerModule
      ],
      providers: [{
        provide: HTTP_INTERCEPTORS,
        useClass: UniversalInterceptor,
        /* Multi is important or you will delete all the other interceptors */
        multi: true
      }],
      bootstrap: [AppComponent]
    })
    export class AppServerModule {}
    

    Now you can use Express engine to pass the full URL to Angular, just update your server.js :

     function angularRouter(req, res) { 
      res.render('index', {
        req,
        res,
        providers: [{
          provide: 'serverUrl',
          useValue: `${req.protocol}://${req.get('host')}`
        }]
      });
    }
    
    0 讨论(0)
  • 2020-12-11 03:45

    Now I'm using the server.ts ngExpressEngine:

    import { ngExpressEngine } from '@nguniversal/express-engine';
    
    const {AppServerModuleNgFactory, LAZY_MODULE_MAP} = require('./dist/server/main.bundle');
    
        const {provideModuleMap} = require('@nguniversal/module-map-ngfactory-loader');
    
        app.engine('html', ngExpressEngine({
            bootstrap: AppServerModuleNgFactory,
            providers: [
                provideModuleMap(LAZY_MODULE_MAP)
            ]
        }));
    

    And after that I can use in location.service.ts:

    constructor(@Optional() @Inject(REQUEST) private request: any,
                @Optional() @Inject(RESPONSE) private response: any,
                @Inject(PLATFORM_ID) private platformId: Object)
    {
      if (isPlatformServer(this.platformId))
      {
        console.log(this.request.get('host’)); // host on the server
      } else
      {
        console.log(document.location.hostname); // host on the browser
      }
    }
    
    0 讨论(0)
  • 2020-12-11 04:00

    I have bit of working code with angular 5 and angular universal

    in server.ts replace this

    app.engine('html', (_, options, callback) => {
        let engine = ngExpressEngine({
            bootstrap: AppServerModuleNgFactory,
            providers: [
                { provide: 'request', useFactory: () => options.req, deps: [] },
                provideModuleMap(LAZY_MODULE_MAP)
            ]
        });
        engine(_, options, callback);
    });
    

    and in Angular side you can get host with below code

    export class AppComponent {
        constructor(
            private injector: Injector,
            @Inject(PLATFORM_ID) private platformId: Object
        ) {
            console.log('hi, we\'re here!');
            if (isPlatformServer(this.platformId)) {
                let req = this.injector.get('request');
                console.log("locales from crawlers: " + req.headers["accept-language"]);
                console.log("host: " + req.get('host'));
                console.log("headers: ", req.headers);
            } else {
                console.log('we\'re rendering from the browser, there is no request object.');
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-11 04:08

    Thanks to help from estus, I managed to hack together something that works.

    It looks like most Angular Universal templates actually have the server passing a zone parameter called "originUrl", which gets used by the server rendering methods to provide a ORIGIN_URL token that can be injected in other methods. I couldn't find any documentation about this, but you can see an example here.

    So if you write something like this...

    export function getBaseUrl() {
        if (Zone.current.get("originUrl")) {
            return Zone.current.get('originUrl');
        } else if (location) {
            return location.origin;
        } else {
            return 'something went wrong!';
        }
    }
    

    You should be able to get the full origin URL on both the server and the client.

    0 讨论(0)
提交回复
热议问题