API 网关 - POST 多部分/表单数据

Posted

技术标签:

【中文标题】API 网关 - POST 多部分/表单数据【英文标题】:API Gateway - POST multipart/form-data 【发布时间】:2017-06-05 00:02:57 【问题描述】:

看来我的问题可能有点类似to this one。

我的 API Gateway 中有一个 API,并且正在通过 HTTP 代理到 POST 的 multipart/form-data 文件的端点。

如果我直接调用 HTTP 端点(不是通过 API gateway) - 使用 postman,它会按预期工作,但是,使用 API 网关端点(通过 postman)会失败。

我比较了两个请求(通过fiddlerCloudWatch 日志),它们似乎是相同的:

请求直接 API 调用(工作):

POST https://domainname/api/v1/documents HTTP/1.1
Host: api.service
Connection: keep-alive
Content-Length: 202
Authorization: AuthToken
Postman-Token: a75869d6-1d64-6b9f-513d-a80ac192c8e1
Cache-Control: no-cache
Origin: chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop
docMetaInfo: some extra data needed
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/55.0.2883.87 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryB85rsPlMffA2fziS
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8

------WebKitFormBoundaryB85rsPlMffA2fziS
Content-Disposition: form-data; name=""; filename="Test.txt"
Content-Type: text/plain

This is a test Text File
------WebKitFormBoundaryB85rsPlMffA2fziS--

来自 API 网关的请求(无效):

POST https://GATEWAY_domainname/api/v1/documents HTTP/1.1
Host: api-Gateway.service
Connection: keep-alive
Content-Length: 202
Authorization: AuthToken
Postman-Token: e25536fa-3dfa-ddcb-8ca6-3f3552d2bc40
Cache-Control: no-cache
Origin: chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop
docMetaInfo: some extra data needed
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarybX9MyWBsuLGm6QIC

x-api-key: *********************
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8

------WebKitFormBoundarybX9MyWBsuLGm6QIC
Content-Disposition: form-data; name=""; filename="Test.txt"
Content-Type: text/plain

This is a test Text File
------WebKitFormBoundarybX9MyWBsuLGm6QIC--

我从网关方面尝试了一些事情,包括更改 Integration Request 以映射相同内容类型的新主体,但没有运气。

据我所知,我应该只需要passthrough 这个电话,因此为什么它变得有点混乱 - 应该不需要数据操作/拦截?

我得到的错误是 400 - 错误请求(抱怨找不到 file),但正如您在请求中看到的那样,它就在那里。

有什么想法吗?

编辑 在同一 APIGateway POST 上来自 CloudWatch 的日志

错误仍然是 400 - 找不到文件

【问题讨论】:

您好,我遇到了完全相同的问题。还没有得到解决方案。你有什么发现吗?我在 aws api 网关中使用了二进制类型。仍然是同样的问题。我没有使用 lambda,而是直接 api 网关到 http 代理直通。 @MihirShah - 是的,如 cmets 中所述。我通过使用 S3 签名 URL 解决了这个问题——它们很简单,事后看来真的很有用。或者,您可以尝试以下其他答案之一,但是,我认为使用具有此功能的 S3 非常适合。 【参考方案1】:

API Gateway 当前不支持多部分表单数据。这正在考虑未来的发展。同时,您需要修改您的客户端以使用多个请求或单个单部分请求。

更新:API Gateway 现在支持二进制负载。只需将 multipart/form-data 定义为您的 API 的二进制媒体类型,并将有效负载直接代理到 Lambda 函数。从那里您可以解析正文以获取文件内容。应该有可用的库来帮助解析多部分正文(例如 Node.js 中的parse-multipart)。

【讨论】:

@RyanG-AWS 这没有意义。如果 passthrough 没有将请求透明地传递到后端......那么它会做什么? (为什么它被称为“直通”?)这是一个简单的POSTContent-Type: multipart/form-data——称它为“多部分上传”听起来也不太恰当。 我使用了“上传”一词,因为多部分表单数据通常包含文件内容作为内容部分之一。为了清楚起见,我编辑了帖子。请记住,API Gateway 不是一个纯粹的反向代理——API Gateway 数据平面即使在“直通”模式下也会对请求数据执行操作,并且特定的 HTTP 功能(例如 multipart/form-data)是在一个案例上实现的——以个案为基础。话虽如此,我理解这种混乱,我会认为这是一个限制。我们将考虑优先考虑这项工作。 嗨@Matt,是的 - 遵循此限制的最简单方法是使用 AWS 开发工具包和 S3 签名 URL。请参阅以下链接:docs.aws.amazon.com/AmazonS3/latest/dev/… 和 docs.aws.amazon.com/AmazonS3/latest/dev/… @RyanG-AWS 是否将multipart/form-data 作为二进制媒体类型也匹配multipart/form-data; boundary=90a8997f @RyanG-AWS “API 网关现在支持二进制有效负载。只需将“multipart/form-data”定义为二进制媒体类型”。这是否记录在某处?【参考方案2】:

