如何在 Angular Universal 中获取完整的基础 URL(包括服务器、端口和协议)?
How to get full base URL (including server, port and protocol) in Angular Universal?
我需要获取 Angular 2 应用程序的完整基础 URL(例如 http://localhost:5000 或 https://productionserver.com),以便我可以将其传递给应用上下文中的第三方服务。应用程序的位置根据它是开发环境、各种 staging/testing 环境还是生产环境而有所不同,我想动态检测它,因此我不需要维护硬编码列表。
A 已在过去发布,但答案(即使用某些版本的 window.location.hostname 或 window.location.origin 属性)仅在 angular2 个应用程序正在由浏览器呈现。
我希望我的应用程序与 Angular Universal 一起使用,这意味着它需要在无法访问 DOM 对象(例如 window.location
)的服务器端呈现.
关于如何实现这个的任何想法?作为参考,使用 asp.net core
作为后端(使用默认的 dotnet
新的 angular 模板)。
感谢 estus 的帮助,我设法拼凑出了一些有用的东西。
看起来大多数 Angular 通用模板实际上让服务器传递一个名为 "originUrl" 的区域参数,服务器渲染方法使用它来提供一个 ORIGIN_URL 令牌,可以以其他方式注入。我找不到任何关于此的文档,但您可以查看 an example here.
所以如果你写这样的东西...
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。
现在我正在使用 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
}
}
我有一些使用 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.');
}
}
}
您会发现所有来自 Http 请求的内容都不会被预渲染:这是因为 Universal 需要绝对 URLs。
因为你的开发和生产服务器不会有相同的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')}`
}]
});
}
我需要获取 Angular 2 应用程序的完整基础 URL(例如 http://localhost:5000 或 https://productionserver.com),以便我可以将其传递给应用上下文中的第三方服务。应用程序的位置根据它是开发环境、各种 staging/testing 环境还是生产环境而有所不同,我想动态检测它,因此我不需要维护硬编码列表。
A
我希望我的应用程序与 Angular Universal 一起使用,这意味着它需要在无法访问 DOM 对象(例如 window.location
)的服务器端呈现.
关于如何实现这个的任何想法?作为参考,使用 asp.net core
作为后端(使用默认的 dotnet
新的 angular 模板)。
感谢 estus 的帮助,我设法拼凑出了一些有用的东西。
看起来大多数 Angular 通用模板实际上让服务器传递一个名为 "originUrl" 的区域参数,服务器渲染方法使用它来提供一个 ORIGIN_URL 令牌,可以以其他方式注入。我找不到任何关于此的文档,但您可以查看 an example here.
所以如果你写这样的东西...
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。
现在我正在使用 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
}
}
我有一些使用 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.');
}
}
}
您会发现所有来自 Http 请求的内容都不会被预渲染:这是因为 Universal 需要绝对 URLs。
因为你的开发和生产服务器不会有相同的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')}`
}]
});
}