12

我需要获取我的 Angular 2 应用程序的完整基本 URL(例如http://localhost:5000https://productionserver.com),以便我可以将它传递给应用程序上下文中的第 3 方服务。应用程序的位置取决于它是开发、各种暂存/测试环境还是生产环境,我想动态检测它,所以我不需要维护硬编码列表。

过去曾提出过类似的问题,但答案(即使用某些版本的 window.location.hostname 或 window.location.origin 属性)仅在浏览器呈现 angular2 应用程序时才有效。

我希望我的应用程序可以与 Angular Universal 一起使用,这意味着它需要在无法访问 DOM 对象(如 window.location)的服务器端呈现。

任何想法如何做到这一点?作为参考,使用 asp.net core 作为后端(使用默认的 dotnet new angular 模板)。

4

4 回答 4

24

我有一些 angular 5 和 angular universal 的工作代码

在 server.ts 中替换这个

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

在Angular方面,您可以使用以下代码获取主机

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.');
        }
    }
}
于 2017-11-22T12:11:30.527 回答
4

现在我正在使用 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)
        ]
    }));

之后我可以在 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
  }
}
于 2017-09-02T14:57:13.107 回答
1

你会发现所有来自 Http 请求的内容都不会被预渲染:这是因为 Universal 需要绝对 URL。

由于您的开发和生产服务器不会有相同的 URL,因此您自己管理它是非常痛苦的。

我的自动化解决方案:使用 Angular 4.3 的新 HttpClient 拦截器功能,结合 Express 引擎。

拦截器在服务器上下文中捕获所有请求以添加完整的 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);
  }
}

然后在您的 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 {}

现在您可以使用 Express 引擎将完整的 URL 传递给 Angular,只需更新您的 server.js :

 function angularRouter(req, res) { 
  res.render('index', {
    req,
    res,
    providers: [{
      provide: 'serverUrl',
      useValue: `${req.protocol}://${req.get('host')}`
    }]
  });
}
于 2019-02-28T21:23:56.363 回答
0

感谢 estus 的帮助,我设法拼凑出一些可行的东西。

看起来大多数 Angular Universal 模板实际上让服务器传递了一个名为“originUrl”的区域参数,服务器渲染方法使用该参数来提供可以注入其他方法的 ORIGIN_URL 令牌。我找不到任何关于此的文档,但您可以在此处查看示例

所以如果你写这样的东西......

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

您应该能够在服务器和客户端上获取完整的原始 URL。

于 2017-04-17T04:31:30.293 回答