代币和 AWS CDK - AWS Cloud Development Kit (AWS CDK) v2

这是 AWS CDK v2 开发者指南。较旧的 CDK v1 于 2022 年 6 月 1 日进入维护阶段,并于 2023 年 6 月 1 日终止支持。

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

代币和 AWS CDK

令牌表示只能在应用程序生命周期的稍后时间解析的值。例如,只有在合成模板时,才会分配CDK您在应用程序中定义的亚马逊简单存储服务 (Amazon S3) 存储桶 AWS CloudFormation 的名称。如果你打印这个bucket.bucketName属性(一个字符串),你会看到它包含如下内容:

${TOKEN[Bucket.Name.1234]}

这就是对代币进行 AWS CDK 编码的方式,该代币的价值在构造时尚不清楚,但稍后将可用。它们 AWS CDK 调用这些占位符标记。在本例中,它是编码为字符串的标记。

你可以像存储桶的名称一样传递这个字符串。在以下示例中,存储桶名称被指定为 AWS Lambda 函数的环境变量。

TypeScript
const bucket = new s3.Bucket(this, 'amzn-s3-demo-bucket'); const fn = new lambda.Function(stack, 'MyLambda', { // ... environment: { BUCKET_NAME: bucket.bucketName, } });
JavaScript
const bucket = new s3.Bucket(this, 'amzn-s3-demo-bucket'); const fn = new lambda.Function(stack, 'MyLambda', { // ... environment: { BUCKET_NAME: bucket.bucketName } });
Python
bucket = s3.Bucket(self, "amzn-s3-demo-bucket") fn = lambda_.Function(stack, "MyLambda", environment=dict(BUCKET_NAME=bucket.bucket_name))
Java
final Bucket bucket = new Bucket(this, "amzn-s3-demo-bucket"); Function fn = Function.Builder.create(this, "MyLambda") .environment(java.util.Map.of( // Map.of requires Java 9+ "BUCKET_NAME", bucket.getBucketName())) .build();
C#
var bucket = new s3.Bucket(this, "amzn-s3-demo-bucket"); var fn = new Function(this, "MyLambda", new FunctionProps { Environment = new Dictionary<string, string> { ["BUCKET_NAME"] = bucket.BucketName } });

当 AWS CloudFormation 模板最终合成时,标记将呈现为 AWS CloudFormation 内在函数{ "Ref": "amzn-s3-demo-bucket" }。在部署时, AWS CloudFormation 将此内部函数替换为创建的存储桶的实际名称。

代币和代币编码

令牌是实现IResolvable接口的对象,该接口包含一个resolve方法。在合成过程中 AWS CDK 调用此方法以生成 AWS CloudFormation 模板的最终值。代币参与合成过程以生成任何类型的任意值。

注意

你很少会直接使用该IResolvable界面。您很可能只会看到字符串编码版本的令牌。

其他函数通常只接受基本类型的参数,例如stringnumber。要在这些情况下使用标记,您可以在 CDK .Token 类上使用静态方法将它们编码为三种类型之一。

它们采用任意值(可以是)IResolvable,然后将它们编码为指定类型的原始值。

重要

由于前面的任何一种类型都可能是编码标记,因此在解析或尝试读取其内容时要小心。例如,如果您尝试解析字符串以从中提取值,而该字符串是编码标记,则解析将失败。同样,如果您尝试查询数组的长度或使用数字执行数学运算,则必须首先验证它们不是经过编码的标记。

要检查值中是否有未解析的标记,请调用 Token.isUnresolved (Python:is_unresolved) 方法。

以下示例验证字符串值(可能是标记)的长度是否不超过 10 个字符。

TypeScript
if (!Token.isUnresolved(name) && name.length > 10) { throw new Error(`Maximum length for name is 10 characters`); }
JavaScript
if ( !Token.isUnresolved(name) && name.length > 10) { throw ( new Error(`Maximum length for name is 10 characters`)); }
Python
if not Token.is_unresolved(name) and len(name) > 10: raise ValueError("Maximum length for name is 10 characters")
Java
if (!Token.isUnresolved(name) && name.length() > 10) throw new IllegalArgumentException("Maximum length for name is 10 characters");
C#
if (!Token.IsUnresolved(name) && name.Length > 10) throw new ArgumentException("Maximum length for name is 10 characters");

如果 name 是标记,则不执行验证,并且在生命周期的后期阶段(例如部署期间)仍可能发生错误。

注意

您可以使用令牌编码来逃避类型系统。例如,您可以对在合成时生成数字值的标记进行字符串编码。如果您使用这些函数,则您有责任确保您的模板在合成后解析为可用状态。

字符串编码的标记

字符串编码的标记如下所示。

${TOKEN[Bucket.Name.1234]}

它们可以像常规字符串一样传递,也可以串联,如以下示例所示。

TypeScript
const functionName = bucket.bucketName + 'Function';
JavaScript
const functionName = bucket.bucketName + 'Function';
Python
function_name = bucket.bucket_name + "Function"
Java
String functionName = bucket.getBucketName().concat("Function");
C#
string functionName = bucket.BucketName + "Function";

如果您的语言支持,也可以使用字符串插值,如以下示例所示。

TypeScript
const functionName = `${bucket.bucketName}Function`;
JavaScript
const functionName = `${bucket.bucketName}Function`;
Python
function_name = f"{bucket.bucket_name}Function"
Java
String functionName = String.format("%sFunction". bucket.getBucketName());
C#
string functionName = $"${bucket.bucketName}Function";

避免以其他方式操纵字符串。例如,取一个字符串的子字符串很可能会破坏字符串标记。

列表编码的代币

列表编码的代币如下所示:

["#{TOKEN[Stack.NotificationArns.1234]}"]

处理这些列表的唯一安全方法是将它们直接传递给其他构造。不能连接字符串列表形式的标记,也不能从令牌中提取元素。操作它们的唯一安全方法是使用诸如 fn.Select 之类的 AWS CloudFormation 内部函数。

数字编码的代币

数字编码的代币是一组微小的负浮点数,如下所示。

-1.8881545897087626e+289

与列表标记一样,您无法修改数字值,因为这样做可能会破坏数字标记。唯一允许的操作是将值传递给另一个构造。

懒惰的值

除了表示部署时间值(例如 AWS CloudFormation 参数)外,令牌还通常用于表示合成时的延迟值。这些值的最终值将在合成完成之前确定,但不会在构造值时确定。使用标记将文字字符串或数字值传递给另一个构造,而合成时的实际值可能取决于尚未进行的某些计算。

你可以使用Lazy类上的静态方法(例如 lazy.String 和 Laz y.Number)来构造表示合成时延迟值的标记。这些方法接受一个对象,该对象的produce属性是一个接受上下文参数并在调用时返回最终值的函数。

以下示例创建一个 Auto Scaling 组,其容量是在创建后确定的。

TypeScript
let actualValue: number; new AutoScalingGroup(this, 'Group', { desiredCapacity: Lazy.numberValue({ produce(context) { return actualValue; } }) }); // At some later point actualValue = 10;
JavaScript
let actualValue; new AutoScalingGroup(this, 'Group', { desiredCapacity: Lazy.numberValue({ produce(context) { return (actualValue); } }) }); // At some later point actualValue = 10;
Python
class Producer: def __init__(self, func): self.produce = func actual_value = None AutoScalingGroup(self, "Group", desired_capacity=Lazy.number_value(Producer(lambda context: actual_value)) ) # At some later point actual_value = 10
Java
double actualValue = 0; class ProduceActualValue implements INumberProducer { @Override public Number produce(IResolveContext context) { return actualValue; } } AutoScalingGroup.Builder.create(this, "Group") .desiredCapacity(Lazy.numberValue(new ProduceActualValue())).build(); // At some later point actualValue = 10;
C#
public class NumberProducer : INumberProducer { Func<Double> function; public NumberProducer(Func<Double> function) { this.function = function; } public Double Produce(IResolveContext context) { return function(); } } double actualValue = 0; new AutoScalingGroup(this, "Group", new AutoScalingGroupProps { DesiredCapacity = Lazy.NumberValue(new NumberProducer(() => actualValue)) }); // At some later point actualValue = 10;

正在转换为 JSON

有时你想生成一JSON串任意数据,但你可能不知道这些数据是否包含标记。要正确地对任何数据结构进行JSON编码,无论其是否包含标记,请使用方法堆栈。 toJsonString,如以下示例所示。

TypeScript
const stack = Stack.of(this); const str = stack.toJsonString({ value: bucket.bucketName });
JavaScript
const stack = Stack.of(this); const str = stack.toJsonString({ value: bucket.bucketName });
Python
stack = Stack.of(self) string = stack.to_json_string(dict(value=bucket.bucket_name))
Java
Stack stack = Stack.of(this); String stringVal = stack.toJsonString(java.util.Map.of( // Map.of requires Java 9+ put("value", bucket.getBucketName())));
C#
var stack = Stack.Of(this); var stringVal = stack.ToJsonString(new Dictionary<string, string> { ["value"] = bucket.BucketName });