简体   繁体   English

在 CDK 中跨不同堆栈共享 API 网关端点 URL

[英]Sharing API gateway endpoint URL across different stacks in CDK

I have following AWS CDK backed solution:我有以下 AWS CDK 支持的解决方案:

  1. Static S3 based webpage which communicates with Static 基于 S3 的网页,与
  2. API Gateway which then sends data to API 然后将数据发送到的网关
  3. AWS lambda. AWS lambda。

The problem is that S3 page needs to be aware of API gateway endpoint URL. Obviously this is not achievable within the same CDK stack.问题是 S3 页面需要知道 API 网关端点 URL。显然,这在同一个 CDK 堆栈中是无法实现的。 So I have defined two stacks:所以我定义了两个堆栈:

  1. Backend (API gateway + lambda)后端(API 网关 + lambda)
  2. Frontend (S3 based static webpage)前端(基于 S3 的 static 网页)

They are linked as dependant in CDK code:它们在 CDK 代码中被链接为依赖项:

    const app = new cdk.App();
    const backStack = new BackendStack(app, 'Stack-back', {...});
    new FrontendStack(app, 'Stack-front', {...}).addDependency(backStack, "API URL from backend is needed");

I try to share URL as follows.我尝试如下分享URL。 Code from backend stack definition:来自后端堆栈定义的代码:

      const api = new apiGW.RestApi(this, 'MyAPI', {
          restApiName: 'My API',
          description: 'This service provides interface towards web app',
          defaultCorsPreflightOptions: {
              allowOrigins: apiGW.Cors.ALL_ORIGINS,
          }
      });
    
      api.root.addMethod("POST", lambdaIntegration); 
    
      new CfnOutput(this, 'ApiUrlRef', {
          value: api.url,
          description: 'API Gateway URL',
          exportName: 'ApiUrl',
      });

Code from frontend stack definition:来自前端堆栈定义的代码:

    const apiUrl = Fn.importValue('ApiUrl');

Unfortunately, instead of URL I get token (${Token[TOKEN.256]}).不幸的是,我得到的不是 URL 令牌(${Token[TOKEN.256]})。 At the same time, I see URL is resolved in CDK generated files:同时,我看到 URL 在 CDK 生成的文件中被解析:

./cdk.out/Stack-back.template.json: ./cdk.out/Stack-back.template.json:

    "ApiUrlRef": {
      "Description": "API Gateway URL",
      "Value": {
        "Fn::Join": [
          "",
          [
            "https://",
            {
              "Ref": "MyAPI7DAA778AA"
            },
            ".execute-api.us-west-1.",
            {
              "Ref": "AWS::URLSuffix"
            },
            "/",
            {
              "Ref": "MyAPIDeploymentStageprodA7777A7A"
            },
            "/"
          ]
        ]
      },
      "Export": {
        "Name": "ApiUrl"
      }
    }
  },

What I'm doing wrong?我做错了什么?

UPD:更新:

After advice of fedonev to pass data as props, situation did not changed much.在 fedonev 建议将数据作为 props 传递之后,情况并没有太大变化。 Now url looks like that:现在 url 看起来像这样:

"https://${Token[TOKEN.225]}.execute-api.us-west-1.${Token[AWS.URLSuffix.3]}/${Token[TOKEN.244]}/"

I think important part I missed (which was also pointed by我认为我错过了重要的部分(这也被指出
Milan Gatyas) is how I create HTML with URL of gateway. Milan Gatyas) 是我用 URL 网关创建 HTML 的方法。

In my frontend-stack.ts, I use template file.在我的 frontend-stack.ts 中,我使用模板文件。 After template is filled, I store it in S3:填充模板后,我将其存储在 S3 中:

      const filledTemplatePath: string = path.join(processedWebFileDir,'index.html');
      const webTemplate: string = fs.readFileSync(filledTemplatePath, 'utf8')
      const Handlebars = require("handlebars")
      let template = Handlebars.compile(webTemplate)
      const adjustedHtml: string = template({ apiGwEndpoint: apiUrl.toString() })
      fs.writeFileSync(filledTemplatePath, adjustedHtml)

      // bucket
      const bucket: S3.Bucket = new S3.Bucket(this, "WebsiteBucket", 
      {
          bucketName: 'frontend',
          websiteIndexDocument: 'index.html',
          websiteErrorDocument: 'error.html',
          publicReadAccess: true,

      })

      new S3Deploy.BucketDeployment(this, 'DeployWebsite', {
          sources: [S3Deploy.Source.asset(processedWebFileDir)],
          destinationBucket: bucket,
      });

