ngx-translate-router Angular 通用 SSR:XMLHttpRequest.send 处的网络错误

ngx-translate-router Angular Universal SSR : NetworkError at XMLHttpRequest.send

我正在 Angular 通用应用程序中实现 ngx-translate-router,该应用程序已经在 SSR 中运行,但是一旦我添加了这个模块,SSR 就不再 运行 了。 但是与 ng serve 一起工作正常,所以这意味着没有 SSR 的这个模块的集成工作正常。

但是当 运行在 SSR 模式下 : npm run serve:ssr 我得到这个错误 :

NetworkError
at XMLHttpRequest.send (F:\GitaLab\vyv-angular\dist\server\main.js:1:819512)
at Observable_Observable._subscribe (F:\GitaLab\vyv-angular\dist\server\main.js:1:3285565)
at Observable_Observable._trySubscribe (F:\GitaLab\vyv-angular\dist\server\main.js:1:576303)
at Observable_Observable.subscribe (F:\GitaLab\vyv-angular\dist\server\main.js:1:576085)
at CatchOperator.call (F:\GitaLab\vyv-angular\dist\server\main.js:1:3994238)
at Observable_Observable.subscribe (F:\GitaLab\vyv-angular\dist\server\main.js:1:575939)
at DoOperator.call (F:\GitaLab\vyv-angular\dist\server\main.js:1:3343772)
at Observable_Observable.subscribe (F:\GitaLab\vyv-angular\dist\server\main.js:1:575939)
at F:\GitaLab\vyv-angular\dist\server\main.js:1:3315893
at Observable_Observable._subscribe (F:\GitaLab\vyv-angular\dist\server\main.js:1:3316238)

我根据 ngx-translate-router 的说明实现了 SSR 部分,所以我为 SSR 部分额外做了以下工作:

1 - 在 app.server.module.ts 中实现了一个拦截器,以便能够访问服务器部分中的翻译。这是拦截器:

    import { REQUEST } from '@nguniversal/express-engine/tokens';
    import * as express from 'express';
    import {Inject, Injectable} from '@angular/core';
    import {HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
    
    @Injectable()
    export class TranslateInterceptor implements HttpInterceptor {
      private readonly DEFAULT_PORT = 4200;
      private readonly PORT = process.env.PORT || this.DEFAULT_PORT;
    
      constructor(@Inject(REQUEST) private request: express.Request) {}
    
      getBaseUrl(req: express.Request) {
        const { protocol, hostname } = req;
        return this.PORT ?
          `${protocol}://${hostname}:${this.PORT}` :
          `${protocol}://${hostname}`;
      }
    
      intercept(request: HttpRequest<any>, next: HttpHandler) {
        if (request.url.startsWith('./assets')) {
          const baseUrl = this.getBaseUrl(this.request);
          request = request.clone({
            url: `${baseUrl}/${request.url.replace('./assets', 'assets')}`
          });
        }
        return next.handle(request);
      }
    }

2 - 我修改了 server.ts 以访问不同的语言环境并为它们添加了路由,但我认为问题出在这里。我想我错误地添加了在 server.ts 中监听的路由,但我在任何地方都找不到关于此的帮助...

这里是 server.ts


// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
  const server = express();
  const distFolder = join(process.cwd(), 'dist/browser');
  const fs = require('fs');
  const data: any = JSON.parse(fs.readFileSync(`src/assets/locales.json`, 'utf8'));
  const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
  server.use(cookieParser());
  // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
  server.engine('html', ngExpressEngine({
    bootstrap: AppServerModule,
  }));
  server.set('view engine', 'html');
  server.set('views', distFolder);
  server.get('/', (req, res) => {
    const defaultLang = 'en';
    const lang = req.acceptsLanguages('en', 'de', 'fr', 'es', 'pt');
    let cookieLang = req.cookies.lang;
    if (!cookieLang) {
      cookieLang = req.cookies.LOCALIZE_DEFAULT_LANGUAGE; // This is the default name of cookie
    }
    const definedLang = cookieLang || lang || defaultLang;
    console.log('domain requested without language');
    res.redirect(301, `/${definedLang}/`);
  });
  // Example Express Rest API endpoints
  // server.get('/api/**', (req, res) => { });
  // Serve static files from /browser
  server.get('*.*', express.static(distFolder, {
    maxAge: '1y'
  }));
  console.log('routes for the locales:');
  console.log(data);
  data.locales.forEach(route => {
    server.get(`/${route}`, (req: express.Request, res: express.Response) => {
      console.log('domain requested with language' + req.originalUrl);
      res.render(indexHtml, {
        req, providers: [
          { provide: REQUEST, useValue: req }
        ]
      });
    });
    server.get(`/${route}/*`, (req: express.Request, res: express.Response) => {
      console.log('page requested with language ' + req.originalUrl);
      res.render(indexHtml, {
        req, providers: [
          { provide: REQUEST, useValue: req }
        ]
      });
    });
  });
  return server;
}

function run(): void {
  const port = process.env.PORT || 4000;

  // Start up the Node server
  const server = app();
  server.use(compression());
  server.listen(port, () => {
    console.log(`Node Express server listening on http://localhost:${port}`);
  });
}

// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
  run();
}

export * from './src/main.server';

当我启动SSR服务器然后请求页面http://localhost 我可以看到重定向使用默认语言,控制台在上面报告的错误之前记录 "domain requested with language /en/"

我认为问题在于 server.ts 无法将请求的 url 映射到应用程序中声明的路由中的某些内容-routing.module.ts 但我不知道怎么做。 在 ngx-translate-router 的 GitHub 存储库中,他们说:

// let node server knows about the new routes:

let fs = require('fs');
let data: any = JSON.parse(fs.readFileSync(`src/assets/locales.json`, 'utf8'));
 
app.get('/', ngApp);
data.locales.forEach(route => {
  app.get(`/${route}`, ngApp);
  app.get(`/${route}/*`, ngApp);
});

但他们没有描述“ngApp”是什么,所以我只是根据 server.ts 在集成此插件之前的情况进行推断:

  // All regular routes use the Universal engine
  server.get('*', (req: express.Request, res: express.Response) => {
    res.render(indexHtml, {
      req, providers: [
        { provide: REQUEST, useValue: req }
      ]
    });
  });

所以我的问题是双重的。你认为我在我确定的方向上继续寻找是对的吗? (server.ts 执行错误)。 如果是,您知道如何纠正它吗? 如果没有,还有其他方向吗?

错误不在 server.ts,而是拦截器。我最初设计了这个拦截器,我认为它可以像那样共享以从 ./assets/locales.json 检索数据,因为它在 SSR 中检索 ./assets/i18n/en.[ 时工作正常。 =18=],但没有运气。 ngx-translate 和 ngx-translate-router 不能共享同一个拦截器,我不知道为什么,但就是这样。所以我不得不创建第二个拦截器(下面的代码),这解决了我的问题。

import { REQUEST } from '@nguniversal/express-engine/tokens';
import * as express from 'express';
import {Inject, Injectable, PLATFORM_ID} from '@angular/core';
import {HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {isPlatformServer} from '@angular/common';

@Injectable()
export class LocalizeInterceptor implements HttpInterceptor {
  constructor(@Inject(REQUEST) private request: express.Request, @Inject(PLATFORM_ID) private platformId: any) {}
  intercept(request: HttpRequest<any>, next: HttpHandler) {
    if (request.url.startsWith('assets') && isPlatformServer(this.platformId)) {
      const req = this.request;
      const url = req.protocol + '://' + req.get('host') + '/' + request.url;
      request = request.clone({
        url
      });
    }
    return next.handle(request);
  }
}