对于那些仍然需要帮助的人,现在正式记录在案:

https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings-configure-with-console.html

总结一下,步骤如下:

    转到您的 API 的 API Gateway 设置选项卡,并将 multipart/form-data 添加到 二进制媒体类型部分。 将Content-TypeAccept 添加到代理方法的请求标头中 将这些相同的标头添加到集成请求标头中 重新部署 API

【讨论】:

这很有帮助,谢谢!补充一点,如果您使用的是 Lambda 代理集成(proxy+,则只需第 1 步和第 4 步即可。 值得注意的是,一旦添加了二进制媒体类型,lambda 的“body”中的内容将被 base64 编码。【参考方案3】:

我在与我的 tomcat 服务器集成时遇到了同样的问题,我发现需要进行以下更改来修复它。

    通过控制台在 api 网关的 api 的 HTTP 请求标头 中添加 Content-Type 或将其添加到开放的 api 文档中,如

    
        "/yourApi":
            "post":
                "operationId":"uploadImageUsingPOST",
                "produces":[
                    "application/json"
                ],
                "parameters":[
                
                    "name":"Content-Type",
                    "in":"header",
                    "required":false,
                    "type":"string"
                ,
                
                    //Other headers
                ]   
            
        
    

    上述步骤还在您的 api 的集成请求的 HTTP 标头中添加 Content-Type,如果不添加它,请在其中添加一个标头 Accept ='/'通过控制台的 api 网关或将其添加到开放的 api 文档中,如

    "requestParameters":
        "integration.request.header.Accept":"'*/*'",
        "integration.request.header.Content-Type":"method.request.header.Content-Type",
        //Other headers
    
    

    在您的 api 集成请求中将内容处理设置为直通。

    通过控制台或打开 api 文档在您的 api 设置中添加 multipart/form-data 作为二进制媒体类型

    "x-amazon-apigateway-binary-media-types": [
        "multipart/form-data"
    ]
    

    将上述更改部署到您要将图像作为分段上传的所需阶段。

Api 网关会将你的多部分文件作为二进制数组传递,你仍然可以在控制器中使用 @RequestBody MultipartFile multipartFile,spring 会为你解析这个二进制文件为多部分。

【讨论】:

第 1 点中的 json 无效.. 一些右括号不正确【参考方案4】:

已解决:https://github.com/mscdex/busboy/issues/199#issuecomment-505239005

我在 node.js 中为 multipart-form-data 使用 express-fileupload

然后只在 AWS API Gateway 上配置设置

选择 API => 设置 => 二进制媒体类型 =>

现在没有损坏 formdata 中的任何文件,一切正常。

【讨论】:

您仍然会遇到 10MB 有效负载大小的问题。 S3 是要走的路——我学到了很艰难的路。 然后重新部署你的 lambda。以防万一有人也像我一样忘记它。 :D【参考方案5】:

似乎发生了变化,API Gateway 不再对整个 Content-Type 标头值进行严格匹配,因此现在所有“二进制”支持都按预期工作。

将您的 API 设置为 POST(或 PUT)并将 Lambda 集成设置为“代理”。转到您的 API 的设置并添加您要用作“二进制”的媒体类型。我添加了multipart/signed。 接收到的媒体类型其实是: Content-Type: multipart/signed; protocol="application/pkcs7-signature"; micalg="sha256"; boundary="----54645645645664564563424768"

API GW 仍将其作为“二进制”提取,并将其作为 base64 交付给我的 Lambda。

在您的 Lambda 中,您将捕捉到这个:

Context:

  "callbackWaitsForEmptyEventLoop": true,
  "logGroupName": "/aws/lambda/api-invoice",
  "logStreamName": "2018/04/27/[$LATEST]3454",
  "functionName": "api-invoice",
  "memoryLimitInMB": "128",
  "functionVersion": "$LATEST",
  "invokeid": "345-49e2-11e8-34-345",
  "awsRequestId": "345-49e2-11e8-34-345",
  "invokedFunctionArn": "arn:aws:lambda:eu-west-1:12345:function:api-invoice"

-------
Event:

  "resource": "/peppol/as2",
  "path": "/peppol/as2",
  "httpMethod": "POST",
  "headers": 
    "Accept": "*/*",
    "AS2-From": "PEPPOL_AP",
    "AS2-To": "234567890",
    "AS2-Version": "1.1",
    "cache-control": "no-cache",
    "Content-Type": "multipart/signed; protocol=\"application/pkcs7-signature\"; micalg=\"sha256\"; boundary=\"----54645645645664564563424768\"",
    "Date": "Fri, 27 Apr 2018 06:17:10 GMT",
    "Disposition-Notification-Options": "signed-receipt-protocol=optional, pkcs7-signature; signed-receipt-micalg=optional, sha1,md5",
    "Disposition-Notification-To": "ignored@example.com",
    "Host": "123.execute-api.eu-west-1.amazonaws.com",
    "Message-ID": "<456-9d44-4c61-456-456546@172.17.0.3>",
    "MIME-Version": "1.0",
    "Postman-Token": "ert-59c1-45656-94d1-456546",
    "Recipient-Address": "as2s://123.execute-api.eu-west-1.amazonaws.com/dev/peppol/as2",
    "Subject": "234567890;PEPPOL_AP",
    "User-Agent": "PostmanRuntime/7.1.1",
    "Via": "1.1 ert-",
    "X-Amzn-Trace-Id": "Root=1-4556-ertfd6554",
    "X-CLIENT-IP": "172.17.0.1",
    "X-Forwarded-For": "xx.xxx.xx.80",
    "X-Forwarded-Port": "443",
    "X-Forwarded-Proto": "https"
  ,
  "queryStringParameters": null,
  "pathParameters": null,
  "stageVariables": null,
  "requestContext": 
    "resourceId": "80r6gp",
    "resourcePath": "/peppol/as2",
    "httpMethod": "POST",
    "extendedRequestId": "sdsdd343434=",
    "requestTime": "27/Apr/2018:06:17:11 +0000",
    "path": "/dev/peppol/as2",
    "accountId": "123",
    "protocol": "HTTP/1.1",
    "stage": "dev",
    "requestTimeEpoch": 1524809831262,
    "requestId": "354-49e2-3445-b2ba-535345",
    "identity": 
      "cognitoIdentityPoolId": null,
      "accountId": null,
      "cognitoIdentityId": null,
      "caller": null,
      "sourceIp": "xx.xxx.xx.80",
      "accessKey": null,
      "cognitoAuthenticationType": null,
      "cognitoAuthenticationProvider": null,
      "userArn": null,
      "userAgent": "PostmanRuntime/7.1.1",
      "user": null
    ,
    "apiId": "123"
  ,
  "body": "VGhpcyBpcyBhbiBTL01/ [snip] /S0NCg==",
  "isBase64Encoded": true

【讨论】:

正确,API 网关现在支持二进制,但问题是您仍然受到有效负载/请求长度限制的限制 - 对于二进制文件,这是一个问题。虽然现在这适用于小文件,但我仍然认为 S3 签名 URL 是最好的前进方式/最佳实践方案。 是的,10MB 用于对 API GW 的任何请求。有一个最大值。 Lambda 为 6MB,但我认为它已被删除/增加,因为我们通过 Lambda 处理了大于 6MB 的文件... 正确 - lambda 超时现在增加到 15 分钟(仅供参考) @Hexie 是 API 客户端的预签名 URL 的两步过程?调用者是否需要调用 API 来获取预签名 URL,然后使用该 URL 上传文件? @LP13 是的,这正是它的工作原理。他们获取一个预签名的 URL,然后将其用于所需的发布/工作。这是一个非常快速的过程,避免了使用其他方法会遇到的任何限制。【参考方案6】:

使用 AWS API Gateway Proxy+ 和 Lambda 从服务器上传和检索图像 通过 AWS Gateway 和 lambda 代理上传和检索图像以及管理二进制数据的代码示例+

Check here

【讨论】:

以上是关于API 网关 - POST 多部分/表单数据的主要内容,如果未能解决你的问题,请参考以下文章

post api调用和使用post方法提交表单有啥区别?

使用 CORS 到 API 网关的 AJAX POST 不起作用,但可以使用 CURL 进行 POST

如何通过 lambda 和 api 网关将我的 blob 上传到我的 s3 存储桶?

Facebook API 使用带有多部分表单数据的 HTTP POST 请求的错误响应

将表单从客户端发布到 AWS API 网关功能时如何修复 CORS 错误?

API网关GET失败,出现 "缺少认证令牌"(但POST正常)。