Angular nguniversal express发动机请求空喷油器
我正在使用Angular nguniversal express发动机请求空喷油器,angular,express,server-side-rendering,angular-universal,Angular,Express,Server Side Rendering,Angular Universal,我正在使用@nguniversal/express engine成功运行angular universal应用程序。我无法工作的部分是在angular应用程序中注入node/expressreq对象。对我来说,遵循这个原则并不能解决问题。错误是 NullInjectorError:InjectionToken请求没有提供程序 在服务器上,我使用以下摘录设置了express引擎 app.engine('html',ngExpressEngine({ bootstrap:AppServerModul
@nguniversal/express engine
成功运行angular universal应用程序。我无法工作的部分是在angular应用程序中注入node/expressreq
对象。对我来说,遵循这个原则并不能解决问题。错误是
NullInjectorError:InjectionToken请求没有提供程序
在服务器上,我使用以下摘录设置了express引擎
app.engine('html',ngExpressEngine({
bootstrap:AppServerModuleNgFactory,//给它一个要引导的模块,
提供程序:[provideModuleMap(惰性模块映射)]
}));
app.set('view engine','html');
app.set('views',join(DIST_文件夹,'browser');
app.get('*.',express.static(join(DIST_文件夹,'browser'));
并使用(1)呈现索引文件
app.get('*',(请求:请求,res)=>{
res.render(join(DIST_文件夹,'browser','index.html'),{req});
});
应用程序组件中的注入看起来像
构造函数(
@注入(请求)私有请求:请求
) {}
而请求
是从@nguniversal/express engine/tokens导入的。我还测试了在渲染部分(1)中添加以下条目,但它也不起作用
providers: [ { provide: REQUEST, useValue: req } ]
还尝试使用@Optional()
装饰器注入,并使用平台ID
检查平台是否为服务器
,请求对象是否未注入且为空
。
您对如何成功访问angular本身的
req
对象有什么建议吗?您不能在浏览器中访问REQUEST
,它是从expressjs
传递到ng universal
的服务器端对象。因此,在最坏的情况下,您的代码应该执行以下操作
constructor(
@Inject(PLATFORM_ID) private platformId,
@Optional() @Inject(REQUEST) private request
) {
doSomethingWithRequestIfServer();
}
someOtherMethod() {
doSomethingWithRequestIfServer();
}
doSomethingWithRequestIfServer() {
if (isPlatformServer(this.platformId)) {
// should see this in stdout of node process, or wherever node logs
console.log('rendering server side for request:', req);
/* use req */
} else {
// browser console should print null
console.log('working browser side, request should be null', req);
}
}
然后,在浏览器中,应用程序将不会查看请求,而在服务器端呈现时,应用程序将不会查看请求。也就是说,服务器端渲染将运行angular应用程序一次,因为此渲染的结果HTML将发送到浏览器。然后,浏览器将再次引导Angular应用程序,并从服务器渲染完成的位置拾取
在最好的情况下,为了避免如果和平台检查,我建议您对应用程序进行布局,使您的服务器端代码仅在服务器端提供,而浏览器端代码在浏览器中提供。这肯定需要更多的代码,但从维护的角度来看,这将更直接。
(下面是猴子快速打字)
公共接口声明
// common.ts
export abstract class MyServiceBase {
abstract doSomething(): void;
}
// if implementations of service will be provided only in Angular implementation, InjectionToken can be used.
export const MY_SERVICE = new InjectionToken<MyServiceBase>('MY_SERVICE');
// otherwise, if server side implementation will be injected by from node process, then should be string only. (For alternative illustrated below in the end).
// export const MY_SERVICE = 'MY_SERVICE';
适用于服务器端和浏览器端的通用应用程序代码
// app.module.ts
@NgModule({
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
// app-browser.service.ts
@Injectable()
export class MyServiceForBrowser extends MyServiceBase {
constructor() {
console.log('MyService browser implementation');
}
doSomthing(): void {
// do something meaningful in browser
}
}
引导此选项,而不是默认的AppModule
,以分离实现配置
// app-browser.module.ts
@NgModule({
imports: [AppModule],
providers: [{provide: MY_SERVICE, useClass: MyServiceForBrowser}],
bootstrap: [AppComponent]
})
export class AppBrowserModule {
}
角度应用程序的服务器端
若您可以在Angular应用程序中直接实现服务器端功能,那个么
// app-server.service.ts
@Injectable()
export class MyServiceForServer extends MyServiceBase {
constructor(@Inject(REQUEST) private request) {
console.log('MyService server implementation');
}
doSomthing(): void {
// do something meaningful on server
console.log('request is', this.request);
}
}
从ngExpressEngine
引导服务器端模块
// app-server.module.ts
@NgModule({
imports: [AppModule],
providers: [{provide: MY_SERVICE, useClass: MyServiceForServer}],
bootstrap: [AppComponent]
})
export class AppServerModule {}
服务器的备选服务器端
作为替代方案,MyServiceForServer
甚至可以从服务器代码中提供。在这种情况下,无需在角度实现中提供实现:
// app-server.module.ts
@NgModule({
imports: [AppModule],
bootstrap: [AppComponent]
})
export class AppServerModule {}
相反,请将其作为普通服务器端代码编写:
export class MyServiceForServer extends MyService {
constructor(private request) {
console.log('MyService server implementation');
}
doSomthing(): void {
// do something meaningful on server
console.log('request is', this.request);
}
}
并作为外部值注入:
app.get('*', (req: Request, res) => {
// construct the server side service instance
const myService = new MyServiceForServer(req);
// render server side with service instance provided
res.render(join(DIST_FOLDER, 'browser', 'index.html'), {
providers: [{provide: MY_SERVICE, useValue: myService}],
req
});
});
在这种情况下,请确保您的MY_服务
令牌是纯字符串,而不是InjectionToken
实例。在package.json中设置版本:
"@types/express": "^4.17.4",
"@types/node": "^12.11.1",
浏览器或节点日志中是否出现错误?错误出现在浏览器中您无法在浏览器中访问请求
,它是从expressjs
传递到ng universal
的服务器端对象。因此,在最坏的情况下,您的代码应该执行类似于if(isServerPlatform(this.platformId)){/*use req*/}
的操作,并注释@Optional()@Inject(REQUEST)req
。然后在浏览器中,应用程序将不会查看请求
,而在服务器端呈现时,应用程序会查看。感谢您的详细回答。这并不能解决我的问题,我熟悉isPlatformServer
和isPlatformBrowser
的用法,但我无法成功访问请求对象。投了赞成票,但我还不能接受。可能它与AppServerModuleNgFactory
有关,因为它是静态的?该图告诉您,request
对象在浏览器中不可用,因为它只发生在expressjs中的服务器中。所以,您不应该期望它在浏览器中存在。既然您根据自己的评论抱怨浏览器出现错误,那么答案会解释原因。请记住,该请求是用户浏览器GET
对服务器的请求。此GET
请求由服务器处理,响应是带有JavaScript等工件的纯HTML
。然后此响应触发angular应用程序在浏览器中启动。您希望在浏览器中访问什么req obj?另一方面,为了清楚起见,如果您确实希望在浏览器中访问您的请求对象,那么您必须序列化它并将其在响应中传递给浏览器(在标头、cookie、TransferState等内)。然后您可以在浏览器中反序列化它并相应地访问它。这种方式通常不是你想要的东西。只要记住,如果你想做SSR,那么你的angular应用程序将运行2次。一次在服务器上,然后在浏览器中。
"@types/express": "^4.17.4",
"@types/node": "^12.11.1",