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

问题描述

我正在已在SSR中运行的Angular Universal应用程序中实现ngx-translate-router,但是一旦添加此模块,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/**',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}/*`,res: express.Response) => {
      console.log('page requested with language ' + req.originalUrl);
      res.render(indexHtml,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映射到app-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('*',res: express.Response) => {
    res.render(indexHtml,{
      req,providers: [
        { provide: REQUEST,useValue: req }
      ]
    });
  });

所以我的问题是双重的。您认为继续寻找我确定的方向是正确的吗? (server.ts实现是错误的)。 如果是,您是否知道如何纠正它? 如果否,则有其他寻找方向吗?

解决方法

我已经10h了...我不擅长调试节点...错误不是在server.ts中,而是在拦截器中。这个拦截器最初是为ngx-translate设计的,我认为可以像从./assets/locales.json检索数据那样共享它,因为它在SSR中可以很好地用于检索./assets/i18n/en .json,但没有运气。 ngx-translate和ngx-translate-router无法共享同一个拦截器,我没有确切的原因,仅此而已。所以我不得不创建第二个拦截器(下面的代码),这解决了我的问题。

我知道我并没有引起很多反响,但是有一天,maby会帮助某人。

    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);
      }
    }