这是 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
界面。您很可能只会看到字符串编码版本的令牌。
其他函数通常只接受基本类型的参数,例如string
或number
。要在这些情况下使用标记,您可以在 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
});