(I'm new to TS and web, please don't judge much:) ) (我是TS和web的新手,请不要多评判:))

Am I correct that S3 is populated on synth, deploy does not change anything and this is why I get tokens in html?我是否正确认为 S3 是在合成器上填充的,部署不会改变任何东西,这就是我在 html 中获得令牌的原因? Will be grateful for a link or explanation so that I could understand the process better, there are so much new information to me that some parts are still quite foggy.将不胜感激链接或解释,以便我可以更好地理解该过程,对我来说有太多新信息以至于某些部分仍然很模糊。

[Edit: rewrote the answer to reflect updates to the OP] [编辑:重写答案以反映对 OP 的更新]

Am I correct that S3 is populated on synth, deploy does not change anything and this is why I get tokens in html?我是否正确认为 S3 是在合成器上填充的,部署不会改变任何东西,这就是我在 html 中获得令牌的原因?

Yes.是的。 The API URL will resolve only at deploy-time . API URL 将仅在部署时解析。 You are trying to consume it at synth-time when you write to the template file.当您写入模板文件时,您正试图在合成时使用它。 At synth-time, CDK represents not-yet-available values as Tokens like ${Token[TOKEN.256]} , the CDK's clever way of handling such deferred values.在合成时,CDK 将尚未可用的值表示为令牌,如${Token[TOKEN.256]} ,这是 CDK 处理此类延迟值的巧妙方法。

What I'm doing wrong?我做错了什么?

You need to defer the consumption of API URL until its value is resolved (= until the API is deployed).您需要推迟 API URL 的消耗,直到它的值被解析(=直到 API 被部署)。 In most cases, passing constructs as props between stacks is the right approach.在大多数情况下, 将构造作为 props 在堆栈之间传递是正确的方法。 But not in your case: you want to inject the URL into the template file.但不是你的情况:你想将 URL 注入到模板文件中。 As usual with AWS, you have many options:与 AWS 一样,您有很多选择:

  1. Split the stacks into separate apps, deployed separately.将堆栈拆分为单独的应用程序,单独部署。 Deploy BackendStack .部署BackendStack Hardcode the url into FrontendStack .将 url 硬编码到FrontendStack中。 Quick and dirty.又快又脏。
  2. Instead of S3, use Amplify front-end hosting , which can expose the URL to your template as an environment variable.使用Amplify front-end hosting代替 S3,它可以将 URL 作为环境变量公开给您的模板。 Beginner friendly, has CDK support .初学者友好,有CDK 支持
  3. Add a CustomResource construct, which would be backed by a Lambda that writes the URL to the template file as part of the deploy lifecycle.添加一个CustomResource构造,它将由 Lambda 支持,该构造将 URL 作为部署生命周期的一部分写入模板文件。 This solution is elegant but not newbie-friendly.这个解决方案很优雅,但对新手不友好。
  4. Use a Pipeline to inject the URL variable as a build step during deploy.使用管道将 URL 变量作为部署期间的构建步骤注入。 Another advanced approach.另一种先进的方法。

As @fedonev mentioned, the tokens are just placeholder values in the TypeScript application.正如@fedonev 所提到的,令牌只是 TypeScript 应用程序中的占位符值。 CDK app replaces tokens with intrinsic functions when the CloudFormation template is produced. CDK 应用程序在生成 CloudFormation 模板时将令牌替换为内部函数。

However, your use case is different.但是,您的用例不同。 You try to know the information inside the CDK app which is available only at synthesis time, and you can't use the intrinsic function to resolve the URL while being in CDK app to write to file.您尝试了解 CDK 应用程序内部的信息,这些信息仅在综合时可用,并且您不能在 CDK 应用程序写入文件时使用内部 function 解析 URL。

If possible you can utilize the custom domain for the API Gateway.如果可能,您可以使用 API 网关的自定义域。 Then you can work with beforehand known custom domain in your static file and assign the custom domain to the API Gateway in your CDK App.然后,您可以在 static 文件中使用事先已知的自定义域,并将自定义域分配给 CDK 应用程序中的 API 网关。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM