在同一 URL 下在 S3 + Cloudfront 上托管多个 SPA 网络应用程序
Hosting multiple SPA web apps on S3 + Cloudfront under same URL
我有两个静态 Web 应用程序 (create-react-apps),它们目前位于两个单独的 S3 存储桶中。两个存储桶都配置为 public 读取 + 静态网络托管,访问他们的 S3 托管 URLs 正确显示网站。
Bucket 1 - First App:
index.html
static/js/main.js
Bucket 2 - Second App:
/secondapp/
index.html
static/js/main.js
我为此设置了一个 Cloudfront - 默认的 cloudfront 源正确加载 FirstApp
,这样 www.mywebsite.com 默认加载 index.html。
对于 SecondApp
,我设置了缓存行为,以便路径模式 secondapp/*
指向 SecondApp 存储桶 URL。
在我的浏览器中,当我访问 www.mywebsite.com/secondapp/
时,它会正确显示第二个网络应用程序。
但是,如果我省略尾部的斜线,我反而会看到第一个应用程序,这是不需要的。
如果我访问 www.mywebsite.com/secondapp/something
,我也会看到第一个应用程序,这也是不受欢迎的。 (我希望它加载 secondapp
的 .html)
两个应用程序都配置为通过 react-router-dom.dom.
使用 html5 推送状态
我想要的行为是访问以下显示正确的 site/bucket:
www.mywebsite.com
- 目前工作
www.mywebsite.com/secondapp/
- 目前工作
www.mywebsite.com/secondapp
-(没有尾部斜杠)不工作,显示第一个应用程序
www.mywebsite.com/secondapp/something_else
- 不工作,显示第一个应用程序
我怎样才能达到预期的效果?
谢谢!
在研究了这个问题之后,我能够使用 lambda@edge (https://aws.amazon.com/lambda/edge/)
解决它
通过部署一个简单的 javascript 函数将特定路径路由到所需的 s3 存储桶,我们能够实现类似 nginx 的路由设置。
该函数位于我们的 Cloudfront CDN 上的 lambda@edge 上,这意味着您可以指定 何时 它被触发。对我们来说,它在 "Origin Request"
我的设置如下:
- 我使用了一个 s3 存储桶,并将我的第二个应用程序部署在一个子文件夹中 "second-app"
- 我在 "U.S. East N Virginia" 上创建了一个新的 Lambda 函数。该区域在这里很重要,因为您只能在该区域托管 lambda 函数 @edge。
- 请参阅下面的实际 Lambda 函数
- 创建后,转到您的 CloudFront 配置并转到 "Behaviors > Select the Default (*) path pattern and hit Edit"
- 滚动到底部"Lambda Function Associations"
- Select "Origin Request" 形成下拉列表
- 输入您的 lambda 函数的地址 (
arn:aws:lambda:us-east-1:12345667890:function:my-function-name
)
这是我使用的 lambda 函数的示例。
var path = require('path');
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;
const parsedPath = path.parse(request.uri);
// If there is no extension present, attempt to rewrite url
if (parsedPath.ext === '') {
// 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(/second-app.*/, 'second-app/index.html');
// Replace the received URI with the URI that includes the index page
request.uri = newuri;
}
// If an extension was not present, we are trying to load static access, so allow the request to proceed
// Return to CloudFront
return callback(null, request);
};
这些是我用于此解决方案的资源:
我有两个静态 Web 应用程序 (create-react-apps),它们目前位于两个单独的 S3 存储桶中。两个存储桶都配置为 public 读取 + 静态网络托管,访问他们的 S3 托管 URLs 正确显示网站。
Bucket 1 - First App:
index.html
static/js/main.js
Bucket 2 - Second App:
/secondapp/
index.html
static/js/main.js
我为此设置了一个 Cloudfront - 默认的 cloudfront 源正确加载 FirstApp
,这样 www.mywebsite.com 默认加载 index.html。
对于 SecondApp
,我设置了缓存行为,以便路径模式 secondapp/*
指向 SecondApp 存储桶 URL。
在我的浏览器中,当我访问 www.mywebsite.com/secondapp/
时,它会正确显示第二个网络应用程序。
但是,如果我省略尾部的斜线,我反而会看到第一个应用程序,这是不需要的。
如果我访问 www.mywebsite.com/secondapp/something
,我也会看到第一个应用程序,这也是不受欢迎的。 (我希望它加载 secondapp
的 .html)
两个应用程序都配置为通过 react-router-dom.dom.
使用 html5 推送状态我想要的行为是访问以下显示正确的 site/bucket:
www.mywebsite.com
- 目前工作
www.mywebsite.com/secondapp/
- 目前工作
www.mywebsite.com/secondapp
-(没有尾部斜杠)不工作,显示第一个应用程序
www.mywebsite.com/secondapp/something_else
- 不工作,显示第一个应用程序
我怎样才能达到预期的效果?
谢谢!
在研究了这个问题之后,我能够使用 lambda@edge (https://aws.amazon.com/lambda/edge/)
解决它通过部署一个简单的 javascript 函数将特定路径路由到所需的 s3 存储桶,我们能够实现类似 nginx 的路由设置。 该函数位于我们的 Cloudfront CDN 上的 lambda@edge 上,这意味着您可以指定 何时 它被触发。对我们来说,它在 "Origin Request"
我的设置如下:
- 我使用了一个 s3 存储桶,并将我的第二个应用程序部署在一个子文件夹中 "second-app"
- 我在 "U.S. East N Virginia" 上创建了一个新的 Lambda 函数。该区域在这里很重要,因为您只能在该区域托管 lambda 函数 @edge。
- 请参阅下面的实际 Lambda 函数
- 创建后,转到您的 CloudFront 配置并转到 "Behaviors > Select the Default (*) path pattern and hit Edit"
- 滚动到底部"Lambda Function Associations"
- Select "Origin Request" 形成下拉列表
- 输入您的 lambda 函数的地址 (
arn:aws:lambda:us-east-1:12345667890:function:my-function-name
)
这是我使用的 lambda 函数的示例。
var path = require('path');
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;
const parsedPath = path.parse(request.uri);
// If there is no extension present, attempt to rewrite url
if (parsedPath.ext === '') {
// 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(/second-app.*/, 'second-app/index.html');
// Replace the received URI with the URI that includes the index page
request.uri = newuri;
}
// If an extension was not present, we are trying to load static access, so allow the request to proceed
// Return to CloudFront
return callback(null, request);
};
这些是我用于此解决方案的资源: