问题
Seems my question maybe a little similar to this one.
I have an API within my API Gateway and am doing a HTTP proxy through to an endpoint that POST's multipart/form-data files.
If I call the http endpoint directly (not through the API gateway) - using postman, it works as expected, however, using the API gateway endpoint (through postman) fails.
I have compared both requests (through fiddler and CloudWatch logs) which seem to be identical:
Request for direct API call (working):
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--
Request from the API Gateway (not working):
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--
I have tried a few things from the gateway side, including changing the Integration Request
to map a new body for the same content-type, no luck.
As far as I am aware, I should only need to passthrough
this call, hence why it's becoming a little confusing - there should be no need for data manipulation / interception?
The error I get is 400 - bad request (complaining about the file
not being found), but as you can see in the request, it's there.
Any ideas?
EDIT Logs from CloudWatch on the same APIGateway POST
Error is still 400 - no file found
回答1:
API Gateway does not currently support multipart form data. This is being considered for future development. In the meantime, you will need to modify your client to use multiple requests or a single one-part request.
Update: API Gateway now supports binary payloads. Simply define multipart/form-data
as a binary media type for your API and proxy the payload directly to a Lambda function. From there you can parse the body to get your file content. There should be libraries available to help parse the multipart body (parse-multipart in Node.js for example).
回答2:
For those who still need some help, this is now officially documented:
https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings-configure-with-console.html
To summarize, the steps are as follows:
- Go to the API Gateway settings tab for your API and add "multipart/form-data" to the "binary media types" section.
- Add "Content-Type" and "Accept" to the request headers for your proxy method
- Add those same headers to the integration request headers
- re-deploy
Cheers
回答3:
There seems to have been a change and API Gateway does no longer do a strict matching of the entire Content-Type header value so now everything for "binary" support works as expected.
Set your API to POST (or PUT) and set the Lambda integration to "proxy". Go to Settings for your API and add the media types you want to use as "binary". I've added multipart/signed
.
The received media type is actually:
Content-Type: multipart/signed; protocol="application/pkcs7-signature"; micalg="sha256"; boundary="----54645645645664564563424768"
The API GW still picks this up as "binary" and delivers it as base64 to my Lambda.
In your Lambda you will then catch this:
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
}
回答4:
I had same problem to integrate with my tomcat server, I found below changes needed to fix it.
Add Content-Type in your api's HTTP Request Headers in api gateway by console or add it in open api documentation like
{ "/yourApi":{ "post":{ "operationId":"uploadImageUsingPOST", "produces":[ "application/json" ], "parameters":[ { "name":"Content-Type", "in":"header", "required":false, "type":"string" }, { //Other headers }] } }
Above step also add Content-Type in your api's HTTP Headers of integration request, If not add it there also and add one more header Accept ='/' in api gateway by console or or add it in open api documentation like
"requestParameters":{ "integration.request.header.Accept":"'*/*'", "integration.request.header.Content-Type":"method.request.header.Content-Type", //Other headers }
Set Content Handling as Passthrough in your api's integration request.
Add multipart/form-data as Binary Media Types in your api's settings through console or by open api documentation
"x-amazon-apigateway-binary-media-types": [ "multipart/form-data" ]
Deploy above changes to desired stage where you are going to upload image as multipart.
Api gateway will pass your multipart file as binary array and you can still use @RequestBody MultipartFile multipartFile in your controller and spring will parse this binary to multipart for you.
来源:https://stackoverflow.com/questions/41756190/api-gateway-post-multipart-form-data