Joh*_*ohn 10 amazon-web-services swagger aws-lambda aws-api-gateway openapi
我想使用 AWS CDK 来定义 API 网关和 APIG 将代理到的 lambda。
OpenAPI 规范支持x-amazon-apigateway-integration对 Swagger 规范的自定义扩展(在此处详细说明),为此需要 lambda 的调用 URL。如果 lambda 定义在与 API 相同的堆栈中,我在 OpenAPI 规范中看不到如何提供它。我能想到的最好的方法是使用 lambda 定义一个堆栈,然后从中获取输出并运行sed以在 OpenAPI 规范中执行查找和替换以插入 uri,然后使用此修改创建第二个堆栈OpenAPI 规范。
例子:
/items:
post:
x-amazon-apigateway-integration:
uri: "arn:aws:apigateway:eu-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:eu-west-2:123456789012:function:MyStack-SingletonLambda4677ac3018fa48679f6-B1OYQ50UIVWJ/invocations"
passthroughBehavior: "when_no_match"
httpMethod: "POST"
type: "aws_proxy"
Run Code Online (Sandbox Code Playgroud)
一季度。这似乎是一个鸡与蛋的问题,以上是唯一的方法吗?
我尝试使用SpecRestApi CDK 构造的defaultIntegration属性。该文件指出:
除非指定了集成,否则用作此 API 中创建的所有方法的默认值的集成。
这似乎应该能够使用 CDK 规范中定义的 lambda 定义默认集成,因此所有方法都使用此集成,而无需提前知道 lambda 的 uri。
因此我试过这个:
SingletonFunction myLambda = ...
SpecRestApi openapiRestApi = SpecRestApi.Builder.create(this, "MyApi")
.restApiName("MyApi")
.apiDefinition(ApiDefinition.fromAsset("openapi.yaml"))
.defaultIntegration(LambdaIntegration.Builder.create(myLambda)
.proxy(false)
.build())
.deploy(true)
.build();
Run Code Online (Sandbox Code Playgroud)
中定义的 OpenAPI 规范openapi.yaml不包括x-amazon-apigateway-integration节;它只有一个在标准 OpenApi 3 规范中定义的 GET 方法。
但是,当我尝试部署它时,出现错误:
No integration defined for method (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: 56113150-1460-4ed2-93b9-a12618864582)
Run Code Online (Sandbox Code Playgroud)
这似乎是一个错误,所以我在这里提交了一个。
Q2。如何使用 CDK 定义 API 网关和 Lambda 并通过 OpenAPI 规范将两者连接在一起?
小智 12
有一个现有的解决方法。具体方法如下:
您的 OpenAPI 文件必须如下所示:
openapi: "3.0.1"
info:
title: "The Super API"
description: "API to do super things"
version: "2019-09-09T12:56:55Z"
servers:
- url: ""
variables:
basePath:
default:
Fn::Sub: ${ApiStage}
paths:
/path/subpath:
get:
parameters:
- name: "Password"
in: "header"
schema:
type: "string"
responses:
200:
description: "200 response"
content:
application/json:
schema:
$ref: "#/components/schemas/UserConfigResponseModel"
security:
- sigv4: []
x-amazon-apigateway-integration:
uri:
Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${MySuperLambda.Arn}/invocations"
responses:
default:
statusCode: "200"
requestTemplates:
application/json: "{blablabla}"
passthroughBehavior: "when_no_match"
httpMethod: "POST"
type: "aws"
Run Code Online (Sandbox Code Playgroud)
如您所见,此 OpenAPI 模板引用了ApiStage、AWS::Region和MySuperLambda.Arn。
关联的 cdk 文件包含以下内容:
// To pass external string, nothing better than this hacky solution:
const ApiStage = new CfnParameter(this, 'ApiStage',{type: 'String', default: props.ApiStage})
ApiStage.overrideLogicalId('ApiStage')
Run Code Online (Sandbox Code Playgroud)
这里在 props 中使用了ApiStage 。例如,它允许我在 CI 期间使用环境变量将其传递给 cdk 应用程序。
const MySuperLambda = new lambda.Function(this, 'MySuperLambda', {
functionName: "MySuperLambda",
description: "Hello world",
runtime: lambda.Runtime.PYTHON_3_7,
code: lambda.Code.asset(lambda_asset),
handler: "MySuperLambda.lambda_handler",
timeout: cdk.Duration.seconds(30),
memorySize: 128,
role: MySuperLambdaRole
});
const forceLambdaId = MySuperLambda.node.defaultChild as lambda.CfnFunction
forceLambdaId.overrideLogicalId('MySuperLambda')
Run Code Online (Sandbox Code Playgroud)
在这里,和以前一样,我强制 CDK 覆盖逻辑 id,以便我在部署之前知道该 id。否则,cdk 会向逻辑 ID 添加后缀。
const asset = new Asset(this, 'SampleAsset', {
path: './api-gateway-definitions/SuperAPI.yml',
});
Run Code Online (Sandbox Code Playgroud)
这允许我将 OpenAPI 文件直接上传到 cdk 存储桶(无需创建新的存储桶,这太棒了)。
const data = Fn.transform('AWS::Include', {'Location': asset.s3ObjectUrl})
Run Code Online (Sandbox Code Playgroud)
这是 Cloudformation 魔法的一部分。这是解释 Fn::Sub 和 Fn::GetAtt 的地方。我无法使其与 !Ref 函数一起使用。
const SuperApiDefinition = apigateway.AssetApiDefinition.fromInline(data)
Run Code Online (Sandbox Code Playgroud)
从之前读取的文件创建 api 定义。
const sftpApiGateway = new apigateway.SpecRestApi(this, 'superAPI', {
apiDefinition: SuperApiDefinition,
deploy: false
})
Run Code Online (Sandbox Code Playgroud)
最后,创建 SpecRestApi。奔跑并施展魔法,这行得通。您可能仍然会遇到 400 错误,可能是因为 OpenAPI 文件中的格式不正确(并且不要使用 !Ref)。
我会推荐这个吗?嗯。这几乎是一个解决方法。如果您想在 CI 中使用带有动态变量的 OpenAPI 格式,那么它非常有用。无需太多努力,只需切换 1 个环境变量即可在 dev 和 prod 中部署。
然而,这感觉真的很hacky并且似乎不符合CDK的理念。这是我目前用于部署的内容,但将来可能会改变。我相信真正的模板解决方案可能更适合这里,但现在,我并没有真正考虑过它。
看起来我所追求的内容是由这个 CDK 问题跟踪的。与此同时,我根据此处关于该问题的评论提出了解决方法。
我使用https://github.com/spullara/mustache.java解析我的 OpenAPI 规范文件并替换其中引用 API 网关的调用 ARN(其本身引用 Lambda ARN)的模板值。
Map<String, Object> variables = new HashMap<>();
variables.put("restapi-lambda", String.format("arn:aws:apigateway:%s:lambda:path/2015-03-31/functions/%s/invocations", props.getEnv().getRegion(), myLambda.getFunctionArn()));
Writer writer = new StringWriter();
MustacheFactory mf = new DefaultMustacheFactory();
Object openapiSpecAsObject;
try (Reader reader = new FileReader(new File("myapi.yaml"))) {
Mustache mustache = mf.compile(reader, "OAS");
mustache.execute(writer, scopes);
writer.flush();
ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory());
openapiSpecAsObject = yamlMapper.readValue(writer.toString(), Object.class);
}
SpecRestApi openapiRestApi = SpecRestApi.Builder.create(this, "MyRestApi")
.restApiName("MyRestApi")
.apiDefinition(ApiDefinition.fromInline(openapiSpecAsObject))
.deploy(true)
.build();
Run Code Online (Sandbox Code Playgroud)
请注意,props是一个引用Stackprops 的变量,并且myLambda是对 a 的引用SingletonFunction。
我的 OpenAPI 规范如下所示(删除了标头和模型部分):
paths:
/items:
get:
summary: List all items.
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/ItemList'
x-amazon-apigateway-integration:
uri: "{{restapi-lambda}}"
passthroughBehavior: "when_no_match"
httpMethod: "POST"
type: "aws_proxy"
Run Code Online (Sandbox Code Playgroud)
另请注意,当我授予 API Gateway 权限来调用 lambda 时,如下所示:
myLambda.grantInvoke(ServicePrincipal.Builder.create("apigateway.amazonaws.com")
.build());
Run Code Online (Sandbox Code Playgroud)
我仍然收到 500 错误,并且在日志中我可以看到“Lambda 函数的权限无效”错误消息。如果我向 Lambda 添加权限,如下所示:
myLambda.addPermission("PermitAPIGInvocation", Permission.builder()
.action("lambda:InvokeFunction")
.principal(ServicePrincipal.Builder.create("apigateway.amazonaws.com")
.build())
.sourceArn(openapiRestApi.arnForExecuteApi())
.build());
Run Code Online (Sandbox Code Playgroud)
那么我目前需要在权限生效之前重新部署API。我仍在研究如何避免这种情况。
| 归档时间: |
|
| 查看次数: |
4979 次 |
| 最近记录: |