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);
}
}
我正在 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);
}
}