我使用S3来托管将使用HTML5 pushStates的javascript应用。问题是如果用户为任何URL添加书签,它将无法解析为任何内容。我需要的是能够获取所有URL请求并在我的S3存储桶中提供root index.html,而不仅仅是执行完全重定向。然后我的javascript应用程序可以解析URL并提供正确的页面。
有没有办法告诉S3为所有URL请求提供index.html而不是重定向?这与设置apache以通过提供单个index.html来处理所有传入请求类似,如下例所示:https://stackoverflow.com/a/10647521/1762614。我真的想避免运行Web服务器来处理这些路由。从S3做一切非常有吸引力。
答案 0 :(得分:202)
使用CloudFront帮助,在没有网址攻击的情况下解决它很容易。
答案 1 :(得分:191)
我能够让它发挥作用的方式如下:
在您的域的S3控制台的编辑重定向规则部分中,添加以下规则:
<RoutingRules>
<RoutingRule>
<Condition>
<HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
</Condition>
<Redirect>
<HostName>yourdomainname.com</HostName>
<ReplaceKeyPrefixWith>#!/</ReplaceKeyPrefixWith>
</Redirect>
</RoutingRule>
</RoutingRules>
这将使用路径的hash-bang版本将导致未找到404的所有路径重定向到根域。因此,如果/ post没有文件,http://yourdomainname.com/posts会重定向到http://yourdomainname.com/#!/posts。
然而,要使用HTML5 pushStates,我们需要接受此请求并根据hash-bang路径手动建立正确的pushState。所以将它添加到index.html文件的顶部:
<script>
history.pushState({}, "entry page", location.hash.substring(1));
</script>
这会抓取哈希并将其转换为HTML5 pushState。从这一点开始,您可以使用pushStates在您的应用程序中使用非哈希爆炸路径。
答案 2 :(得分:123)
其他人提到的基于S3 / Redirect的方法几乎没有问题。
解决方案是:
5.对于SEO需求+确保您的index.html没有缓存,请执行以下操作:
我可以提供有关nginx设置的更多详细信息,只需留言即可。已经很难学会了。
云端分发更新后。使您的云端缓存无效一次,使其处于原始模式。 点击浏览器中的网址,一切都很好。
答案 3 :(得分:14)
这是切向的,但是对于那些使用Rackt's React Router library并且希望在S3上托管的(HTML5)浏览器历史记录的人来说,这是一个提示。
假设用户在S3托管的静态网站上访问/foo/bear
。根据{{3}} David's,重定向规则会将其发送到/#/foo/bear
。如果您的应用程序是使用浏览器历史记录构建的,那么这将无济于事。但是,此时您的应用程序已加载,现在可以操作历史记录。
在项目中包含Rackt earlier suggestion(另请参阅React Router项目中的history),您可以添加一个知道哈希历史路径的侦听器并根据需要替换路径,如下所示例如:
import ReactDOM from 'react-dom';
/* Application-specific details. */
const route = {};
import { Router, useRouterHistory } from 'react-router';
import { createHistory } from 'history';
const history = useRouterHistory(createHistory)();
history.listen(function (location) {
const path = (/#(\/.*)$/.exec(location.hash) || [])[1];
if (path) history.replace(path);
});
ReactDOM.render(
<Router history={history} routes={route}/>,
document.body.appendChild(document.createElement('div'))
);
回顾一下:
/foo/bear
定向到/#/foo/bear
。#/foo/bear
历史记录符号。Using Custom Histories将按预期工作,所有其他浏览器历史记录功能也将如此。我注意到的唯一缺点是初始请求时发生的插页式重定向。
这受到Link
tags的启发,我怀疑可以很容易地适应任何应用程序。
答案 4 :(得分:11)
我看到了这个问题的4个解决方案。前3个已经包含在答案中,最后一个是我的贡献。
Set the error document to index.html.
问题:响应正文将是正确的,但状态代码将为404,这会伤害搜索引擎优化。
Set the redirection rules.
问题:受#!
污染的网址和加载时页面闪烁。
Configure CloudFront.
问题:所有页面都将从原点返回404,因此您需要选择是否不缓存任何内容(建议使用TTL 0),或者在更新站点时是否缓存并出现问题。
预呈现所有页面 问题:预呈现页面的额外工作,特别是在页面频繁更改时。例如,新闻网站。
我的建议是使用选项4.如果您预渲染所有页面,预期页面将不会有404错误。页面将加载正常,框架将采取控制并正常作为SPA。您还可以设置错误文档以显示通用error.html页面和重定向规则,以将404错误重定向到404.html页面(不使用hashbang)。
关于403 Forbidden错误,我根本不让它们发生。在我的应用程序中,我认为主机存储桶中的所有文件都是公共的,我使用所有选项和读取权限进行设置。如果您的网站包含私有网页,则让用户看到HTML 布局应该不是问题。您需要保护的是数据,这在后端完成。
此外,如果您有私有资产,例如用户照片,则可以将其保存在另一个存储桶中。由于私有资产需要与数据相同,并且无法与用于托管应用程序的资产文件进行比较。
答案 5 :(得分:10)
我今天遇到了同样的问题,但@ Mark-Nutter的解决方案不完整,无法从我的angularjs应用程序中删除hashbang。
事实上,您必须转到修改权限,点击添加更多权限,然后在您的存储桶中添加正确的列表给大家。使用此配置,AWS S3现在将能够返回404错误,然后重定向规则将正确捕获案例。
然后您可以转到编辑重定向规则并添加此规则:
<RoutingRules>
<RoutingRule>
<Condition>
<HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
</Condition>
<Redirect>
<HostName>subdomain.domain.fr</HostName>
<ReplaceKeyPrefixWith>#!/</ReplaceKeyPrefixWith>
</Redirect>
</RoutingRule>
</RoutingRules>
在这里,您可以将 HostName subdomain.domain.fr替换为您的域名和 KeyPrefix #!/,如果您不使用hashbang方法进行搜索引擎优化目的
当然,只有在角度应用程序中已经安装了html5mode时,所有这一切才有效。
$locationProvider.html5Mode(true).hashPrefix('!');
答案 6 :(得分:7)
答案 7 :(得分:5)
您现在可以使用Lambda @ Edge来重写路径
这是一个有效的lambda @ Edge函数:
'use strict';
exports.handler = (event, context, callback) => {
// Extract the request from the CloudFront event that is sent to Lambda@Edge
var request = event.Records[0].cf.request;
// Extract the URI from the request
var olduri = request.uri;
// Match any '/' that occurs at the end of a URI. Replace it with a default index
var newuri = olduri.replace(/\/$/, '\/index.html');
// Log the URI as received by CloudFront and the new URI to be used to fetch from origin
console.log("Old URI: " + olduri);
console.log("New URI: " + newuri);
// Replace the received URI with the URI that includes the index page
request.uri = newuri;
return callback(null, request);
};
答案 8 :(得分:4)
因为问题仍然存在,我虽然投入另一种解决方案。
我的情况是我想在合并之前将所有拉取请求自动部署到s3进行测试,以便在[mydomain] / pull-requests / [pr number] /
上访问它们
(例如www.example.com/pull-requests/822 /)
据我所知,非s3规则场景允许使用html5路由在一个存储桶中有多个项目,因此虽然大多数投票建议适用于根文件夹中的项目,但它不适用于自己子文件夹中的多个项目
所以我将我的域指向我的服务器,其中跟随nginx配置完成了工作
location /pull-requests/ {
try_files $uri @get_files;
}
location @get_files {
rewrite ^\/pull-requests\/(.*) /$1 break;
proxy_pass http://<your-amazon-bucket-url>;
proxy_intercept_errors on;
recursive_error_pages on;
error_page 404 = @get_routes;
}
location @get_routes {
rewrite ^\/(\w+)\/(.+) /$1/ break;
proxy_pass http://<your-amazon-bucket-url>;
proxy_intercept_errors on;
recursive_error_pages on;
error_page 404 = @not_found;
}
location @not_found {
return 404;
}
它试图获取文件,如果没有找到假定它是html5路由并尝试。如果你有一个404角度的页面找不到路线,你将永远不会到达@not_found并获得角度404页面而不是找不到的文件,这可以通过@get_routes中的某些if规则修复。
我不得不说我在nginx配置和使用正则表达式方面感觉不太舒服,我得到了一些试验和错误,所以虽然这有效,但我确信还有改进的空间,请做分享你的想法。
注意:如果您在S3配置中使用了s3重定向规则,请将其删除。
和btw在Safari中运行
答案 9 :(得分:3)
正在寻找同样的问题。 我最终使用了上述建议解决方案的混合。
首先,我有一个包含多个文件夹的s3存储桶,每个文件夹代表一个react / redux网站。 我还使用cloudfront进行缓存失效。
所以我不得不使用路由规则来支持404并将它们重定向到散列配置:
<RoutingRules>
<RoutingRule>
<Condition>
<KeyPrefixEquals>website1/</KeyPrefixEquals>
<HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
</Condition>
<Redirect>
<Protocol>https</Protocol>
<HostName>my.host.com</HostName>
<ReplaceKeyPrefixWith>website1#</ReplaceKeyPrefixWith>
</Redirect>
</RoutingRule>
<RoutingRule>
<Condition>
<KeyPrefixEquals>website2/</KeyPrefixEquals>
<HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
</Condition>
<Redirect>
<Protocol>https</Protocol>
<HostName>my.host.com</HostName>
<ReplaceKeyPrefixWith>website2#</ReplaceKeyPrefixWith>
</Redirect>
</RoutingRule>
<RoutingRule>
<Condition>
<KeyPrefixEquals>website3/</KeyPrefixEquals>
<HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
</Condition>
<Redirect>
<Protocol>https</Protocol>
<HostName>my.host.com</HostName>
<ReplaceKeyPrefixWith>website3#</ReplaceKeyPrefixWith>
</Redirect>
</RoutingRule>
</RoutingRules>
在我的js代码中,我需要使用baseName
配置react-router来处理它。
首先,确保您的依赖项是可互操作的,我安装了history==4.0.0
与react-router==3.0.1
不兼容。
我的依赖关系是:
我已经为加载历史记录创建了一个history.js
文件:
import {useRouterHistory} from 'react-router';
import createBrowserHistory from 'history/lib/createBrowserHistory';
export const browserHistory = useRouterHistory(createBrowserHistory)({
basename: '/website1/',
});
browserHistory.listen((location) => {
const path = (/#(.*)$/.exec(location.hash) || [])[1];
if (path) {
browserHistory.replace(path);
}
});
export default browserHistory;
这段代码允许用哈希处理服务器发送的404,并在历史记录中替换它们以加载我们的路由。
您现在可以使用此文件来配置商店和根文件。
import {routerMiddleware} from 'react-router-redux';
import {applyMiddleware, compose} from 'redux';
import rootSaga from '../sagas';
import rootReducer from '../reducers';
import {createInjectSagasStore, sagaMiddleware} from './redux-sagas-injector';
import {browserHistory} from '../history';
export default function configureStore(initialState) {
const enhancers = [
applyMiddleware(
sagaMiddleware,
routerMiddleware(browserHistory),
)];
return createInjectSagasStore(rootReducer, rootSaga, initialState, compose(...enhancers));
}
import React, {PropTypes} from 'react';
import {Provider} from 'react-redux';
import {Router} from 'react-router';
import {syncHistoryWithStore} from 'react-router-redux';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import variables from '!!sass-variable-loader!../../../css/variables/variables.prod.scss';
import routesFactory from '../routes';
import {browserHistory} from '../history';
const muiTheme = getMuiTheme({
palette: {
primary1Color: variables.baseColor,
},
});
const Root = ({store}) => {
const history = syncHistoryWithStore(browserHistory, store);
const routes = routesFactory(store);
return (
<Provider {...{store}}>
<MuiThemeProvider muiTheme={muiTheme}>
<Router {...{history, routes}} />
</MuiThemeProvider>
</Provider>
);
};
Root.propTypes = {
store: PropTypes.shape({}).isRequired,
};
export default Root;
希望它有所帮助。 你会注意到这个配置我使用redux注入器和自制的sagas注入器来通过路由异步加载javascript。 不介意用这些线。
答案 10 :(得分:2)
如果您登陆此处寻找适用于React Router和AWS Amplify Console的解决方案-您已经知道您不能直接使用CloudFront重定向规则,因为Amplify Console不会公开该应用程序的CloudFront分布。
但是,解决方案非常简单-您只需要在Amplify Console中添加重定向/重写规则,如下所示:
有关更多信息(以及屏幕截图中的易于复制的规则),请参见以下链接:
答案 11 :(得分:0)
我自己正在寻找答案。 S3似乎只支持重定向,您不能只重写URL并静默返回不同的资源。我正在考虑使用我的构建脚本来简单地在所有必需的路径位置复制我的index.html。也许这对你也有用。
答案 12 :(得分:0)
我发现这是the most elegant solution - 使用带有通配符重定向的app路由器模块。
{ path: '**', redirectTo: '' }
然后,正如上面无数帖子中所述,请确保您将404/403错误重定向到index.html,状态为200。问题是这会导致浏览器刷新将基本href加载为(href + previous route)。如果您正在
查看路由器视图 www.my-app.com/home
然后刷新将显示
www.my-app.com/home/home
要剥离重复的路径路径,请使用APP_BASE_HREF模块重新分配浏览器基础href just like this
如果您需要保留第一个url参数,请附加'/'
拆分中的多个结果。
浏览器点击您的SPA重定向www.your-app.com/home/home
现在将用www.your-app.com/home
替换该网址,该应用将按照您的应用内路由配置中的预期行为
答案 13 :(得分:0)
大多数建议的解决方案的问题,尤其是最受欢迎的答案,是您永远不会收到 404 错误。如果你想继续得到404,参考这个解决方案
这是我在s3存储桶中添加的重定向规则
code <file name>
在我的 Vue 应用程序安装后,我在加载时添加了以下代码。它会将我的应用程序带到所需的路线。您可以根据 Angular 或您正在使用的任何内容更改 router.push 部分。
code inde.html style.css
即使您不在 s3 级别使用重定向,在您可能喜欢的任何其他重定向中,所有路由的单一基础也很方便。它有助于后端识别不是对真正后端资源的请求,前端应用将能够处理它。
答案 14 :(得分:0)
与使用 Lambda@Edge 的另一个 answer 类似,您可以使用 Cloudfront Functions(截至 2021 年 8 月,它是 free tier 的一部分)。
我的网址结构是这样的:
由于我将博客托管在与 SPA 相同的域上,因此我无法使用使用默认错误页面的 Cloudfront 403/404 错误页面的建议答案。
我对 Cloudfront 的设置是:
/blog
default (*)
default (*)
行为的“查看者请求”我正在使用这个基于 AWS example 的 Cloudfront 函数。这可能不适用于所有情况,但我的 React 应用程序 URL 结构不包含任何 .
。
function handler(event) {
var request = event.request;
var uri = request.uri;
// If the request is not for an asset (js, png, etc), render the index.html
if (!uri.includes('.')) {
request.uri = '/index.html';
}
return request;
}
答案 15 :(得分:-1)
只需提出极其简单的答案。如果您托管在S3上,只需对路由器使用哈希定位策略即可。
export const AppRoutingModule:ModuleWithProviders = RouterModule.forRoot(routes,{useHash:true,scrollPositionRestoration:'enabled'});