AWS CDK将API网关URL传递到同一堆栈中的静态站点 [英] AWS CDK passing API Gateway URL to static site in same Stack

查看:68
本文介绍了AWS CDK将API网关URL传递到同一堆栈中的静态站点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to deploy an S3 static website and API gateway/lambda in a single stack.

S3静态站点中的javascript调用lambda来填充HTML列表,但它需要知道用于lambda集成的API网关URL.

The javascript in the S3 static site calls the lambda to populate an HTML list but it needs to know the API Gateway URL for the lambda integration.

当前,我像这样生成RestApi ...

Currently, I generate a RestApi like so...

    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 =

But on cdk deploy, apiUrl =

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

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

So the url is not parsed/generated until after the static site requires the value.

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

How can I calculate/find/fetch the API Gateway URL and update the javascript on cdk deploy?

还是有更好的方法来做到这一点?也就是说,静态javascript是否有一种优美的方式来检索lambda api网关网址?

Or is there a better way to do this? i.e. is there a graceful way for the static javascript to retrieve a lambda api gateway url?

谢谢.

推荐答案

如果您还使用 s3-deployment 模块来部署您的网站,那么我能够使用当前可用(在 https://github.com/aws/aws上寻求更好的解决方案-cdk/issues/12903 ).以下内容一起使您可以将 config.js 部署到存储桶(包含来自堆栈的属性,这些属性仅在部署时填充),然后可以在运行时依赖于代码中的其他位置.

If you are using the s3-deployment module to deploy your website as well, I was able to hack together a solution using what is available currently (pending a better solution at https://github.com/aws/aws-cdk/issues/12903). The following together allow for you to deploy a config.js to your bucket (containing attributes from your stack that will only be populated at deploy time) that you can then depend on elsewhere in your code at runtime.

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中):

In inline-lambda/index.js (also requires archiver installed into 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的资产.

You can move inline-lambda elsewhere but it needs to be able to be bundled as an asset for the lambda.

这可以通过创建依赖于堆栈中其他资源的自定义资源(从而允许解析属性)来工作,该自定义资源将文件写入zip文件中,然后存储到存储桶中,然后存储桶中解压缩到您的部署/目标存储桶中.相当复杂,但是可以使用当前可用的功能来完成工作.

This works by creating a custom resource that depends on your other resources in the stack (thereby allowing for the attributes to be resolved) that writes your file into a zip that is then stored into a bucket, which is then picked up and unzipped into your deployment/destination bucket. Pretty complicated but gets the job done with what is currently available.

这篇关于AWS CDK将API网关URL传递到同一堆栈中的静态站点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