我使用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执行所有操作非常有吸引力。


当前回答

对于这个问题,我有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中。因为私有资产需要与数据同样的关注,并且不能与用于托管应用程序的资产文件进行比较。

其他回答

在CloudFront的帮助下,不需要url黑客就可以很容易地解决这个问题。

创建S3桶,例如:react 使用以下设置创建CloudFront发行版: 默认根对象:index.html 来源域名:S3桶域名,例如:react.s3.amazonaws.com 转到错误页面选项卡,单击创建自定义错误响应: HTTP错误码:403:禁止(404:未找到,在S3静态网站的情况下) 自定义错误响应:是 响应页面路径:/index.html HTTP响应码:200:OK 点击创建

我自己也在寻找答案。S3似乎只支持重定向,你不能只是重写URL并无声地返回一个不同的资源。我正在考虑使用我的构建脚本在所有需要的路径位置简单地复制index.html。也许这对你也有用。

与使用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;
}

在2022年,Lambda函数是

函数处理程序(事件){ Var request = event.request; Var uri = request.uri; if (uri.endsWith("/")) { 请求。Uri += "index.html" } else if (!uri.includes(".")){ 请求。Uri += "/index.html" } 返回请求; }

这是一个非常简单的答案。如果您在S3上托管,则只需为路由器使用散列位置策略。

export const AppRoutingModule: ModuleWithProviders = RouterModule。forRoot(routes, {useHash: true, scrollPositionRestoration: 'enabled'});