我正在尝试验证在lambda上收到的事件的请求的延迟性,我目前正在使用带有lambda后端的api-gateway。
在我的serverless.yml上,我有它用于事件处理程序
integration: lambda
passthroughBehavior: "WHEN_NO_TEMPLATE"
request:
template:
application/x-www-form-urlencoded: ${file(aws-api-gateway-form-to-json.ftl)}
我获得了通过AWS API代理控制台生成的文件的内容,我正在使用生成的方法请求传递并进行了一个修改,这只是将原始主体添加到要交付的有效负载中到lambda "rawBody": "$input.body",
,但是当我添加此修改时,请求停止到达lambda,并且在发送请求时出现错误。
## See http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
## This template will pass through all parameters including path, querystring, header, stage variables, and context through to the integration endpoint via the body/payload
#set($allParams = $input.params())
{
"body-json" : $input.json('$'),
"rawBody": "$input.body",
"params" : {
#foreach($type in $allParams.keySet())
#set($params = $allParams.get($type))
"$type" : {
#foreach($paramName in $params.keySet())
"$paramName" : "$util.escapeJavaScript($params.get($paramName))"
#if($foreach.hasNext),#end
#end
}
#if($foreach.hasNext),#end
#end
},
"stage-variables" : {
#foreach($key in $stageVariables.keySet())
"$key" : "$util.escapeJavaScript($stageVariables.get($key))"
#if($foreach.hasNext),#end
#end
},
"context" : {
"account-id" : "$context.identity.accountId",
"api-id" : "$context.apiId",
"api-key" : "$context.identity.apiKey",
"authorizer-principal-id" : "$context.authorizer.principalId",
"caller" : "$context.identity.caller",
"cognito-authentication-provider" : "$context.identity.cognitoAuthenticationProvider",
"cognito-authentication-type" : "$context.identity.cognitoAuthenticationType",
"cognito-identity-id" : "$context.identity.cognitoIdentityId",
"cognito-identity-pool-id" : "$context.identity.cognitoIdentityPoolId",
"http-method" : "$context.httpMethod",
"stage" : "$context.stage",
"source-ip" : "$context.identity.sourceIp",
"user" : "$context.identity.user",
"user-agent" : "$context.identity.userAgent",
"user-arn" : "$context.identity.userArn",
"request-id" : "$context.requestId",
"resource-id" : "$context.resourceId",
"resource-path" : "$context.resourcePath"
}
}
回答一些反馈。
如果我在Use Lambda Proxy integration
中使用Integration Request
我得到了这样的有效载荷,这很棒,但我还需要不存在的原始体。
{ body:
{ token: 'xxxxxxxxxxxx',
team_id: 'xxxxxxxxxxxx',
api_app_id: 'xxxxxxxxxxxx',
event:
{ client_msg_id: 'xxxxxxxxxxxx',
type: 'message',
text: 'xxxxxxxxxxxx',
user: 'xxxxxxxxxxxx',
ts: '123456789.000200',
channel: 'xxxxxxxxxxxx',
event_ts: '123456789.000200',
channel_type: 'im' },
type: 'event_callback',
event_id: 'xxxxxxxxxxxx',
event_time: 123456798,
authed_users: [ 'xxxxxxxxxxxx' ] },
method: 'POST',
principalId: '',
stage: 'dev',
cognitoPoolClaims: { sub: '' },
enhancedAuthContext: {},
headers:
{ Accept: '*/*',
'Accept-Encoding': 'gzip,deflate',
'CloudFront-Forwarded-Proto': 'https',
'CloudFront-Is-Desktop-Viewer': 'true',
'CloudFront-Is-Mobile-Viewer': 'false',
'CloudFront-Is-SmartTV-Viewer': 'false',
'CloudFront-Is-Tablet-Viewer': 'false',
'CloudFront-Viewer-Country': 'US',
'Content-Type': 'application/json',
Host: 'xxxxxxxxxxxx.execute-api.region.amazonaws.com',
'User-Agent': 'Slackbot 1.0 (+https://api.slack.com/robots)',
Via: '1.1 xxxxxxxxxxxx.cloudfront.net (CloudFront)',
'X-Amz-Cf-Id': 'xxxxxxxxxxxx==',
'X-Amzn-Trace-Id': 'Root=xxxxxxxxxxxx',
'X-Forwarded-For': 'xx.xx.xx.xx, xx.xx.xx.xx',
'X-Forwarded-Port': '443',
'X-Forwarded-Proto': 'https',
'X-Slack-Request-Timestamp': '12345678',
'X-Slack-Signature': 'v0=xxxxxxxxxxxx' },
query: {},
path: {},
identity:
{ cognitoIdentityPoolId: '',
accountId: '',
cognitoIdentityId: '',
caller: '',
sourceIp: 'xx.xx.xx.xx',
accessKey: '',
cognitoAuthenticationType: '',
cognitoAuthenticationProvider: '',
userArn: '',
userAgent: 'Slackbot 1.0 (+https://api.slack.com/robots)',
user: '' },
stageVariables: {} }
答案 0 :(得分:4)
我不知道无服务器的工作原理,我的工作流程仅限于本地编写代码和上载zip,其他任何配置都在AWS Consolse本身上完成。
我相信您正在寻找一个名为question的小功能,可以在API网关的“集成请求”标签下找到它。它的作用是为请求和响应提供两个标准的映射模板。
使用Lambda代理集成时,事件对象将如下所示:
{
"resource": "/users/single",
"path": "/users/single",
"httpMethod": "GET",
"headers": {
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"accept-encoding": "gzip, deflate, br",
"accept-language": "en-GB,en-US;q=0.9,en;q=0.8",
"Host": "xxxxxxx.execute-api.xxxx.amazonaws.com",
"upgrade-insecure-requests": "1",
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",
"X-Amzn-Trace-Id": "Root=xxxxxxxxxxxxxxxxx",
"X-Forwarded-For": "xx.xx.xx.xx",
"X-Forwarded-Port": "443",
"X-Forwarded-Proto": "https"
},
"multiValueHeaders": {
"accept": [
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"
],
"accept-encoding": ["gzip, deflate, br"],
"accept-language": ["en-GB,en-US;q=0.9,en;q=0.8"],
"Host": ["xxxxx.execute-api.xxxx.amazonaws.com"],
"upgrade-insecure-requests": ["1"],
"User-Agent": [
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"
],
"X-Amzn-Trace-Id": ["Root=xxxxx"],
"X-Forwarded-For": ["xx.xx.xx.xx"],
"X-Forwarded-Port": ["443"],
"X-Forwarded-Proto": ["https"]
},
// this contains the get body
"queryStringParameters": { "id": "2" },
"multiValueQueryStringParameters": { "id": ["2"] },
// This contains pathParams, if you url looks like users/{id}, this object will contain a key called id containing the value from the URL
"pathParameters": null,
"stageVariables": null,
"requestContext": {
"resourceId": "xxxxx",
"resourcePath": "/users/single",
"httpMethod": "GET",
"extendedRequestId": "xxxxxxx=",
"requestTime": "xx/xx/xxxx:xx:xx:xx +0000",
"path": "/dev/users/single",
"accountId": "642495909037",
"protocol": "HTTP/1.1",
"stage": "dev",
"domainPrefix": "xxxxx",
"requestTimeEpoch": 1547113372715,
"requestId": "xx-xx-xx-xxxxx-xxxxxxxxx",
"identity": {
"cognitoIdentityPoolId": null,
"accountId": null,
"cognitoIdentityId": null,
"caller": null,
"sourceIp": "xx.xx.xx.xx",
"accessKey": null,
"cognitoAuthenticationType": null,
"cognitoAuthenticationProvider": null,
"userArn": null,
"userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",
"user": null
},
"domainName": "xxxxxxx.execute-api.xxxxxxx.amazonaws.com",
"apiId": "xxxx"
},
"body": null, //this contains the post body
"isBase64Encoded": false
}
“ body”键始终是字符串,您必须根据内容类型(即json或www-form-encoded或其他任何内容)对其进行解析。
使用Lambda代理时,从处理程序返回的对象具有遵循一种特定格式,根据该格式,API Gateway会将其映射回响应,即:
{
statusCode: Integer,
headers: HashTable<String, String>,
body: String
}
答案 1 :(得分:1)
因此,在无服务器环境中,这里有一些集成选项。 https://serverless.com/framework/docs/providers/aws/events/apigateway/#request-templates
为澄清起见,您使用的是lambda
,因此您将不得不手动在API Gateway(已提供)中定义模板。
当您尝试让serverless.yml
使用lambda-proxy
(或者被接受为aws-proxy
或aws_proxy
)时,是指您将所有内容都发送到了不包含原始正文的其他格式。
侧面说明:就API网关的LAMBDA_PROXY
集成而言,您应该获得完整的请求正文。我一直使用这种集成(以及ANY
请求路径上的{proxy+}
方法)来特别避免映射模板。我不确定Serverless框架是否最终会在event
上进行其他解析,但是在将事件赋予Lambda函数处理程序的情况下,您确实应该拥有整个主体。我已经编写了一个用于无服务器AWS的框架,这是我在其中使用的框架,并且处理JSON以外的请求格式。所以我知道您可以得到“原始”的身体。在这种情况下,您需要使用框架吗?
好的,所以我的理解是您不能使用lambda-proxy
集成,而必须求助于API Gateway中的自定义映射。
老实说,我需要查看CloudWatch的一些错误。我还希望看到一些您希望收到的示例请求正文示例。您说存在错误,但未发布任何有关错误的信息。我的假设是这是API Gateway中的模板问题。自从我详细研究了映射以来已经有一段时间了(同样,LAMBDA_PROXY
集成才是可行的方法),但是让我提出一些想法。
请记住,$input.body
可以包含可能弄乱模板的JSON。这将产生一个错误,并且您的Lambda将永远不会被触发。在CloudWatch中,您会看到有关无法解析内容的信息。
您可以尝试使用$util.escapeJavaScript()
函数。您可以尝试使用$util.base64Decode()
函数的技巧(这需要在您的API上启用二进制支持)。
API网关可以使用以base64字符串表示的二进制数据,这是不麻烦模板映射内容的一种方法。然后,例如"rawBody": "$util.base64Decode($input.body)"
将在您的映射模板中工作。
要启用二进制支持,请转到API Gateway API的设置,您将看到Binary Media Types
的部分。您可以在其中提供所需的任何内容类型字符串,即使您确实愿意,也可以提供application/json
。我认为如果您接受JSON,则可能可以很好地解析它(可能与转义一起使用)...但是,如果您遇到奇怪的事情,则可能需要这样做。请记住,这是API范围的设置。我认为从您共享的内容来看,您只是在这里使用application/x-www-form-urlencoded
,因此普通的JSON请求将不会受到影响。
最重要的是,某处存在解析错误。
答案 2 :(得分:0)
我意识到这是一个老话题,但是一年半之后,我仍然遇到这个问题,而且我发现了许多其他类似的SO帖子,所以这是2020年12月的工作方式:
API网关的默认设置是将输入视为文本,并将其序列化为JSON。这会导致输入不是文本的问题,例如包含图片/ jpeg的多部分/表单数据。解决此问题的最佳方法是为您希望是二进制数据(https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings.html)的Content-Type启用API网关二进制媒体类型。这将导致API网关将正文的base64编码值移交给Lambda,而不是尝试序列化为JSON(它还会设置“ isBase64Encoded”标头)。这样,您可以使用Lambda代理集成,而不必弄乱映射模板或任何$util.escapeJavaScript()
疯狂。 Lambda代码中唯一需要处理的更改是对主体进行base64解码。