如何使用 API 网关调用 AWS Step Function
How to invoke an AWS Step Function using API Gateway
如何使用 API 网关 POST 请求调用 AWS Step Function,并将请求的 JSON 有效负载传递给 Step Function?
1。创建您的阶跃函数
很明显。我想如果您正在阅读本文,您就会知道该怎么做。
否则,您可以查看此处的文档:What is AWS Step Functions?。
2。为您的 API
创建 IAM 角色
它可以用于所有 Step Functions,也可以只用于这一个。我们将只介绍第一种情况,如亚马逊教程中所述:Creating an API Using API Gateway.
To create the IAM role
Log in to the AWS Identity and Access Management console.
On the Roles page, choose Create New Role.
On the Set Role Name page, type APIGatewayToStepFunctions for Role Name, and then choose Next Step.
On the Select Role Type page, under Select Role Type, select Amazon API Gateway.
On the Attach Policy page, choose Next Step.
On the Review page, note the Role ARN, for example:
arn:aws:iam::123456789012:role/APIGatewayToStepFunctions
- Choose Create Role.
To attach a policy to the IAM role
- On the Roles page, search for your role by name (APIGatewayToStepFunctions) and then choose the role.
- On the Permissions tab, choose Attach Policy.
- On the Attach Policy page, search for AWSStepFunctionsFullAccess, choose the policy, and then choose Attach Policy.
3。设置
3.a 如果你没有 JSON 有效载荷
正如 Ka Hou Ieong 在 How can i call AWS Step Functions by API Gateway? 中所解释的那样,您可以通过 API 网关控制台创建 AWS 服务集成,如下所示:
- 集成类型:AWS 服务
- AWS 服务:Step Functions
- HTTP 方法:POST
- 动作类型:使用动作名称
- 操作:开始执行
- 执行角色:开始执行的角色(我们刚刚创建的。只需粘贴它的 ARN)
Headers:
X-Amz-Target -> 'AWSStepFunctions.StartExecution'
Content-Type -> 'application/x-amz-json-1.0'
Body 映射 Templates/Request 负载:
{
"input": "string" (optional),
"name": "string" (optional),
"stateMachineArn": "string"
}
3.b 如果您有 JSON 有效载荷作为输入传递
除 body 映射模板外,一切都与 2.a 中的相同。你要做的就是把它变成一个字符串。使用 $util.escapeJavascript(),例如这样。它会将您的整个请求 body 作为输入传递给您的 Step Function
#set($data = $util.escapeJavaScript($input.json('$')))
{
"input": "$data",
"name": "string" (optional),
"stateMachineArn": "string" (required)
}
备注
stateMachineArn
:如果您不想将 stateMachineArn 作为请求的一部分传递给 API 网关,您可以简单地 hard-code 它在您的 Body 映射模板(参见 AWS API Gateway with Step Function)
name
:省略名称 属性 将使 API 网关在每次执行时为您生成一个不同的名称。
现在,这是我的第一个 "Answer your own question",所以也许这不是它的完成方式,但我确实花了好几个小时试图了解我的映射模板有什么问题。希望这将有助于节省其他人的头发和时间。
对于那些正在寻找一种使用 OpenApi 集成和 CloudFormation 将 ApiGateway 与 Step Functions 状态机 直接连接的人,这是我如何让它发挥作用的一个例子:
这是我设计的 Visual Workflow(CloudFormation 文件中的更多详细信息)作为概念验证:
template.yaml
AWSTemplateFormatVersion: 2010-09-09
Transform: 'AWS::Serverless-2016-10-31'
Description: POC Lambda Examples - Step Functions
Parameters:
CorsOrigin:
Description: Header Access-Control-Allow-Origin
Default: "'http://localhost:3000'"
Type: String
CorsMethods:
Description: Header Access-Control-Allow-Headers
Default: "'*'"
Type: String
CorsHeaders:
Description: Header Access-Control-Allow-Headers
Default: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'"
Type: String
SwaggerS3File:
Description: 'S3 "swagger.yaml" file location'
Default: "./swagger.yaml"
Type: String
Resources:
LambdaRoleForRuleExecution:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${AWS::StackName}-lambda-role
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: 'sts:AssumeRole'
Principal:
Service: lambda.amazonaws.com
Policies:
- PolicyName: WriteCloudWatchLogs
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- 'logs:CreateLogGroup'
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
Resource: 'arn:aws:logs:*:*:*'
ApiGatewayStepFunctionsRole:
Type: AWS::IAM::Role
Properties:
Path: !Join ["", ["/", !Ref "AWS::StackName", "/"]]
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Sid: AllowApiGatewayServiceToAssumeRole
Effect: Allow
Action:
- 'sts:AssumeRole'
Principal:
Service:
- apigateway.amazonaws.com
Policies:
- PolicyName: CallStepFunctions
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- 'states:StartExecution'
Resource:
- !Ref Workflow
Start:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub ${AWS::StackName}-start
Code: ../dist/src/step-functions
Handler: step-functions.start
Role: !GetAtt LambdaRoleForRuleExecution.Arn
Runtime: nodejs8.10
Timeout: 1
Wait3000:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub ${AWS::StackName}-wait3000
Code: ../dist/src/step-functions
Handler: step-functions.wait3000
Role: !GetAtt LambdaRoleForRuleExecution.Arn
Runtime: nodejs8.10
Timeout: 4
Wait500:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub ${AWS::StackName}-wait500
Code: ../dist/src/step-functions
Handler: step-functions.wait500
Role: !GetAtt LambdaRoleForRuleExecution.Arn
Runtime: nodejs8.10
Timeout: 2
End:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub ${AWS::StackName}-end
Code: ../dist/src/step-functions
Handler: step-functions.end
Role: !GetAtt LambdaRoleForRuleExecution.Arn
Runtime: nodejs8.10
Timeout: 1
StateExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- !Sub states.${AWS::Region}.amazonaws.com
Action:
- 'sts:AssumeRole'
Policies:
- PolicyName: "StatesExecutionPolicy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action: "lambda:InvokeFunction"
Resource:
- !GetAtt Start.Arn
- !GetAtt Wait3000.Arn
- !GetAtt Wait500.Arn
- !GetAtt End.Arn
Workflow:
Type: AWS::StepFunctions::StateMachine
Properties:
StateMachineName: !Sub ${AWS::StackName}-state-machine
RoleArn: !GetAtt StateExecutionRole.Arn
DefinitionString: !Sub |
{
"Comment": "AWS Step Functions Example",
"StartAt": "Start",
"Version": "1.0",
"States": {
"Start": {
"Type": "Task",
"Resource": "${Start.Arn}",
"Next": "Parallel State"
},
"Parallel State": {
"Type": "Parallel",
"Next": "End",
"Branches": [
{
"StartAt": "Wait3000",
"States": {
"Wait3000": {
"Type": "Task",
"Resource": "${Wait3000.Arn}",
"End": true
}
}
},
{
"StartAt": "Wait500",
"States": {
"Wait500": {
"Type": "Task",
"Resource": "${Wait500.Arn}",
"End": true
}
}
}
]
},
"End": {
"Type": "Task",
"Resource": "${End.Arn}",
"End": true
}
}
}
RestApi:
Type: AWS::Serverless::Api
Properties:
StageName: !Ref Environment
Name: !Sub ${AWS::StackName}-api
DefinitionBody:
'Fn::Transform':
Name: AWS::Include
Parameters:
# s3 location of the swagger file
Location: !Ref SwaggerS3File
swagger.yaml
openapi: 3.0.0
info:
version: '1.0'
title: "pit-jv-lambda-examples"
description: POC API
license:
name: MIT
x-amazon-apigateway-request-validators:
Validate body:
validateRequestParameters: false
validateRequestBody: true
params:
validateRequestParameters: true
validateRequestBody: false
Validate body, query string parameters, and headers:
validateRequestParameters: true
validateRequestBody: true
paths:
/execute:
options:
x-amazon-apigateway-integration:
type: mock
requestTemplates:
application/json: |
{
"statusCode" : 200
}
responses:
"default":
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Headers:
Fn::Sub: ${CorsHeaders}
method.response.header.Access-Control-Allow-Methods:
Fn::Sub: ${CorsMethods}
method.response.header.Access-Control-Allow-Origin:
Fn::Sub: ${CorsOrigin}
responseTemplates:
application/json: |
{}
responses:
200:
$ref: '#/components/responses/200Cors'
post:
x-amazon-apigateway-integration:
credentials:
Fn::GetAtt: [ ApiGatewayStepFunctionsRole, Arn ]
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:states:action/StartExecution
httpMethod: POST
type: aws
responses:
default:
statusCode: 200
responseParameters:
method.response.header.Access-Control-Allow-Headers:
Fn::Sub: ${CorsHeaders}
method.response.header.Access-Control-Allow-Origin:
Fn::Sub: ${CorsOrigin}
".*CREATION_FAILED.*":
statusCode: 403
responseParameters:
method.response.header.Access-Control-Allow-Headers:
Fn::Sub: ${CorsHeaders}
method.response.header.Access-Control-Allow-Origin:
Fn::Sub: ${CorsOrigin}
responseTemplates:
application/json: $input.path('$.errorMessage')
requestTemplates:
application/json:
Fn::Sub: |-
{
"input": "$util.escapeJavaScript($input.json('$'))",
"name": "$context.requestId",
"stateMachineArn": "${Workflow}"
}
summary: Start workflow
responses:
200:
$ref: '#/components/responses/200Empty'
403:
$ref: '#/components/responses/Error'
components:
schemas:
Error:
title: Error
type: object
properties:
code:
type: string
message:
type: string
responses:
200Empty:
description: Default OK response
200Cors:
description: Default response for CORS method
headers:
Access-Control-Allow-Headers:
schema:
type: "string"
Access-Control-Allow-Methods:
schema:
type: "string"
Access-Control-Allow-Origin:
schema:
type: "string"
Error:
description: Error Response
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
headers:
Access-Control-Allow-Headers:
schema:
type: "string"
Access-Control-Allow-Origin:
schema:
type: "string"
步骤-functions.js
exports.start = (event, context, callback) => {
console.log('start event', event);
console.log('start context', context);
callback(undefined, { function: 'start' });
};
exports.wait3000 = (event, context, callback) => {
console.log('wait3000 event', event);
console.log('wait3000 context', context);
setTimeout(() => {
callback(undefined, { function: 'wait3000' });
}, 3000);
};
exports.wait500 = (event, context, callback) => {
console.log('wait500 event', event);
console.log('wait500 context', context);
setTimeout(() => {
callback(undefined, { function: 'wait500' });
}, 500);
};
exports.end = (event, context, callback) => {
console.log('end event', event);
console.log('end context', context);
callback(undefined, { function: 'end' });
};
如何使用 API 网关 POST 请求调用 AWS Step Function,并将请求的 JSON 有效负载传递给 Step Function?
1。创建您的阶跃函数
很明显。我想如果您正在阅读本文,您就会知道该怎么做。
否则,您可以查看此处的文档:What is AWS Step Functions?。
2。为您的 API
创建 IAM 角色它可以用于所有 Step Functions,也可以只用于这一个。我们将只介绍第一种情况,如亚马逊教程中所述:Creating an API Using API Gateway.
To create the IAM role
Log in to the AWS Identity and Access Management console.
On the Roles page, choose Create New Role.
On the Set Role Name page, type APIGatewayToStepFunctions for Role Name, and then choose Next Step.
On the Select Role Type page, under Select Role Type, select Amazon API Gateway.
On the Attach Policy page, choose Next Step.
On the Review page, note the Role ARN, for example:
arn:aws:iam::123456789012:role/APIGatewayToStepFunctions
- Choose Create Role.
To attach a policy to the IAM role
- On the Roles page, search for your role by name (APIGatewayToStepFunctions) and then choose the role.
- On the Permissions tab, choose Attach Policy.
- On the Attach Policy page, search for AWSStepFunctionsFullAccess, choose the policy, and then choose Attach Policy.
3。设置
3.a 如果你没有 JSON 有效载荷
正如 Ka Hou Ieong 在 How can i call AWS Step Functions by API Gateway? 中所解释的那样,您可以通过 API 网关控制台创建 AWS 服务集成,如下所示:
- 集成类型:AWS 服务
- AWS 服务:Step Functions
- HTTP 方法:POST
- 动作类型:使用动作名称
- 操作:开始执行
- 执行角色:开始执行的角色(我们刚刚创建的。只需粘贴它的 ARN)
Headers:
X-Amz-Target -> 'AWSStepFunctions.StartExecution'
Content-Type -> 'application/x-amz-json-1.0'Body 映射 Templates/Request 负载:
{ "input": "string" (optional), "name": "string" (optional), "stateMachineArn": "string" }
3.b 如果您有 JSON 有效载荷作为输入传递
除 body 映射模板外,一切都与 2.a 中的相同。你要做的就是把它变成一个字符串。使用 $util.escapeJavascript(),例如这样。它会将您的整个请求 body 作为输入传递给您的 Step Function
#set($data = $util.escapeJavaScript($input.json('$')))
{
"input": "$data",
"name": "string" (optional),
"stateMachineArn": "string" (required)
}
备注
stateMachineArn
:如果您不想将 stateMachineArn 作为请求的一部分传递给 API 网关,您可以简单地 hard-code 它在您的 Body 映射模板(参见 AWS API Gateway with Step Function)name
:省略名称 属性 将使 API 网关在每次执行时为您生成一个不同的名称。
现在,这是我的第一个 "Answer your own question",所以也许这不是它的完成方式,但我确实花了好几个小时试图了解我的映射模板有什么问题。希望这将有助于节省其他人的头发和时间。
对于那些正在寻找一种使用 OpenApi 集成和 CloudFormation 将 ApiGateway 与 Step Functions 状态机 直接连接的人,这是我如何让它发挥作用的一个例子:
这是我设计的 Visual Workflow(CloudFormation 文件中的更多详细信息)作为概念验证:
template.yaml
AWSTemplateFormatVersion: 2010-09-09
Transform: 'AWS::Serverless-2016-10-31'
Description: POC Lambda Examples - Step Functions
Parameters:
CorsOrigin:
Description: Header Access-Control-Allow-Origin
Default: "'http://localhost:3000'"
Type: String
CorsMethods:
Description: Header Access-Control-Allow-Headers
Default: "'*'"
Type: String
CorsHeaders:
Description: Header Access-Control-Allow-Headers
Default: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'"
Type: String
SwaggerS3File:
Description: 'S3 "swagger.yaml" file location'
Default: "./swagger.yaml"
Type: String
Resources:
LambdaRoleForRuleExecution:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${AWS::StackName}-lambda-role
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: 'sts:AssumeRole'
Principal:
Service: lambda.amazonaws.com
Policies:
- PolicyName: WriteCloudWatchLogs
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- 'logs:CreateLogGroup'
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
Resource: 'arn:aws:logs:*:*:*'
ApiGatewayStepFunctionsRole:
Type: AWS::IAM::Role
Properties:
Path: !Join ["", ["/", !Ref "AWS::StackName", "/"]]
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Sid: AllowApiGatewayServiceToAssumeRole
Effect: Allow
Action:
- 'sts:AssumeRole'
Principal:
Service:
- apigateway.amazonaws.com
Policies:
- PolicyName: CallStepFunctions
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- 'states:StartExecution'
Resource:
- !Ref Workflow
Start:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub ${AWS::StackName}-start
Code: ../dist/src/step-functions
Handler: step-functions.start
Role: !GetAtt LambdaRoleForRuleExecution.Arn
Runtime: nodejs8.10
Timeout: 1
Wait3000:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub ${AWS::StackName}-wait3000
Code: ../dist/src/step-functions
Handler: step-functions.wait3000
Role: !GetAtt LambdaRoleForRuleExecution.Arn
Runtime: nodejs8.10
Timeout: 4
Wait500:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub ${AWS::StackName}-wait500
Code: ../dist/src/step-functions
Handler: step-functions.wait500
Role: !GetAtt LambdaRoleForRuleExecution.Arn
Runtime: nodejs8.10
Timeout: 2
End:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub ${AWS::StackName}-end
Code: ../dist/src/step-functions
Handler: step-functions.end
Role: !GetAtt LambdaRoleForRuleExecution.Arn
Runtime: nodejs8.10
Timeout: 1
StateExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- !Sub states.${AWS::Region}.amazonaws.com
Action:
- 'sts:AssumeRole'
Policies:
- PolicyName: "StatesExecutionPolicy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action: "lambda:InvokeFunction"
Resource:
- !GetAtt Start.Arn
- !GetAtt Wait3000.Arn
- !GetAtt Wait500.Arn
- !GetAtt End.Arn
Workflow:
Type: AWS::StepFunctions::StateMachine
Properties:
StateMachineName: !Sub ${AWS::StackName}-state-machine
RoleArn: !GetAtt StateExecutionRole.Arn
DefinitionString: !Sub |
{
"Comment": "AWS Step Functions Example",
"StartAt": "Start",
"Version": "1.0",
"States": {
"Start": {
"Type": "Task",
"Resource": "${Start.Arn}",
"Next": "Parallel State"
},
"Parallel State": {
"Type": "Parallel",
"Next": "End",
"Branches": [
{
"StartAt": "Wait3000",
"States": {
"Wait3000": {
"Type": "Task",
"Resource": "${Wait3000.Arn}",
"End": true
}
}
},
{
"StartAt": "Wait500",
"States": {
"Wait500": {
"Type": "Task",
"Resource": "${Wait500.Arn}",
"End": true
}
}
}
]
},
"End": {
"Type": "Task",
"Resource": "${End.Arn}",
"End": true
}
}
}
RestApi:
Type: AWS::Serverless::Api
Properties:
StageName: !Ref Environment
Name: !Sub ${AWS::StackName}-api
DefinitionBody:
'Fn::Transform':
Name: AWS::Include
Parameters:
# s3 location of the swagger file
Location: !Ref SwaggerS3File
swagger.yaml
openapi: 3.0.0
info:
version: '1.0'
title: "pit-jv-lambda-examples"
description: POC API
license:
name: MIT
x-amazon-apigateway-request-validators:
Validate body:
validateRequestParameters: false
validateRequestBody: true
params:
validateRequestParameters: true
validateRequestBody: false
Validate body, query string parameters, and headers:
validateRequestParameters: true
validateRequestBody: true
paths:
/execute:
options:
x-amazon-apigateway-integration:
type: mock
requestTemplates:
application/json: |
{
"statusCode" : 200
}
responses:
"default":
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Headers:
Fn::Sub: ${CorsHeaders}
method.response.header.Access-Control-Allow-Methods:
Fn::Sub: ${CorsMethods}
method.response.header.Access-Control-Allow-Origin:
Fn::Sub: ${CorsOrigin}
responseTemplates:
application/json: |
{}
responses:
200:
$ref: '#/components/responses/200Cors'
post:
x-amazon-apigateway-integration:
credentials:
Fn::GetAtt: [ ApiGatewayStepFunctionsRole, Arn ]
uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:states:action/StartExecution
httpMethod: POST
type: aws
responses:
default:
statusCode: 200
responseParameters:
method.response.header.Access-Control-Allow-Headers:
Fn::Sub: ${CorsHeaders}
method.response.header.Access-Control-Allow-Origin:
Fn::Sub: ${CorsOrigin}
".*CREATION_FAILED.*":
statusCode: 403
responseParameters:
method.response.header.Access-Control-Allow-Headers:
Fn::Sub: ${CorsHeaders}
method.response.header.Access-Control-Allow-Origin:
Fn::Sub: ${CorsOrigin}
responseTemplates:
application/json: $input.path('$.errorMessage')
requestTemplates:
application/json:
Fn::Sub: |-
{
"input": "$util.escapeJavaScript($input.json('$'))",
"name": "$context.requestId",
"stateMachineArn": "${Workflow}"
}
summary: Start workflow
responses:
200:
$ref: '#/components/responses/200Empty'
403:
$ref: '#/components/responses/Error'
components:
schemas:
Error:
title: Error
type: object
properties:
code:
type: string
message:
type: string
responses:
200Empty:
description: Default OK response
200Cors:
description: Default response for CORS method
headers:
Access-Control-Allow-Headers:
schema:
type: "string"
Access-Control-Allow-Methods:
schema:
type: "string"
Access-Control-Allow-Origin:
schema:
type: "string"
Error:
description: Error Response
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
headers:
Access-Control-Allow-Headers:
schema:
type: "string"
Access-Control-Allow-Origin:
schema:
type: "string"
步骤-functions.js
exports.start = (event, context, callback) => {
console.log('start event', event);
console.log('start context', context);
callback(undefined, { function: 'start' });
};
exports.wait3000 = (event, context, callback) => {
console.log('wait3000 event', event);
console.log('wait3000 context', context);
setTimeout(() => {
callback(undefined, { function: 'wait3000' });
}, 3000);
};
exports.wait500 = (event, context, callback) => {
console.log('wait500 event', event);
console.log('wait500 context', context);
setTimeout(() => {
callback(undefined, { function: 'wait500' });
}, 500);
};
exports.end = (event, context, callback) => {
console.log('end event', event);
console.log('end context', context);
callback(undefined, { function: 'end' });
};