从事件中的 aws api 网关获取原始和解析的有效负载?
get the raw and parsed payloads from aws api gateway inside the event?
我正在尝试验证对我在 lambda 上收到的事件的请求,我目前正在使用 api-gateway 和 lambda 后端。
在我的 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"
}
}
回答一些反馈。
如果我在 Integration Request
中使用 Use Lambda Proxy integration
我得到了这样的有效载荷,这很棒,但我还需要不存在的 rawbody。
{ 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: {} }
我不知道无服务器是如何工作的,我的工作流程仅限于在本地编写代码和上传 zip,任何其他配置都是在 AWS Consolse 本身上完成的。
我相信您正在寻找一个名为 Lambda Proxy Integration 的小功能,您可以在 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 代理时,您 return 来自您的处理程序的对象 必须 遵循特定格式,API 网关根据该格式将其映射回响应,即:
{
statusCode: Integer,
headers: HashTable<String, String>,
body: String
}
所以在无服务器中,这里有一些集成选项。 https://serverless.com/framework/docs/providers/aws/events/apigateway/#request-templates
澄清一下,您使用的是 lambda
,因此您需要在 API 网关(您提供的)中手动定义模板。
当您尝试让您的 serverless.yml
使用 lambda-proxy
(或者被接受为 aws-proxy
或 aws_proxy
)时,您是在说您已将所有内容发送给您采用不包括原始主体的不同格式。
旁注:就 API 网关的 LAMBDA_PROXY
集成而言,您应该获得完整的请求正文。这是我一直使用的集成(连同 {proxy+}
请求路径上的 ANY
方法)专门避免映射模板。我不确定无服务器框架是否最终会在 event
上进行额外的解析,但您确实应该将事件的整个主体提供给 Lambda 函数处理程序。我已经为 AWS 无服务器编写了一个框架,它就是我在那里使用的,我确实处理 JSON 以外的请求格式。所以我知道你可以获得 "raw" 正文。这种情况需要使用框架吗?
好的,我的理解是您无法使用 lambda-proxy
集成,必须求助于 API 网关中的自定义映射。
老实说,我需要查看 CloudWatch 的一些错误。我还想查看您期望收到的一些示例请求正文示例。你说有错误,但 post 什么都没有。我的假设是 API Gateway 中的模板问题。自从我详细处理映射以来已经有一段时间了(同样,LAMBDA_PROXY
集成是可行的方法),但让我抛出一些想法。
请记住,$input.body
可能 包含 JSON,这可能会弄乱模板。这会产生错误,并且您的 Lambda 将永远不会被触发。在 CloudWatch 中,您会看到无法解析的情况。
您可以尝试$util.escapeJavaScript()
功能。您 也可以 尝试使用 $util.base64Decode()
函数的技巧(这需要在您的 API 上启用二进制支持)。
API 网关可以处理二进制数据,表示为 base64 字符串,这是避免模板映射问题的一种方法。然后,例如 "rawBody": "$util.base64Decode($input.body)"
将在您的映射模板中工作。
要启用二进制支持,请转到 API 网关 API 的设置,您将看到 Binary Media Types
的部分。您可以在那里提供您想要的任何内容类型字符串,如果您确实需要,甚至可以提供 application/json
。我想如果你接受 JSON,你可能会很好地解析它(可能连同转义)......但是如果你遇到一些奇怪的事情,你可能需要这样做。请记住,这是一个 API 宽的设置。我认为从你分享的内容来看,你只是在这里使用 application/x-www-form-urlencoded
,所以正常的 JSON 请求不会受到影响。
最重要的是某处存在解析错误。
我意识到这是一个旧线程,但一年半后我仍然遇到这个问题,并且我发现了许多其他类似的 SO 帖子,所以这是 2020 年 12 月有效的帖子:
默认 API 网关设置将输入视为文本,并将它们序列化为 JSON。这会导致非文本输入出现问题,例如multipart/form-data 与 image/jpeg。解决此问题的最佳方法是为您希望成为二进制数据 (https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings.html) 的 Content-Type 启用 API 网关二进制媒体类型。这将导致 API 网关将 body 的 base64 编码值移交给 Lambda,而不是尝试序列化为 JSON(它还会设置 'isBase64Encoded' header ).这样,您就可以使用 Lambda 代理集成,而不必乱用映射模板或任何 $util.escapeJavaScript()
的疯狂行为。 Lambda 代码中唯一要处理的更改是对 body.
进行 base64 解码
我正在尝试验证对我在 lambda 上收到的事件的请求,我目前正在使用 api-gateway 和 lambda 后端。
在我的 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"
}
}
回答一些反馈。
如果我在 Integration Request
中使用 Use Lambda Proxy integration
我得到了这样的有效载荷,这很棒,但我还需要不存在的 rawbody。
{ 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: {} }
我不知道无服务器是如何工作的,我的工作流程仅限于在本地编写代码和上传 zip,任何其他配置都是在 AWS Consolse 本身上完成的。
我相信您正在寻找一个名为 Lambda Proxy Integration 的小功能,您可以在 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 代理时,您 return 来自您的处理程序的对象 必须 遵循特定格式,API 网关根据该格式将其映射回响应,即:
{
statusCode: Integer,
headers: HashTable<String, String>,
body: String
}
所以在无服务器中,这里有一些集成选项。 https://serverless.com/framework/docs/providers/aws/events/apigateway/#request-templates
澄清一下,您使用的是 lambda
,因此您需要在 API 网关(您提供的)中手动定义模板。
当您尝试让您的 serverless.yml
使用 lambda-proxy
(或者被接受为 aws-proxy
或 aws_proxy
)时,您是在说您已将所有内容发送给您采用不包括原始主体的不同格式。
旁注:就 API 网关的 LAMBDA_PROXY
集成而言,您应该获得完整的请求正文。这是我一直使用的集成(连同 {proxy+}
请求路径上的 ANY
方法)专门避免映射模板。我不确定无服务器框架是否最终会在 event
上进行额外的解析,但您确实应该将事件的整个主体提供给 Lambda 函数处理程序。我已经为 AWS 无服务器编写了一个框架,它就是我在那里使用的,我确实处理 JSON 以外的请求格式。所以我知道你可以获得 "raw" 正文。这种情况需要使用框架吗?
好的,我的理解是您无法使用 lambda-proxy
集成,必须求助于 API 网关中的自定义映射。
老实说,我需要查看 CloudWatch 的一些错误。我还想查看您期望收到的一些示例请求正文示例。你说有错误,但 post 什么都没有。我的假设是 API Gateway 中的模板问题。自从我详细处理映射以来已经有一段时间了(同样,LAMBDA_PROXY
集成是可行的方法),但让我抛出一些想法。
请记住,$input.body
可能 包含 JSON,这可能会弄乱模板。这会产生错误,并且您的 Lambda 将永远不会被触发。在 CloudWatch 中,您会看到无法解析的情况。
您可以尝试$util.escapeJavaScript()
功能。您 也可以 尝试使用 $util.base64Decode()
函数的技巧(这需要在您的 API 上启用二进制支持)。
API 网关可以处理二进制数据,表示为 base64 字符串,这是避免模板映射问题的一种方法。然后,例如 "rawBody": "$util.base64Decode($input.body)"
将在您的映射模板中工作。
要启用二进制支持,请转到 API 网关 API 的设置,您将看到 Binary Media Types
的部分。您可以在那里提供您想要的任何内容类型字符串,如果您确实需要,甚至可以提供 application/json
。我想如果你接受 JSON,你可能会很好地解析它(可能连同转义)......但是如果你遇到一些奇怪的事情,你可能需要这样做。请记住,这是一个 API 宽的设置。我认为从你分享的内容来看,你只是在这里使用 application/x-www-form-urlencoded
,所以正常的 JSON 请求不会受到影响。
最重要的是某处存在解析错误。
我意识到这是一个旧线程,但一年半后我仍然遇到这个问题,并且我发现了许多其他类似的 SO 帖子,所以这是 2020 年 12 月有效的帖子:
默认 API 网关设置将输入视为文本,并将它们序列化为 JSON。这会导致非文本输入出现问题,例如multipart/form-data 与 image/jpeg。解决此问题的最佳方法是为您希望成为二进制数据 (https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings.html) 的 Content-Type 启用 API 网关二进制媒体类型。这将导致 API 网关将 body 的 base64 编码值移交给 Lambda,而不是尝试序列化为 JSON(它还会设置 'isBase64Encoded' header ).这样,您就可以使用 Lambda 代理集成,而不必乱用映射模板或任何 $util.escapeJavaScript()
的疯狂行为。 Lambda 代码中唯一要处理的更改是对 body.