我使用S3托管一个javascript应用程序,将使用HTML5 pushStates。问题是,如果用户书签了任何url,它将不会解析为任何东西。我需要的是能够接受所有url请求,并在我的S3桶中提供根index.html,而不仅仅是进行完全重定向。然后我的javascript应用程序可以解析URL并提供适当的页面。
有没有办法告诉S3为所有URL请求服务index.html,而不是做重定向?这类似于通过提供一个index.html来设置apache来处理所有传入的请求,如本例中的https://stackoverflow.com/a/10647521/1762614。我真的希望避免仅仅为了处理这些路由而运行web服务器。从S3执行所有操作非常有吸引力。
既然问题仍然存在,我想我提出另一个解决方案。
我的情况是,我想在合并之前自动将所有拉请求部署到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路由并尝试。如果你在angular的404页面中没有找到路由,你将永远不会到达@not_found,而会返回angular 404页面而不是not found files,这可以通过在@get_routes中添加If规则来修复。
我不得不说我在nginx配置和使用regex方面感到不太舒服,我得到了这个工作,经过了一些试验和错误,所以虽然这是有效的,但我相信有改进的空间,请分享你的想法。
注意:如果在s3配置中有重定向规则,请删除它们。
顺便说一下,在Safari中工作
与使用Lambda@Edge的另一个答案类似,您可以使用Cloudfront函数(截至2021年8月,它是免费层的一部分)。
我的URL结构是这样的:
React SPA托管在S3上
example.com/blog -博客托管在EC2上
由于我将博客托管在与SPA相同的域上,所以我不能使用Cloudfront 403/404错误页面使用默认错误页面的建议答案。
我的Cloudfront设置是:
设置两个源(S3和我的EC2实例通过Elastic ALB)
建立两个行为:
/博客
默认(*)
创建Cloudfront函数
将Cloudfront函数设置为默认(*)行为的“查看器请求”
我使用的是基于AWS示例的Cloudfront函数。这可能并不适用于所有情况,但我的URL结构的React应用程序不包含任何..
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;
}
2022年6月
我已经将我的函数更新为:
function handler(event) {
var request = event.request;
var uri = request.uri;
// Check whether the URI is missing a file name.
if (uri.endsWith('/')) {
request.uri += 'index.html';
}
// Check whether the URI is missing a file extension.
else if (!uri.includes('.')) {
request.uri += '/index.html';
}
return request;
}
我做到这一点的方法如下:
在域的S3控制台的“编辑重定向规则”部分中,添加以下规则:
<RoutingRules>
<RoutingRule>
<Condition>
<HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
</Condition>
<Redirect>
<HostName>yourdomainname.com</HostName>
<ReplaceKeyPrefixWith>#!/</ReplaceKeyPrefixWith>
</Redirect>
</RoutingRule>
</RoutingRules>
这将导致404错误的所有路径重定向到根域,并显示路径的散列版本。所以http://yourdomainname.com/posts将重定向到http://yourdomainname.com/#!如果/posts上没有文件。
然而,要使用HTML5的pushState,我们需要接受这个请求,并根据散列路径手动建立正确的pushState。所以把这个添加到index.html文件的顶部:
<script>
history.pushState({}, "entry page", location.hash.substring(1));
</script>
这将获取散列并将其转换为HTML5 pushState。从这一点开始,你可以使用pushStates在你的应用程序中拥有非哈希键路径。
如果你在这里寻找与React路由器和AWS Amplify控制台一起工作的解决方案,你已经知道你不能直接使用CloudFront重定向规则,因为Amplify控制台不会为应用程序公开CloudFront Distribution。
然而,解决方案非常简单-你只需要在Amplify控制台中添加一个重定向/重写规则,就像这样:
查看以下链接获取更多信息(以及截图中的复制友好规则):
https://docs.aws.amazon.com/amplify/latest/userguide/redirects.html#redirects-for-single-page-web-apps-spa
https://github.com/aws-amplify/amplify-console/issues/83
对于这个问题,我有4种解决方案。前三个问题已经在答案中提到了,最后一个是我的贡献。
Set the error document to index.html.
Problem: the response body will be correct, but the status code will be 404, which hurts SEO.
Set the redirection rules.
Problem: URL polluted with #! and page flashes when loaded.
Configure CloudFront.
Problem: all pages will return 404 from origin, so you need to chose if you won't cache anything (TTL 0 as suggested) or if you will cache and have issues when updating the site.
Prerender all pages.
Problem: additional work to prerender pages, specially when the pages changes frequently. For example, a news website.
我的建议是使用选项4。如果预先呈现所有页面,预期页面将不会出现404错误。页面将正常加载,框架将作为一个SPA进行控制和正常工作。还可以设置错误文档以显示通用的error.html页面和重定向规则,将404错误重定向到404.html页面(不带hashbang)。
关于403禁忌错误,我不让它们发生。在我的应用程序中,我认为主机桶中的所有文件都是公共的,我用具有读权限的everyone选项设置了这一点。如果您的站点有私有页面,让用户看到HTML布局应该不是问题。您需要保护的是数据,这是在后端完成的。
此外,如果你有私有资产,比如用户照片,你可以将它们保存在另一个bucket中。因为私有资产需要与数据同样的关注,并且不能与用于托管应用程序的资产文件进行比较。