问题描述
我正在已在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);
}
}