AWS CDK 将 API 网关 URL 传递到同一堆栈中的静态站点



我正在尝试在单个堆栈中部署 S3 静态网站和 API 网关/lambda。

S3 静态站点中的 javascript 调用 lambda 来填充 HTML 列表,但它需要知道 lambda 集成的 API 网关 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 部署中,apiUrl =

"https://${Token[TOKEN.39]}.execute-api.${Token[AWS::Region.4]}.${Token[AWS::URLSuffix.1]}/${Token[TOKEN.45]}/"

因此,直到静态站点需要该值之后,才会解析/生成 url。

如何计算/查找/获取 API 网关 URL 并在 cdk 部署上更新 javascript?

或者有更好的方法可以做到这一点吗? 即静态JavaScript是否有一种优雅的方式来检索Lambda API网关URL?

谢谢。

我成功使用的模式是将 CloudFront 分配或 API 网关放在 S3 存储桶前面。

因此,对https://[api-gw]/**/*的请求被代理到https://[s3-bucket]/**/*.

然后,我将在同一 API 网关中为名为/config的路由创建新的代理路径,该路由是标准 Lambda 支持的 API 终端节点,只要前端调用GET /config,我就可以在其中向前端返回各种内容,例如品牌信息或 API 密钥。

此外,这避免了 CORS 等问题,因为两个源是相同的(API 网关域(。

使用 CloudFront 分配而不是 API 网关,情况几乎相同,只是您使用 CloudFront 分配的"源"配置,而不是路径和方法。

您正在创建一个 LambdaIntegration,但它未连接到您的 API。

要将其添加到 API 的根目录,请执行以下操作:this.api.root.addMethod(...(,并使用它来连接您的 LambdaIntegration 和 API。

这应该为您提供一个带有 URL 的终结点

如果您也使用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${id}StagingBucket`, {
removalPolicy: RemovalPolicy.DESTROY
});

const fn = new Function(scope, `InlineSource${id}Lambda`, {
runtime: Runtime.NODEJS_12_X,
handler: 'index.handler',
code: Code.fromAsset('./inline-lambda')
});

bucket.grantReadWrite(fn);

const myProvider = new Provider(scope, `InlineSource${id}Provider`, {
onEventHandler: fn,
logRetention: RetentionDays.ONE_DAY   // default is INFINITE
});

const resource = new CustomResource(scope, `InlineSource${id}CustomResource`, { 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 然后存储到存储桶中,然后将其提取并解压缩到您的部署/目标存储桶中。相当复杂,但可以使用当前可用的功能完成工作。

最新更新