我正在开发的一个网站使用Angular Universal 10.1.1,并托管在IIS服务器上。这种情况的一个例子是https://tragoa.com/welcome
如果我像foo.com一样从根目录导航到页面,那么网站会直接转到正确的路径foo.com/welcome,而不会进行301重定向。然而,如果我尝试直接加载foo.com/welcome,那么它将返回该请求,并将301重定向到foo.com/welcome/,该重定向请求将返回200 OK,然后在url栏中删除尾部斜杠。因此:
- 不带斜杠的请求(/welcome)
- 301重定向返回到位置包含尾部斜杠(/webre/)的请求
- 返回200 OK
- 在浏览器的URL栏中删除尾部斜线
这里的主要问题是不需要的重定向
只有当页面使用预先呈现的HTML时才会发生这种情况。如果给定路由没有预先呈现的索引HTML,它不会重定向。
这导致了灯塔的问题和一些第三方重定向问题。
有人知道是什么原因造成的吗?
一些可能有帮助的信息:
html的基本href是<base href="/">
注意:我将其更改为没有/然后路由加倍,如foo.com/welcome/welcome
我的服务器得到的是:
import 'zone.js/dist/zone-node';
import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';
import { existsSync, readFileSync } from 'fs';
import 'localstorage-polyfill';
const domino = require('domino');
let distFolder = join(process.cwd(), '../spa/browser');
const template = readFileSync(join(distFolder, 'index.html')).toString();
const win = domino.createWindow(template);
win.Object = Object;
win.Math = Math;
global['window'] = win;
global['document'] = win.document;
global['branch'] = null;
global['object'] = win.object;
global['HTMLElement'] = win.HTMLElement;
global['navigator'] = win.navigator;
global['localStorage'] = localStorage;
import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '@angular/common';
export function app(): express.Express {
const server = express();
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index.html';
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', distFolder);
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
server.get(
'*',
(req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
return server;
}
function run(): void {
const port = process.env.PORT || 4000;
const server = app();
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
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';
我使用此命令创建我的站点,然后部署文件"prerender": "ng build --configuration production && ng run my-app.spa:prerender:production"
我终于找到了
简短的回答:由于角度通用预渲染的工作方式,您无法删除"在预渲染页面的末尾,您唯一的解决方案是使所有路线以"/"默认情况下,当angular启动并重定向时,它也会重定向到完全相同的url,因此不算作重定向
长答案:
为什么会发生这种情况
首先,我们首先需要了解为什么会发生这种情况。因此,角度预渲染的工作方式是,对于每个路由,生成具有该路由名称的目录/文件夹;index.html";文件内部。假设我有一条路线";"mywebsite.net/page1";,angular universal将生成一个目录";第1页";里面有一个index.html文件,其中包含该页面的html代码。在nginx和apache服务器中,目录都用"/"相对于基本目录。因此,如果服务器想要访问page1文件夹内的index.html文件,则路径将表示为"/page1/";,注意结尾的斜杠意味着我们在page1目录中,或者如果我们有一个";page1.html";根目录下的文件,该文件将被表示为"/第1页";但angular universal并不是这样生成页面的,如上所述,它生成的目录中有index.html文件。
为什么有301重定向
因此,当我们第一次访问任何路线时,让我们说"/第1页";,我们的服务器进入page1目录,在里面打开index.html文件,因为它是一个目录,而不是根目录中的文件,所以它添加了"/"最后,一旦index.html文件被渲染,angular脚本就会运行,它会将您重定向到您在angular路由中定义的路由,如果它没有"/"它将重定向到它,从而删除斜杠。这就是为什么先添加斜线,然后再删除斜线的原因。在你的问题中,你提到铬去除了"/"斜线,但这是不正确的,角度
解决方案
解决方案是在默认情况下,使所有路由以尾部斜杠结尾。在你的每个模块中,你都会将你的路线定义为
const routes: Routes = [
{ path: 'page1/.', component: page1Component, data: { title: "page q", subTitle: " " } },
...
];
通知"/"然后在路径的末尾和您的href中
<a href="" routerLink="/page1/.">Page1</a>
在main.ts中,您可以添加一些逻辑,在没有斜杠的链接中添加尾部斜杠。像这个
import { Location } from '@angular/common';
const __stripTrailingSlash = (Location as any).stripTrailingSlash;
(Location as any).stripTrailingSlash = function _stripTrailingSlash(url: string): string {
const queryString$ = url.match(/([^?]*)?(.*)/);
if (queryString$[2].length > 0) {
return /[^/]/$/.test(queryString$[1]) ? queryString$[1] + '.' + queryString$[2] : __stripTrailingSlash(url);
}
return /[^/]/$/.test(url) ? url + '.' : __stripTrailingSlash(url);
};
这样做可以修复重定向问题。
很抱歉我的英语不好,我希望你能理解我的解释
这进一步推进了@ahmed在答案中的发现,并提供了第二个(可能是第三个)解决方案。
解决方案1:
在Angular Universal 11.1之前,没有现成的混合渲染。11.1中的这一更改允许Angular Universal的渲染器同时处理SSR和预渲染页面(如果存在的话)。更多信息可在此处查看:https://www.youtube.com/watch?v=RFwjJAZOzOA
在这一变化之前,有两种行为:
- 运行的服务器将返回一个未预先呈现的页面,即使存在
- 服务器将返回一个301重定向,以路由到带有问题中所述尾部斜杠的文件路径
有了这个混合渲染更新,如果你像我在web.config
中那样路由流量,或者类似地为Apache服务器路由流量,Angular Universal渲染引擎将简单地返回预渲染的页面(如果存在)或SSR。没有301重定向到所需的文件路径。
解决方案2
有些人已经成功地添加了redirect: false
,如以下回答中所述:https://stackoverflow.com/a/60533554/5832236这是因为预呈现的index.html
文件是静态文件。
该属性是express.static和server static的一部分。重定向发生在这里:https://github.com/expressjs/serve-static/blob/9b5a12a76f4d70530d2d2a8c7742e9158ed3c0a4/index.js#L202您可以通过在node_modules
中临时更改该值来测试是否是这种重定向
我的建议答案
将Angular Universal更新到11.1以上,并确保将所有流量路由到main.js
服务器文件以处理所有内容。
除了问题中的server.ts
之外,还有对我有用的web.config
:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<webSocket enabled="true" />
<handlers>
<add name="iisnode" path="server/main.js" verb="*" modules="iisnode"/>
</handlers>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="100.00:00:00" />
<mimeMap fileExtension="woff" mimeType="application/font-woff" />
<mimeMap fileExtension="woff2" mimeType="application/font-woff2" />
<mimeMap fileExtension=".json" mimeType="application/json" />
</staticContent>
<rewrite>
<rules>
<rule name="MainRule" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
</conditions>
<action type="Rewrite" url="server/main.js" />
</rule>
</rules>
</rewrite>
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="bin"/>
</hiddenSegments>
</requestFiltering>
</security>
<httpErrors existingResponse="PassThrough" />
</system.webServer>
</configuration>