Angular Universal 7不进行http调用

时间:2019-01-17 23:16:55

标签: angular express angular7 serverside-rendering angular-universal

我最近开始将Angular 7.2.0应用程序转换为Angular通用应用程序。该应用程序已成功呈现服务器端,并在快速的PC和Internet连接上相当快速地传输了状态,但不幸的是,直到交出该页面后,呈现的页面仍缺少通常从API提取的任何数据。

我已将serverURL添加到服务器提供的令牌中,并且在呈现服务器端时,所有http请求都是使用完全限定的url发出的。但是,这似乎无关紧要,因为所有http调用都在完成之前被取消!我的服务器未显示对api端点的调用,并且客户端记录了错误{}(默认情况下显示为[Error])。

我正在使用TransferStateModuleTransferHttpCacheModule,但到目前为止没有任何效果。似乎快速引擎渲染将不会等待异步http调用。它会影响组件ngOnInit中的http调用,服务constructor中的resolver中的http调用,因此我不知道如何获取数据。

对于诸如配置数据之类的东西,我将必要的字段作为令牌注入,并在服务器端渲染中检查该令牌,但是我无法对应用程序中的所有数据执行此操作。

下面是我的服务器和应用程序模块的相关部分

app.ts (节点快速服务器)

 app.get('*.*', express.static(APP_CONFIG.client_root, {maxAge: 0}));

    // Render Angular Universal
    if (APP_CONFIG.universal) {
        // Our index.html we'll use as our template
        const templateFile = HelpersService.tryLoad(join(APP_CONFIG.client_root, './index.html'));
            const config = configService.getConfig();
            const template = templateFile.toString();
            const win = domino.createWindow(template);
            const fakeanimation = {
                value: () => {
                    return {
                        enumerable: true,
                        configurable: true
                    };
                },
            };
            global['window'] = win;
            global['document'] = {...win.document, 
                createElement: () => {},
                body: {
                    ...win.document.body,
                    style: { 
                        ...win.document.body.style,
                        transform: fakeanimation,
                        opacity: fakeanimation,    
                        bottom: fakeanimation,    
                        left: fakeanimation,    
                    }
                }
            };
            global['navigator'] = win.navigator;
            global['location'] = win.location;

            const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('../ssr/ssr.js');

            app.engine('html', ngExpressEngine({
                bootstrap: AppServerModuleNgFactory,
                providers: [
                    provideModuleMap(LAZY_MODULE_MAP),
                ]
            }));

            app.set('view engine', 'html');
            app.set('views', APP_CONFIG.client_root);


            app.get('*', (req, res, next) => {
                const protocol = res.locals.protocol || req.protocol;
                const showPort = ((APP_CONFIG.port === 80 && protocol === 'http') || (protocol === 'https')) ? false : true;
                const serverUrl = `${protocol}://${req.hostname}${showPort ? ':'+APP_CONFIG.port : ''}`;
                res.render('index', {
                    req,
                    document: template,
                    url: req.url,
                    providers: [
                        {
                            provide: 'serverURL',
                            useValue: serverUrl
                        },
                        {
                            provide: 'SERVER_CONFIG',
                            useValue: config || {}
                        },
                        {
                            provide: 'SERVER_AUTH',
                            useValue: {signedIn: !!res.locals.auth}
                        },
                    ]
                });
            });
        }

app.module.ts

@NgModule({
    bootstrap: [
        AppComponent
    ],
    imports: [
        BrowserModule.withServerTransition({appId: 'myapp'}),
        BrowserAnimationsModule,
        SharedModule,
        TransferHttpCacheModule,
        BrowserTransferStateModule,
        RouterModule.forRoot(
    ...

app.server.module.ts

@NgModule({
  imports: [
    // The AppServerModule should import your AppModule followed
    // by the ServerModule from @angular/platform-server.
    AppModule,
    ServerModule,
    ServerTransferStateModule,
    ModuleMapLoaderModule // <-- *Important* to have lazy-loaded routes work
  ],
  // Since the bootstrapped component is not inherited from your
  // imported AppModule, it needs to be repeated here.
  bootstrap: [AppComponent],
  providers: [
    {
        provide: HTTP_INTERCEPTORS,
        useClass: ServerSideRequestInterceptor,
        multi: true,
    },
  ]
})
export class AppServerModule {}

ServerSideRequestInterceptor读取serverURL注入令牌,并将其附加到以/开头的所有请求中。

我感觉我已经很接近破解了,但是我无法告诉我如何让它等待应用程序中的http调用。

如果有帮助,我在这里添加了Repro应用:https://github.com/swimmadude66/ngUniversal-http-repro

1 个答案:

答案 0 :(得分:0)

感谢@CaerusKaru在@nguniversal问题页面上解决了这一问题! https://github.com/angular/universal/issues/1046#issuecomment-455408250

从本质上讲,问题在于我使用interceptor将完整的服务器路径附加到api调用中。它使用Object.assign(request, {url: fullServerUrl});为所有以/开头的http请求设置新的url。合适的方法显然是request.clone({url: fullServerUrl});,并且更改了一个代码片段就导致数据像急流一样流动。

希望这对其他人有帮助,我将把repro-repo(很难说和输入)作为Angular7和Universal的样板。