AWS apigateway 会更改 http 主体吗?我怎样才能阻止它这样做?
Does AWS apigateway change http body? How can I stop it from doing this?
AWS apigateway 会改变 http body 吗?我怎样才能阻止它这样做?
我的申请:
(1) 前端 "UI" 使用 "POST method" 发送 "http request",其中包含 "body" 到 "form-data" 中的 "zip file" .
(2) AWS "apigateway" 收到此请求并将其转发给 "Lambda Proxy"
(3) AWS "Lambda" 通过 python 编码实现接收此请求并将此 zip 文件解压缩到临时文件夹。
我面临的问题:
(1) 和 (2) 工作正常,但在 (3) 中,lambda 的 pythong 程序无法解压缩文件。
我的发现:
从 "UI" 发送时,正文似乎包含 zip 文件的二进制数据
如下所示:
"PK\x03\x04\n\x00\x00\x00\x00\x00\xd6;TO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00x2.txtPK\x03\x04\n\x00\x00\x00\x00\x00\xd6;TO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00x1.txtPK\x01\x02\x14\x00\n\x00\x00\x00\x00\x00\xd6;TO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00
\x00\x00\x00\x00\x00\x00\x00x2.txtPK\x01\x02\x14\x00\n\x00\x00\x00\x00\x00\xd6;TO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00
\x00\x00\x00$\x00\x00\x00x1.txtPK\x05\x06\x00\x00\x00\x00\x02\x00\x02\x00h\x00\x00\x00H\x00\x00\x00\x00\x00"
但是在 (3) lambda 的 python 代码中,如果我们只是简单地 returns 响应如下:
response = {
"statusCode": 200,
"headers": {
"lambda-response": str(event["body"])
},
"body": "",
"isBase64Encoded": False
}
return 回复
会发现body中的二进制数据,
好像 apigateway 改变了内容
来自:
"PK\x03\x04\n\x00\x00\x00\x00\x00\xd6;TO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00x2.txtPK\x03\x04\n\x00\x00\x00\x00\x00\xd6;TO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00x1.txtPK\x01\x02\x14\x00\n\x00\x00\x00\x00\x00\xd6;TO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00
\x00\x00\x00\x00\x00\x00\x00x2.txtPK\x01\x02\x14\x00\n\x00\x00\x00\x00\x00\xd6;TO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00
\x00\x00\x00$\x00\x00\x00x1.txtPK\x05\x06\x00\x00\x00\x00\x02\x00\x02\x00h\x00\x00\x00H\x00\x00\x00\x00\x00"
进入:
"PK\u0003\u0004\n\u0000\u0000\u0000\u0000\u0000\ufffd;TO\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000x2.txtPK\u0003\u0004\n\u0000\u0000\u0000\u0000\u0000\ufffd;TO\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000x1.txtPK\u0001\u0002\u0014\u0000\n\u0000\u0000\u0000\u0000\u0000\ufffd;TO\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000
\u0000\u0000\u0000\u0000\u0000\u0000\u0000x2.txtPK\u0001\u0002\u0014\u0000\n\u0000\u0000\u0000\u0000\u0000\ufffd;TO\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000
\u0000\u0000\u0000$\u0000\u0000\u0000x1.txtPK\u0005\u0006\u0000\u0000\u0000\u0000\u0002\u0000\u0002\u0000h\u0000\u0000\u0000H\u0000\u0000\u0000\u0000\u0000\r\n"
这很奇怪,我能做些什么来阻止它?
(2019/12/17 更新) 下面是我正在使用的 lambda 代码。
import json # to decode json
import os # file IO
import shutil # file IO (use this to recursively force remove a directory)
print('Loading function')
def decompress_zip_file(src_file_path, dest_dir_path):
'''
Decompress a zip file into a directory.
Args:
src_file_path (Srting): source zip file's path.
dest_dir_path (Srting): the destination of the output directory.
Returns:
isSuccess (bool): the operation is successful or not.
'''
error_msg = "Nothing."
try:
if(os.path.isdir(dest_dir_path)):
shutil.rmtree(dest_dir_path)
with zipfile.ZipFile(src_file_path, 'r') as zip_ref:
zip_ref.extractall(dest_dir_path)
except Exception as ep:
error_msg = "Error in decompress_zip_file(), ep={}:{}".format(type(ep).__name__, str(ep))
print(error_msg)
return (False, error_msg)
return (True, error_msg)
def decompress_zip_file_from_content_in_binary(src_file_in_binary, dest_dir_path):
'''
Decompress a zip file content into a directory.
Args:
src_file_in_binary (byte array): source zip file's content in binary format.
dest_dir_path (Srting): the destination of the output directory.
Returns:
isSuccess (bool): the operation is successful or not.
'''
# write the obtained binary data into a tmp zip file
tmp_file_path = "/tmp/tmp.zip"
if(os.path.isfile(tmp_file_path)):
os.remove(tmp_file_path)
output_file = open(tmp_file_path, 'wb')
output_file.write(src_file_in_binary)
output_file.close()
(isSuccess, error_msg) = decompress_zip_file(tmp_file_path, dest_dir_path)
return (isSuccess, error_msg)
def convert_from_http_body_encoding_to_local_binary(extracted_file_from_http_body_str):
'''
Extract the file (in binary string format) from event['body'] encoding to local binary encoding.
Args:
extracted_file_from_http_body_str (string): the event['body'] file (in binary string format),.
Returns:
zipfile_binary1 (binary array): the conversion result.
'''
zipfile_binary1 = bytes(extracted_file_from_http_body_str, encoding = "ascii") # convert into a zipfile in binary format
return zipfile_binary1
def extract_zipfile_binary_from_body(body_str):
'''
Extract the zipfile (in binary format) from event['body'] string.
Args:
body_str (string): the event['body'] string.
Returns:
(binary array): the conversion result.
'''
retValue = ""
tmpArray = body_str.split("application/zip") # split the content based on MIME part field data; cut the head
if(len(tmpArray) > 1):
retValue += "entered-Lv1."
tmpArray = tmpArray[1].split("PK") # split the content based on zip file header.
if(len(tmpArray) > 1):
retValue += "entered-Lv2."
zipfile_str = "PK" + 'PK'.join(tmpArray[1:]) # add back the zip file header
tmpArray = zipfile_str.split("------WebKitFormBoundary") # split the content based on MIME part field data; cut the tail
if(len(tmpArray) > 1):
zipfile_str = tmpArray[0]
zipfile_binary = convert_from_http_body_encoding_to_local_binary(zipfile_str)
retValue = zipfile_binary
return retValue
def handler(event, context):
'''Provide an event that contains the following keys:
- operation: one of the operations in the operations dict below
- payload: a parameter to pass to the operation being performed
'''
# set the mapping table for "operation" x "return value"
operations = {
'unzip': lambda x: decompress_zip_file_from_content_in_binary(**x), # unzip an uploaded file
'ping': lambda x: 'pong' # respond to ping req.
}
# because we use "Lambda Proxe", means we have api-gateway forward the whole packet without resolving it for lambda.
event_headers = event["headers"]
operation = event_headers['operation']
event_body = event["body"]
if(operation == 'unzip'):
src_file_in_binary = extract_zipfile_binary_from_body(event_body)
payload_json = {}
payload_json['src_file_in_binary'] = src_file_in_binary
payload_json['dest_dir_path'] = "/tmp/tmp_zipfile_output"
event_headers["payload"] = payload_json
if operation in operations:
responseBody = operations[operation](event_headers.get('payload'))
response = {
"statusCode": 200,
"headers": {
"lambda-response": str(responseBody) # the api-gateway will forward the header to the front end.
},
"body": "",
"isBase64Encoded": False
}
return response
else:
raise ValueError('Unrecognized operation "{}"'.format(operation))
以下是来自 AWS 支持的回复。 LGTM。把它留在这里,以便将来人们可以看到这个问题的解决方案。
=====================下面是 AWS 支持的回复 ================= =
嗨,
感谢您联系 AWS 高级支持。我是 Jyoti,今天我将协助您处理此案。
从案例对应中了解到您担心API网关修改
代理到您的 Lambda 函数之前的二进制数据负载。如果我的理解有误,请纠正我。
预期行为:
API 网关确实将二进制数据负载修改为 UTF-8 编码的 JSON 字符串,如果
API 配置为默认设置。因此,这是预期的行为。
请注意,根据 [1],我们必须配置 API 以支持二进制负载
我们在 API 网关中的 API。 API 网关无法按原样发送二进制文件,因为它必须发送
a JSON body 到 lambda 代理。因此,它默认将 data/payload 编码为 UTF-8。
解决方案:
为了克服上述挑战,我们需要添加所需的
二进制媒体类型(application/zip 在这种情况下)到 binaryMediaTypes 列表
在 RestApi 资源的设置页面上。有关如何实现的更多信息
这个,请参考这里 --> [2]。如果未定义此 属性,则有效载荷
如 [1] 中所述,作为 UTF-8 编码的 JSON 字符串处理。
这就是为什么您请求中的文件看起来是 UTF-8 编码的原因。配置 API 后,
Lambda 接收到的事件将是一个 Base64 编码的字符串。
如果要对这个object(编码请求body或'event["body"]')进行操作,
然后您可以通过以下方式将 base64 编码的字符串解码为其原始二进制形式
以下几行(在 python 运行时):
import base64
coded_string = str(event["body"])
base64.b64decode(coded_string)
疑难解答:
我试图在我的环境中复制您的设置。而不是应用程序的前端'UI',
我使用 Postman 作为客户端,而其余设置(API 网关和 Lambda)是相似的。
我正在从 Postman 向我的 API 发出 POST 请求,请求 headers 'Content-Type' 和 'Accept',
两者都设置为值 'application/zip',这是正在发送的二进制媒体类型,并且
也在响应中预期。我的 API 已配置为支持二进制媒体类型
传入请求 body。我在 API 的 binaryMediaTypes 列表中添加了 'application/zip'。
最后,在 Lambda 函数中,我正在解码 base64 编码的请求 body(即事件 ["body"])
使用 base64 库(在 python 中)将其转换为原始二进制形式。
如果您仍想通过 returning 二进制文件确认请求的 form-data 的一致性
响应中的数据,您可以参考以下代码段:
response {
'isBase64Encoded': True, #Ensure the body is base encoded
'statusCode': 200,
'headers': { "Content-Type": "applicaiton/zip" }, #Define the Content-Type
'body': event["body"] #Response Body returns the Base64-encoded value
}
我们将 isBase64Encoded 参数设置为 True,API 网关自动解码
响应 body 取决于 Content-Type (即二进制 data/media 类型)
客户端(在我的例子中是 Postman)设置为接收(即 application/zip)。请注意,'Accept'
我在 header 中发送的 header 是为了验证响应 body 是否包含二进制文件
数据类型,提出请求。
上面的响应body与第一次发送的请求body二进制数据相同
通过 API,在我的设置中。
希望我已经解决了您的顾虑。但是,如果您仍然需要实施方面的帮助,
请再次与我们联系,我很乐意为您提供帮助。
References:
=-=-=-=--=-=-=-=-=-=
[1] Support Binary Payloads in API Gateway: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings.html
[2] Enable Binary Support Using the API Gateway Console: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings-configure-with-console.html
此致,
乔蒂·普拉卡什 P.
亚马逊网络服务
2019/12/20更新
我意识到我的内容类型实际上是 multipart 而不是 application/zip 所以我再次修改了设置然后它起作用了。
以下是来自 AWS 支持的帮助。非常感谢他们的帮助。
嗨,
非常感谢您详细说明您的申请流程和日志。我现在了解到您的 HTTP 请求 header 'content-type' 设置为 'multipart/form-data'。我同意,对于上传文件的网络表单,将内容类型设置为 form-data 是很常见的,而 AWS API 网关确实支持它。您想知道是否可以在不更改前端代码的情况下阻止 UTF-8 编码。如果我的理解有误,请纠正我。
为了讨论方便,我想把HTTP请求和响应的故障排除方法分开。
对于 API 的请求:
请在 API 网关控制台的“API 设置页面的 binaryMediaType 列表中添加 'multipart/form-data' 作为值之一。您不必更改代码或HTTP 请求或其任何 headers。请注意在 API 网关中处理二进制 media/data,HTTP 请求 Content-Type header 必须匹配 binaryMediaType 中的值列表。
在你的用例中,如果你想发送二进制媒体返回对您的请求的响应,HTTP 请求 'Content-Type' 和 'Accept' headers,API 的 binaryMediaType 值和 HTTP 响应 'Content-Type'必须全部设置为 'multipart/form-data'。我尝试了上面的方法,它对 Postman Client 很有效。如果 HTTP 请求 'Content-Type' 设置为 'multipart/form-data',则 'boundary' 指令由 Postman 自动设置。因此,您只需在 'binaryMediaType' 列表中添加 'multipart/form-data'。请查看我的 HTTP 请求,如下所示:
POST /stg-with-logs HTTP/1.1
Host: <some-api-id>.execute-api.us-east-1.amazonaws.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Accept: multipart/form-data
Cache-Control: no-cache
Postman-Token: 123b64f9-5669-f794-b9df-34a7561e9708
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="File"; filename="archive.zip"
Content-Type: application/zip
------WebKitFormBoundary7MA4YWxkTrZu0gW--
对于来自 API 的响应:
我在查看您的 API 网关日志时注意到,header 'isBase64Encoded' 未设置。请将其设置为 true。 API 如果 'isBase64Encoded' 设置为 true,网关会自动解码 HTTP 响应的 body 中的任何 base64 编码字符串。请查看下面来自我的 lambda 的 HTTP 响应:
(a6729f56-b245-45a4-9ac4-7e00b23c8957) Endpoint response body before transformations:
{
"isBase64Encoded": true,
"statusCode": 200,
"headers": {
"Content-Type": "multipart/form-data",
"Accpet": "multipart/form-data"
},
"body": "LS0tLS0tV2ViS2l0Rm9ybUJvdW5kYXJ5SmxkSW1aV1lHczlSTndPWQ0KQ29udGVudC1EaXNwb3NpdGlvbjogZm9ybS1kYXRhOyBuYW1lPSJGaWxlIjsgZmlsZW5hbWU9ImFyY2hpdmUuemlwIg0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi96aXANCg0KUEsDBBQAAAAIAKF4kE9/Mo7/XgAAAJcAAAAaABwASGVsbG8tV29ybGQtNjY3MzMxNTI4MS50eHRVVAkAA8ZP910SUPdddXgLAAEEHZHreQTMewNxNY1BDgIxDAPvvIVPOY3SEC+9WCrfJ13EZWTNHKwKkzMmxIp5dpsnFMlqrjzBF/SKxCW2/8dl3ttGGjTqnkdMG+Wwj96jA3/YJsC2QF9iesuLUXPfv80KrpaVYeDjC1BLAQIeAxQAAAAIAKF4kE9/Mo7/XgAAAJcAAAAaABgAAAAAAAEAAACkgQAAAABIZWxsby1Xb3JsZC02NjczMzE1MjgxLnR4dFVUBQADxk/3XXV4CwABBB2R63kEzHsDcVBLBQYAAAAAAQABAGAAAACyAAAAAAANCi0tLS0tLVdlYktpdEZvcm1Cb3VuZGFyeUpsZEltWldZR3M5Uk53T1kNCkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsgbmFtZT0iVGVzdCBEYXRhIg0KDQpUZXN0aW5nIEJvdW5kYXJ5IGluIG11bHRpcGFydC9mb3JtLWRhdGENCi0tLS0tLVdlYktpdEZvcm1Cb3VuZGFyeUpsZEltWldZR3M5Uk53T1ktLQ0K"
}
我附上了我的 API Gateway Swagger 文件和 Lambda 函数代码,以供参考。该设置对我来说工作正常,我能够在发出 HTTP 请求时 return 二进制负载。如果您想在您的环境中对其进行测试,请在 Swagger 文件中设置适当的凭据和 lambda uri。
希望这能解决您的问题。但是,如果问题仍然存在或您有任何其他问题,请再次与我们联系,我们很乐意为您提供帮助。
要查看此信件中包含的名为 'binaryPost-stg-with-logs-oas30-apigateway.yaml,python-binary-response.py' 的文件,请使用签名下方给出的大小写 link。
此致,
乔蒂·普拉卡什 P.
亚马逊网络服务
查看 AWS Support 知识中心,这是一个包含文章和视频的知识库,可以回答客户有关 AWS 服务的问题:https://aws.amazon.com/premiumsupport/knowledge-center/?icmpid=support_email_category
AWS apigateway 会改变 http body 吗?我怎样才能阻止它这样做?
我的申请:
(1) 前端 "UI" 使用 "POST method" 发送 "http request",其中包含 "body" 到 "form-data" 中的 "zip file" .
(2) AWS "apigateway" 收到此请求并将其转发给 "Lambda Proxy"
(3) AWS "Lambda" 通过 python 编码实现接收此请求并将此 zip 文件解压缩到临时文件夹。
我面临的问题: (1) 和 (2) 工作正常,但在 (3) 中,lambda 的 pythong 程序无法解压缩文件。
我的发现:
从 "UI" 发送时,正文似乎包含 zip 文件的二进制数据 如下所示:
"PK\x03\x04\n\x00\x00\x00\x00\x00\xd6;TO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00x2.txtPK\x03\x04\n\x00\x00\x00\x00\x00\xd6;TO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00x1.txtPK\x01\x02\x14\x00\n\x00\x00\x00\x00\x00\xd6;TO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00x2.txtPK\x01\x02\x14\x00\n\x00\x00\x00\x00\x00\xd6;TO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00$\x00\x00\x00x1.txtPK\x05\x06\x00\x00\x00\x00\x02\x00\x02\x00h\x00\x00\x00H\x00\x00\x00\x00\x00"
但是在 (3) lambda 的 python 代码中,如果我们只是简单地 returns 响应如下:
response = {
"statusCode": 200,
"headers": {
"lambda-response": str(event["body"])
},
"body": "",
"isBase64Encoded": False
}
return 回复
会发现body中的二进制数据, 好像 apigateway 改变了内容
来自:
"PK\x03\x04\n\x00\x00\x00\x00\x00\xd6;TO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00x2.txtPK\x03\x04\n\x00\x00\x00\x00\x00\xd6;TO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00x1.txtPK\x01\x02\x14\x00\n\x00\x00\x00\x00\x00\xd6;TO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00x2.txtPK\x01\x02\x14\x00\n\x00\x00\x00\x00\x00\xd6;TO\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00$\x00\x00\x00x1.txtPK\x05\x06\x00\x00\x00\x00\x02\x00\x02\x00h\x00\x00\x00H\x00\x00\x00\x00\x00"
进入:
"PK\u0003\u0004\n\u0000\u0000\u0000\u0000\u0000\ufffd;TO\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000x2.txtPK\u0003\u0004\n\u0000\u0000\u0000\u0000\u0000\ufffd;TO\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000x1.txtPK\u0001\u0002\u0014\u0000\n\u0000\u0000\u0000\u0000\u0000\ufffd;TO\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000 \u0000\u0000\u0000\u0000\u0000\u0000\u0000x2.txtPK\u0001\u0002\u0014\u0000\n\u0000\u0000\u0000\u0000\u0000\ufffd;TO\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0006\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000 \u0000\u0000\u0000$\u0000\u0000\u0000x1.txtPK\u0005\u0006\u0000\u0000\u0000\u0000\u0002\u0000\u0002\u0000h\u0000\u0000\u0000H\u0000\u0000\u0000\u0000\u0000\r\n"
这很奇怪,我能做些什么来阻止它?
(2019/12/17 更新) 下面是我正在使用的 lambda 代码。
import json # to decode json
import os # file IO
import shutil # file IO (use this to recursively force remove a directory)
print('Loading function')
def decompress_zip_file(src_file_path, dest_dir_path):
'''
Decompress a zip file into a directory.
Args:
src_file_path (Srting): source zip file's path.
dest_dir_path (Srting): the destination of the output directory.
Returns:
isSuccess (bool): the operation is successful or not.
'''
error_msg = "Nothing."
try:
if(os.path.isdir(dest_dir_path)):
shutil.rmtree(dest_dir_path)
with zipfile.ZipFile(src_file_path, 'r') as zip_ref:
zip_ref.extractall(dest_dir_path)
except Exception as ep:
error_msg = "Error in decompress_zip_file(), ep={}:{}".format(type(ep).__name__, str(ep))
print(error_msg)
return (False, error_msg)
return (True, error_msg)
def decompress_zip_file_from_content_in_binary(src_file_in_binary, dest_dir_path):
'''
Decompress a zip file content into a directory.
Args:
src_file_in_binary (byte array): source zip file's content in binary format.
dest_dir_path (Srting): the destination of the output directory.
Returns:
isSuccess (bool): the operation is successful or not.
'''
# write the obtained binary data into a tmp zip file
tmp_file_path = "/tmp/tmp.zip"
if(os.path.isfile(tmp_file_path)):
os.remove(tmp_file_path)
output_file = open(tmp_file_path, 'wb')
output_file.write(src_file_in_binary)
output_file.close()
(isSuccess, error_msg) = decompress_zip_file(tmp_file_path, dest_dir_path)
return (isSuccess, error_msg)
def convert_from_http_body_encoding_to_local_binary(extracted_file_from_http_body_str):
'''
Extract the file (in binary string format) from event['body'] encoding to local binary encoding.
Args:
extracted_file_from_http_body_str (string): the event['body'] file (in binary string format),.
Returns:
zipfile_binary1 (binary array): the conversion result.
'''
zipfile_binary1 = bytes(extracted_file_from_http_body_str, encoding = "ascii") # convert into a zipfile in binary format
return zipfile_binary1
def extract_zipfile_binary_from_body(body_str):
'''
Extract the zipfile (in binary format) from event['body'] string.
Args:
body_str (string): the event['body'] string.
Returns:
(binary array): the conversion result.
'''
retValue = ""
tmpArray = body_str.split("application/zip") # split the content based on MIME part field data; cut the head
if(len(tmpArray) > 1):
retValue += "entered-Lv1."
tmpArray = tmpArray[1].split("PK") # split the content based on zip file header.
if(len(tmpArray) > 1):
retValue += "entered-Lv2."
zipfile_str = "PK" + 'PK'.join(tmpArray[1:]) # add back the zip file header
tmpArray = zipfile_str.split("------WebKitFormBoundary") # split the content based on MIME part field data; cut the tail
if(len(tmpArray) > 1):
zipfile_str = tmpArray[0]
zipfile_binary = convert_from_http_body_encoding_to_local_binary(zipfile_str)
retValue = zipfile_binary
return retValue
def handler(event, context):
'''Provide an event that contains the following keys:
- operation: one of the operations in the operations dict below
- payload: a parameter to pass to the operation being performed
'''
# set the mapping table for "operation" x "return value"
operations = {
'unzip': lambda x: decompress_zip_file_from_content_in_binary(**x), # unzip an uploaded file
'ping': lambda x: 'pong' # respond to ping req.
}
# because we use "Lambda Proxe", means we have api-gateway forward the whole packet without resolving it for lambda.
event_headers = event["headers"]
operation = event_headers['operation']
event_body = event["body"]
if(operation == 'unzip'):
src_file_in_binary = extract_zipfile_binary_from_body(event_body)
payload_json = {}
payload_json['src_file_in_binary'] = src_file_in_binary
payload_json['dest_dir_path'] = "/tmp/tmp_zipfile_output"
event_headers["payload"] = payload_json
if operation in operations:
responseBody = operations[operation](event_headers.get('payload'))
response = {
"statusCode": 200,
"headers": {
"lambda-response": str(responseBody) # the api-gateway will forward the header to the front end.
},
"body": "",
"isBase64Encoded": False
}
return response
else:
raise ValueError('Unrecognized operation "{}"'.format(operation))
以下是来自 AWS 支持的回复。 LGTM。把它留在这里,以便将来人们可以看到这个问题的解决方案。
=====================下面是 AWS 支持的回复 ================= =
嗨,
感谢您联系 AWS 高级支持。我是 Jyoti,今天我将协助您处理此案。
从案例对应中了解到您担心API网关修改 代理到您的 Lambda 函数之前的二进制数据负载。如果我的理解有误,请纠正我。
预期行为:
API 网关确实将二进制数据负载修改为 UTF-8 编码的 JSON 字符串,如果
API 配置为默认设置。因此,这是预期的行为。
请注意,根据 [1],我们必须配置 API 以支持二进制负载
我们在 API 网关中的 API。 API 网关无法按原样发送二进制文件,因为它必须发送
a JSON body 到 lambda 代理。因此,它默认将 data/payload 编码为 UTF-8。
解决方案:
为了克服上述挑战,我们需要添加所需的 二进制媒体类型(application/zip 在这种情况下)到 binaryMediaTypes 列表 在 RestApi 资源的设置页面上。有关如何实现的更多信息 这个,请参考这里 --> [2]。如果未定义此 属性,则有效载荷 如 [1] 中所述,作为 UTF-8 编码的 JSON 字符串处理。
这就是为什么您请求中的文件看起来是 UTF-8 编码的原因。配置 API 后, Lambda 接收到的事件将是一个 Base64 编码的字符串。
如果要对这个object(编码请求body或'event["body"]')进行操作, 然后您可以通过以下方式将 base64 编码的字符串解码为其原始二进制形式 以下几行(在 python 运行时):
import base64
coded_string = str(event["body"])
base64.b64decode(coded_string)
疑难解答:
我试图在我的环境中复制您的设置。而不是应用程序的前端'UI', 我使用 Postman 作为客户端,而其余设置(API 网关和 Lambda)是相似的。 我正在从 Postman 向我的 API 发出 POST 请求,请求 headers 'Content-Type' 和 'Accept', 两者都设置为值 'application/zip',这是正在发送的二进制媒体类型,并且 也在响应中预期。我的 API 已配置为支持二进制媒体类型 传入请求 body。我在 API 的 binaryMediaTypes 列表中添加了 'application/zip'。 最后,在 Lambda 函数中,我正在解码 base64 编码的请求 body(即事件 ["body"]) 使用 base64 库(在 python 中)将其转换为原始二进制形式。
如果您仍想通过 returning 二进制文件确认请求的 form-data 的一致性 响应中的数据,您可以参考以下代码段:
response {
'isBase64Encoded': True, #Ensure the body is base encoded
'statusCode': 200,
'headers': { "Content-Type": "applicaiton/zip" }, #Define the Content-Type
'body': event["body"] #Response Body returns the Base64-encoded value
}
我们将 isBase64Encoded 参数设置为 True,API 网关自动解码 响应 body 取决于 Content-Type (即二进制 data/media 类型) 客户端(在我的例子中是 Postman)设置为接收(即 application/zip)。请注意,'Accept' 我在 header 中发送的 header 是为了验证响应 body 是否包含二进制文件 数据类型,提出请求。
上面的响应body与第一次发送的请求body二进制数据相同 通过 API,在我的设置中。
希望我已经解决了您的顾虑。但是,如果您仍然需要实施方面的帮助, 请再次与我们联系,我很乐意为您提供帮助。
References:
=-=-=-=--=-=-=-=-=-=
[1] Support Binary Payloads in API Gateway: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings.html
[2] Enable Binary Support Using the API Gateway Console: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings-configure-with-console.html
此致,
乔蒂·普拉卡什 P. 亚马逊网络服务
2019/12/20更新
我意识到我的内容类型实际上是 multipart 而不是 application/zip 所以我再次修改了设置然后它起作用了。 以下是来自 AWS 支持的帮助。非常感谢他们的帮助。
嗨,
非常感谢您详细说明您的申请流程和日志。我现在了解到您的 HTTP 请求 header 'content-type' 设置为 'multipart/form-data'。我同意,对于上传文件的网络表单,将内容类型设置为 form-data 是很常见的,而 AWS API 网关确实支持它。您想知道是否可以在不更改前端代码的情况下阻止 UTF-8 编码。如果我的理解有误,请纠正我。
为了讨论方便,我想把HTTP请求和响应的故障排除方法分开。
对于 API 的请求:
请在 API 网关控制台的“API 设置页面的 binaryMediaType 列表中添加 'multipart/form-data' 作为值之一。您不必更改代码或HTTP 请求或其任何 headers。请注意在 API 网关中处理二进制 media/data,HTTP 请求 Content-Type header 必须匹配 binaryMediaType 中的值列表。
在你的用例中,如果你想发送二进制媒体返回对您的请求的响应,HTTP 请求 'Content-Type' 和 'Accept' headers,API 的 binaryMediaType 值和 HTTP 响应 'Content-Type'必须全部设置为 'multipart/form-data'。我尝试了上面的方法,它对 Postman Client 很有效。如果 HTTP 请求 'Content-Type' 设置为 'multipart/form-data',则 'boundary' 指令由 Postman 自动设置。因此,您只需在 'binaryMediaType' 列表中添加 'multipart/form-data'。请查看我的 HTTP 请求,如下所示:
POST /stg-with-logs HTTP/1.1
Host: <some-api-id>.execute-api.us-east-1.amazonaws.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Accept: multipart/form-data
Cache-Control: no-cache
Postman-Token: 123b64f9-5669-f794-b9df-34a7561e9708
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="File"; filename="archive.zip"
Content-Type: application/zip
------WebKitFormBoundary7MA4YWxkTrZu0gW--
对于来自 API 的响应:
我在查看您的 API 网关日志时注意到,header 'isBase64Encoded' 未设置。请将其设置为 true。 API 如果 'isBase64Encoded' 设置为 true,网关会自动解码 HTTP 响应的 body 中的任何 base64 编码字符串。请查看下面来自我的 lambda 的 HTTP 响应:
(a6729f56-b245-45a4-9ac4-7e00b23c8957) Endpoint response body before transformations:
{
"isBase64Encoded": true,
"statusCode": 200,
"headers": {
"Content-Type": "multipart/form-data",
"Accpet": "multipart/form-data"
},
"body": "LS0tLS0tV2ViS2l0Rm9ybUJvdW5kYXJ5SmxkSW1aV1lHczlSTndPWQ0KQ29udGVudC1EaXNwb3NpdGlvbjogZm9ybS1kYXRhOyBuYW1lPSJGaWxlIjsgZmlsZW5hbWU9ImFyY2hpdmUuemlwIg0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi96aXANCg0KUEsDBBQAAAAIAKF4kE9/Mo7/XgAAAJcAAAAaABwASGVsbG8tV29ybGQtNjY3MzMxNTI4MS50eHRVVAkAA8ZP910SUPdddXgLAAEEHZHreQTMewNxNY1BDgIxDAPvvIVPOY3SEC+9WCrfJ13EZWTNHKwKkzMmxIp5dpsnFMlqrjzBF/SKxCW2/8dl3ttGGjTqnkdMG+Wwj96jA3/YJsC2QF9iesuLUXPfv80KrpaVYeDjC1BLAQIeAxQAAAAIAKF4kE9/Mo7/XgAAAJcAAAAaABgAAAAAAAEAAACkgQAAAABIZWxsby1Xb3JsZC02NjczMzE1MjgxLnR4dFVUBQADxk/3XXV4CwABBB2R63kEzHsDcVBLBQYAAAAAAQABAGAAAACyAAAAAAANCi0tLS0tLVdlYktpdEZvcm1Cb3VuZGFyeUpsZEltWldZR3M5Uk53T1kNCkNvbnRlbnQtRGlzcG9zaXRpb246IGZvcm0tZGF0YTsgbmFtZT0iVGVzdCBEYXRhIg0KDQpUZXN0aW5nIEJvdW5kYXJ5IGluIG11bHRpcGFydC9mb3JtLWRhdGENCi0tLS0tLVdlYktpdEZvcm1Cb3VuZGFyeUpsZEltWldZR3M5Uk53T1ktLQ0K"
}
我附上了我的 API Gateway Swagger 文件和 Lambda 函数代码,以供参考。该设置对我来说工作正常,我能够在发出 HTTP 请求时 return 二进制负载。如果您想在您的环境中对其进行测试,请在 Swagger 文件中设置适当的凭据和 lambda uri。
希望这能解决您的问题。但是,如果问题仍然存在或您有任何其他问题,请再次与我们联系,我们很乐意为您提供帮助。
要查看此信件中包含的名为 'binaryPost-stg-with-logs-oas30-apigateway.yaml,python-binary-response.py' 的文件,请使用签名下方给出的大小写 link。
此致,
乔蒂·普拉卡什 P. 亚马逊网络服务
查看 AWS Support 知识中心,这是一个包含文章和视频的知识库,可以回答客户有关 AWS 服务的问题:https://aws.amazon.com/premiumsupport/knowledge-center/?icmpid=support_email_category