Angular 通用 7 不进行 http 调用

Angular universal 7 not making http calls

我最近开始将我的 Angular 7.2.0 应用程序转换为 Angular 通用应用程序。该应用程序成功呈现服务器端,并在快速 PC 和互联网连接上相当快速地传输状态,但不幸的是,在交接之前,呈现的页面丢失了通常从 API.[=26= 获取的任何数据。 ]

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

我正在使用 TransferStateModule 以及 TransferHttpCacheModule,但到目前为止没有任何效果。似乎 express-engine 渲染器不会等待异步 http 调用。它会影响组件 ngOnInit 中的 http 调用、服务的 constructor 以及 resolver 中的任何调用,所以我不知道如何获取数据。

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

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

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 调用。

编辑: 这是我使用 working universal 和 angular 7 制作的 Repo: https://github.com/swimmadude66/AngularPWASeed/tree/universal7

感谢@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 的样板作为工作的起点。