我想使用 AWS CDK 来定义 API 网关和 APIG 将代理到的 lambda。
OpenAPI规范支持Swagger规范的x-amazon-apigateway-集成
自定义扩展(详见此处),为此需要lambda的调用URL。如果lambda与API在同一个堆栈中定义,我看不出如何在OpenAPI规范中提供此内容。我能想到的最好的办法是定义一个包含lambda的堆栈,然后从中获取输出并运行sed
在OpenAPI规范中执行查找和替换以插入uri,然后使用此html" target="_blank">修改后的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"
这似乎是一个先有鸡还是先有蛋的问题,以上是唯一的方法吗?
我尝试使用SpecRestApi CDK构造的< code>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();
在 openapi.yaml
中定义的开放API规范不包括 x-amazon-apigateway-集成
节;它只有一个在标准OpenApi 3规范中定义的GET方法。
然而,当我尝试部署时,我得到了一个错误:
No integration defined for method (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: 56113150-1460-4ed2-93b9-a12618864582)
这似乎是一个错误,所以我在这里提交了一个。
Q2。如何使用CDK定义API网关和Lambda,并通过OpenAPI规范将两者连接在一起?
看起来我所追求的是这个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();
请注意,props
是一个变量,它引用了堆栈
prop,myLambda
,它引用了一个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"
还要注意,当我授予API网关权限以调用lambda时,如下所示:
myLambda.grantInvoke(ServicePrincipal.Builder.create("apigateway.amazonaws.com")
.build());
我仍然得到一个500错误,在日志中我可以看到一条“Lambda函数上的无效权限”错误消息。如果我向Lambda添加权限,如下所示:
myLambda.addPermission("PermitAPIGInvocation", Permission.builder()
.action("lambda:InvokeFunction")
.principal(ServicePrincipal.Builder.create("apigateway.amazonaws.com")
.build())
.sourceArn(openapiRestApi.arnForExecuteApi())
.build());
那么我现在需要在权限生效之前重新部署API。我还在研究如何避免这种情况。
我想出了一个解决方案,它比这里的其他答案简单一点,因为它不需要阶段变量或多次部署。
首先,将x-amazon-apigateway集成的uri
设置为${API_LAMBDA_ARN}
之类的变量,并使用与此示例中相同的类型
和httpMethod
:
[...]
"paths": {
"/pets": {
"get": {
"summary": "List all pets",
"responses": {
[...]
},
"x-amazon-apigateway-integration": {
"uri": "${API_LAMBDA_ARN}",
"type": "AWS_PROXY",
"httpMethod": "POST",
}
}
}
},
[...]
然后,您可以使用此构造(或等效的TypeScript实现)在构建时替换变量并基于OpenAPI文档创建API Gateway Http API:
from aws_cdk import (
core,
aws_iam as iam,
aws_lambda as _lambda,
aws_apigatewayv2 as apigateway
)
class OpenApiLambdaStack(core.Stack):
def __init__(
self, scope: core.Construct, construct_id: str, **kwargs
) -> None:
super().__init__(scope, construct_id, **kwargs)
# function that handles api request(s)
api_lambda = _lambda.Function([...])
# read openapi document
with open("openapi.json", "r") as json_file:
content = json_file.read()
# replace the variable by the lambda functions arn
content = content.replace("${API_LAMBDA_ARN}", api_lambda.function_arn)
openapi = json.loads(content)
# create apigateway
http_api = apigateway.HttpApi(self, "OpenApiLambdaGateway")
# use escape hatches to import OpenAPI Document
# see: https://docs.aws.amazon.com/cdk/latest/guide/cfn_layer.html
http_api_cfn: apigateway.CfnApi = http_api.node.default_child
http_api_cfn.add_property_override("Body", openapi)
http_api_cfn.add_property_deletion_override("Name")
http_api_cfn.add_property_deletion_override("ProtocolType")
# let it fail on warnings to be sure everything went right
http_api_cfn.add_property_override("FailOnWarnings", True)
# construct arn of createad api gateway (to grant permission)
http_api_arn = (
f"arn:{self.partition}:execute-api:"
f"{http_api.env.region}:{http_api.env.account}:"
f"{http_api.http_api_id}/*/*/*"
)
# grant apigateway permission to invoke api lambda function
api_lambda.add_permission(
f"Invoke By {http_api.node.id} Permission",
principal=iam.ServicePrincipal("apigateway.amazonaws.com"),
action="lambda:InvokeFunction",
source_arn=http_api_arn,
)
# output api gateway url
core.CfnOutput(self, "HttpApiUrl", value=http_api.url)
Python用户也可能对我发布的openapigateway构造感兴趣,以使这个过程更加简单。它支持JSON和YAML。
有一个现有的解决方法。以下是方法:
您的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"
如您所见,此 OpenAPI 模板指的是 ApiStage、AWS::区域和 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')
在这里,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')
在这里,如前所述,我强制CDK覆盖逻辑id,以便在部署之前知道id。否则,cdk将向逻辑ID添加后缀。
const asset = new Asset(this, 'SampleAsset', {
path: './api-gateway-definitions/SuperAPI.yml',
});
这允许我将OpenAPI文件直接上传到cdk bucket(不需要创建新的,这太棒了)。
const data = Fn.transform('AWS::Include', {'Location': asset.s3ObjectUrl})
这是云形成魔法的一部分。这是解释Fn::Sub和Fn::GetAtt的地方。我无法让它工作!参考函数。
const SuperApiDefinition = apigateway.AssetApiDefinition.fromInline(data)
从先前读取的文件创建 api 定义。
const sftpApiGateway = new apigateway.SpecRestApi(this, 'superAPI', {
apiDefinition: SuperApiDefinition,
deploy: false
})
最后,创建SpecRestApi。运行和魔术,这是工作。您可能仍然会遇到400个错误,可能是因为OpenAPI文件中的格式不正确(并且不要使用!Ref)。
我会重新指挥这个吗?呵呵。这几乎是一种解决方法。如果要在 CI 中将 OpenAPI 格式与动态变量一起使用,这将非常有用。无需花费太多精力,只需切换 1 个环境变量,即可在开发和生产中进行部署。
但是,这感觉真的很笨拙,似乎不适合CDK哲学。这是我目前用于部署的内容,但将来可能会更改。我相信一个真正的模板解决方案可能更适合这里,但现在,我真的没有想过。
我不熟悉swagger文档等,请分享为以下endpoint(spring boot microservice的endpoint)创建开放api规范的好资源或步骤: 任何帮助或参考将不胜感激。谢谢大家。
我想问是否有一种方法可以简单地从不同的API中聚合OpenApi规范? 目前,我们为每个API添加了用户招摇过市的功能,并在其中添加了用于身份验证的自定义逻辑,我们还为不同的可访问性规则添加了规则。 然而,以某种方式聚合所有这些API,并将身份验证、访问逻辑保持在同一位置,会更方便。 例: 我们有两个独立的微服务,有独立的API和独立的地址 API 1localhost:5000 使用Orders
IdentityServer实现了以下规范: OpenID Connect OpenID Connect Core 1.0(规范) OpenID Connect Discovery 1.0(规范) OpenID Connect Session Management 1.0 – draft 22(规范) OpenID Connect HTTP-based Logout 1.0 –draft 03(规
当我使用postman时,谷歌似乎没有验证我的请求主体架构,甚至没有验证请求有主体。我错过了什么吗?对我来说,这意味着谷歌在调用x-google-backend之前会验证此类事情,但它总是将请求传递给我的云函数,无论我是否传递有效数据。 我用这个问题作为指导。 user.yaml:
我有一个从swagger 2.0更新到openapi 3.0.0的yaml规范。 文件本身大约有 7,000 行,因此手动验证具有挑战性。 我需要找出哪些标记不再与openapi 3.0.0兼容。如何验证我的模式?有没有我可以使用的命令行工具? 我不想将此代码复制/粘贴到在线某个地方,因为我不想公开所有路由。
我的输入JSON如下所示,但是我不确定如何使用JOLT进行内部数组相关的参数转换。感谢任何帮助,因为我是新的JOLT 以下是我创建的规范文件,但它并不完整 预期输出如下 我正在使用这个库https://github.com/bazaarvoice/jolt