AWS CDK 将 API Gateway URL 传递到同一堆栈中的静态站点
Posted
技术标签:
【中文标题】AWS CDK 将 API Gateway URL 传递到同一堆栈中的静态站点【英文标题】:AWS CDK passing API Gateway URL to static site in same Stack 【发布时间】:2020-05-21 07:03:12 【问题描述】:我正在尝试在单个堆栈中部署 S3 静态网站和 API 网关/lambda。
S3 静态站点中的 javascript 调用 lambda 以填充 html 列表,但它需要知道用于 lambda 集成的 API Gateway URL。
目前,我像这样生成一个 RestApi...
const handler = new lambda.Function(this, "TestHandler",
runtime: lambda.Runtime.NODEJS_10_X,
code: lambda.Code.asset("build/test-service"),
handler: "index.handler",
environment:
);
this.api = new apigateway.RestApi(this, "test-api",
restApiName: "Test Service"
);
const getIntegration = new apigateway.LambdaIntegration(handler,
requestTemplates: "application/json": ' "statusCode": "200" '
);
const apiUrl = this.api.url;
但是在 cdk deploy 上,apiUrl =
"https://$Token[TOKEN.39].execute-api.$Token[AWS::Region.4].$Token[AWS::URLSuffix.1]/$Token[TOKEN.45]/"
因此,在静态站点需要该值之前,不会解析/生成 url。
如何计算/查找/获取 API Gateway URL 并更新 cdk deploy 上的 javascript?
或者有更好的方法吗?即静态javascript是否有一种优雅的方式来检索lambda api网关url?
谢谢。
【问题讨论】:
只是为了确保,您想创建一个新的 api 网关,然后使用 url ? 是的,我正在研究创建一个名为 config.json 的 S3Object,其内容为 "apiurl" : !Sub "https://$restApiId.execute-api.$AWS: :Region.amazonaws.com/$apiGatewayStageName" 但我无法在 cdk 部署期间获取 restApiId。 “稍后使用 url”是指在堆栈部署的后期,我想用 url 值填充 S3 文件。 @benito_h 看看我的回答:***.com/a/61580083/9931092 @Tim 你能找到解决方案吗?正在考虑使用 url 作为参数创建一个自定义资源来创建该对象,但很想听听是否有更优雅的解决方案。 看到他们正在关闭您的问题 @alex9311,我在 github.com/aws/aws-cdk/issues/12903 提出了一个功能请求,可能更符合 CDK 的愿景 【参考方案1】:您正在创建一个 LambdaIntegration,但它没有连接到您的 API。
要将其添加到 API 的根目录,请执行以下操作:this.api.root.addMethod(...) 并使用它来连接您的 LambdaIntegration 和 API。
这应该会给你一个带有 URL 的端点
【讨论】:
【参考方案2】:如果您也使用s3-deployment
模块来部署您的网站,我能够使用当前可用的解决方案(在https://github.com/aws/aws-cdk/issues/12903 等待更好的解决方案)。下面一起允许您将config.js
部署到您的存储桶(包含仅在部署时填充的堆栈中的属性),然后您可以在运行时在代码中的其他地方依赖它。
在inline-source.ts
:
// imports removed for brevity
export function inlineSource(path: string, content: string, options?: AssetOptions): ISource
return
bind: (scope: Construct, context?: DeploymentSourceContext): SourceConfig =>
if (!context)
throw new Error('To use a inlineSource, context must be provided');
// Find available ID
let id = 1;
while (scope.node.tryFindChild(`InlineSource$id`))
id++;
const bucket = new Bucket(scope, `InlineSource$idStagingBucket`,
removalPolicy: RemovalPolicy.DESTROY
);
const fn = new Function(scope, `InlineSource$idLambda`,
runtime: Runtime.NODEJS_12_X,
handler: 'index.handler',
code: Code.fromAsset('./inline-lambda')
);
bucket.grantReadWrite(fn);
const myProvider = new Provider(scope, `InlineSource$idProvider`,
onEventHandler: fn,
logRetention: RetentionDays.ONE_DAY // default is INFINITE
);
const resource = new CustomResource(scope, `InlineSource$idCustomResource`, serviceToken: myProvider.serviceToken, properties: bucket: bucket.bucketName, path, content );
context.handlerRole.node.addDependency(resource); // Sets the s3 deployment to depend on the deployed file
bucket.grantRead(context.handlerRole);
return
bucket: bucket,
zipObjectKey: 'index.zip'
;
,
;
在inline-lambda/index.js
中(还需要将归档器安装到 inline-lambda/node_modules 中):
const aws = require('aws-sdk');
const s3 = new aws.S3( apiVersion: '2006-03-01' );
const fs = require('fs');
var archive = require('archiver')('zip');
exports.handler = async function(event, ctx)
await new Promise(resolve => fs.unlink('/tmp/index.zip', resolve));
const output = fs.createWriteStream('/tmp/index.zip');
const closed = new Promise((resolve, reject) =>
output.on('close', resolve);
output.on('error', reject);
);
archive.pipe(output);
archive.append(event.ResourceProperties.content, name: event.ResourceProperties.path );
archive.finalize();
await closed;
await s3.upload(Bucket: event.ResourceProperties.bucket, Key: 'index.zip', Body: fs.createReadStream('/tmp/index.zip')).promise();
return;
在你的构造中,使用inlineSource
:
export class TestConstruct extends Construct
constructor(scope: Construct, id: string, props: any)
// set up other resources
const source = inlineSource('config.js', `exports.config = apiEndpoint: '$ api.attrApiEndpoint ' `);
// use in BucketDeployment
您可以将inline-lambda
移动到其他地方,但它需要能够作为 lambda 的资产进行捆绑。
这是通过创建一个自定义资源来实现的,该资源依赖于堆栈中的其他资源(从而允许解析属性),该资源将您的文件写入一个 zip,然后存储到一个存储桶中,然后将其拾取并解压缩到您的部署/目标存储桶中。相当复杂,但可以利用当前可用的东西完成工作。
【讨论】:
【参考方案3】:我成功使用的模式是将 CloudFront 分配或 API 网关放在 S3 存储桶前面。
所以对https://[api-gw]/**/*
的请求被代理到https://[s3-bucket]/**/*
。
然后我将在同一个 API 网关中创建一个新的代理路径,用于名为 /config
的路由,它是一个标准的 Lambda 支持的 API 端点,我可以在其中将品牌信息或 API 密钥等各种东西返回到前端,每当前端调用GET /config
。
此外,这还避免了诸如 CORS 之类的问题,因为两个来源是相同的(API 网关域)。
使用 CloudFront 分发而不是 API 网关,它几乎相同,只是您使用 CloudFront 分发的“来源”配置而不是路径和方法。
【讨论】:
【参考方案4】:处理此问题的正确方法是使用您的 API url 创建一个 CfnOutput,如下所示:
new cdk.CfnOutput(this, 'apiUrl',
value: this.api.url!,
);
【讨论】:
问题是作者想在同一个栈中使用那个值,使用一个输出会需要你使用多个栈以上是关于AWS CDK 将 API Gateway URL 传递到同一堆栈中的静态站点的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 OpenAPI 为 AWS API Gateway 配置 CORS?
使用 AWS CDK 为 AWS API 网关启用 CORS
AWS - 使用 @connections websocket 回调 url 从后端发送响应(单向) - API Gateway websocket 协议