AWS API Gateway return HTML - amazon-web-services

I am using the AWS labs' serverless container (https://github.com/awslabs/aws-serverless-java-container) to handle the lambda entrypoint and response for a lambda that returns HTML. It appears that it calls the lambda and returns the HTML just fine from the lambda. However, API Gateway then throws a fit processing the response. I am using the API Gateway as a proxy, not configuring individual endpoints.
Wed Jun 21 20:53:29 UTC 2017 : Endpoint response body before transformations: ---
statusCode: 200
headers:
Content-Type: "text/html"
body: "\r\n\r\nhttp://www.w3.org/1999/xhtml\"\r\n \
\ lang=\"en\">\r\n \r\n \r\n \
\ Page Title\r\n \r\n \r\n \
\ \r\n \r\n\
\ \r\n \r\n ... [TRUNCATED]
Wed Jun 21 20:53:29 UTC 2017 : Endpoint response headers: {x-amzn-Remapped-Content-Length=0, x-amzn-RequestId=adb2b101-56c3-11e7-afc6-8383d836980f, Connection=keep-alive, Content-Length=17551, Date=Wed, 21 Jun 2017 20:53:29 GMT, X-Amzn-Trace-Id=root=1-594adcc9-6987c6ed102696c505538b02;sampled=0, Content-Type=application/octet-stream}
Wed Jun 21 20:53:29 UTC 2017 : Execution failed due to configuration error: Malformed Lambda proxy response
Wed Jun 21 20:53:29 UTC 2017 : Method completed with status: 502
As you can see from the logs, the AWS own Java object, AwsProxyResponse, properly wraps the HTML content how AWS coded it. You see it returning the proper body and text/html headers. It seems API Gateway then blows up handling a response from AWS's own response.
How do I get AWS Gateway to properly handle the response when the response form the lambda is Content-Type: test/html?

The response from the Lambda function does not look correct. It appears a raw string without any format.
It should be JSON in the format:
{
"statusCode": num,
"headers" : {
"key" : "value"
},
"body" : "anything"
}

Related

API gateway SQS response ends with UnknownOperationException

I have an API Gateway which routes requests to SQS, API Gateway has a the needed permission to send messages to SQS (Created IAM role), when I send test data, I'll get the response below with UnknownOperationException.
SQS is also empty with no messages available.
Tue Nov 09 13:46:11 UTC 2021 : Sending request to https://sqs.ca-central-1.amazonaws.com/XXXXXXXX/XXXX
Tue Nov 09 13:46:11 UTC 2021 : Received response. Status: 404, Integration latency: 2 ms
Tue Nov 09 13:46:11 UTC 2021 : Endpoint response headers: {x-amzn-RequestId=e63b5577-9866-569d-85d8-0f73e5851cf0, Date=Tue, 09 Nov 2021 13:46:11 GMT, X-Amzn-Trace-Id=Root=1-618a7ba3-dd93ee11df45d437f9c98705, Content-Type=null, Content-Length=29}
Tue Nov 09 13:46:11 UTC 2021 : Endpoint response body before transformations: <UnknownOperationException/>
Tue Nov 09 13:46:11 UTC 2021 : Method response body after transformations: <UnknownOperationException/>
Any ideas?
To fix that issue you have to add the Content-Type:'application/x-www-form-urlencoded' header to the integration request:
integration request headers

AWS Apigateway + Simple System Mangement + Integration

I'm trying to resolve an ssm parameter throught AWS Apigateway, but i can't find any example.
The cloud formation part where I define the method integration is the follow:
Integration:
Type: AWS
Uri: !Sub "arn:aws:apigateway:us-west-2:ssm:action/GetParameter"
IntegrationHttpMethod: POST
PassthroughBehavior: NEVER
RequestTemplates:
application/json: '
{
"Name": "/dev/configs/xoxo"
}
'
When I test it in the AWS console this are the logs
Mon Aug 10 18:14:10 UTC 2020 : Method request body before transformations:
Mon Aug 10 18:14:10 UTC 2020 : Endpoint request URI: https://ssm.us-west-2.amazonaws.com/?Action=GetParameter
Mon Aug 10 18:14:10 UTC 2020 : Endpoint request headers: {Authorization=**********************************************************************************************************************************************************************************************************************************************************************dbf24f, X-Amz-Date=20200810T181410Z, x-amzn-apigateway-api-id=mrvijesndc, Accept=application/json, User-Agent=AmazonAPIGateway_mrvijesndc, X-Amz-Security-Token=...[TRUNCATED]
Mon Aug 10 18:14:10 UTC 2020 : Endpoint request body after transformations: { "Name": "/dev/configs/xoxo"}
Mon Aug 10 18:14:10 UTC 2020 : Sending request to https://ssm.us-west-2.amazonaws.com/?Action=GetParameter
Mon Aug 10 18:14:10 UTC 2020 : Received response. Status: 400, Integration latency: 43 ms
Mon Aug 10 18:14:10 UTC 2020 : Endpoint response headers: {Server=Server, Date=Mon, 10 Aug 2020 18:14:10 GMT, Content-Type=application/json, Content-Length=220, Connection=keep-alive, x-amzn-RequestId=c2e56a30-d846-4cd6-b4af-1df95267a3fd}
Mon Aug 10 18:14:10 UTC 2020 : Endpoint response body before transformations: {"Error":{"Code":"ValidationError","Message":"1 validation error detected: Value null at 'name' failed to satisfy constraint: Member must not be null.","Type":"Sender"},"RequestId":"c2e56a30-d846-4cd6-b4af-1df95267a3fd"}
I suppose it is not making the call correctly, could anyone help me ?
Seems like the SSM API endpoint, when called in this manner, expects to find its parameters in the query string, not in the request body. So the following ought to work: (not tested as such, because I use Terraform instead of CloudFormation)
Integration:
Type: AWS
Uri: !Sub "arn:aws:apigateway:us-west-2:ssm:action/GetParameter"
IntegrationHttpMethod: POST
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestParameters:
integration.request.querystring.Name: "'/dev/configs/xoxo'"
Well, after a long research, google hide this post for me along 8 hours, finally, I found it:
https://forums.aws.amazon.com/thread.jspa?threadID=288118
I hope be helpfully for someone like me in the future

Cloudwatch Logs PutLogEvents action fails with com.amazon.coral.service#UnknownOperationException when called from API Gateway

I'm using API Gateway's AWS Service integration type to add logs to the Cloudwatch Logs service using the PutLogEvents action, as described here: https://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutLogEvents.html
I've successfully setup a similar type of method to add items into a DynamoDB table using the PutItem action and that worked just fine, so I'm not sure what I'm missing with this one.
I've double checked for typos and problems with my template. Have successfully used PutLogEvents using the CLI tools, so the setup seems all OK.
Here's some screenshots of my setup from the AWS dash:
Here's the mapping template I'm using:
{
"logGroupName": "FromAPI",
"logStreamName": "$input.path('$.streamName')",
"logEvents": [
{
"timestamp": $input.path('$.ts'),
"message": "$input.path('$.message')"
}
]
}
The response I get back has a 200 status code, but the following response body:
{
"Output": {
"__type": "com.amazon.coral.service#UnknownOperationException",
"message": null
},
"Version": "1.0"
}
Here's a redacted (xxx) execution log:
Execution log for request xxx
Fri Apr 19 02:28:58 UTC 2019 : Starting execution for request: xxx
Fri Apr 19 02:28:58 UTC 2019 : HTTP Method: POST, Resource Path: /log
Fri Apr 19 02:28:58 UTC 2019 : Method request path: {}
Fri Apr 19 02:28:58 UTC 2019 : Method request query string: {}
Fri Apr 19 02:28:58 UTC 2019 : Method request headers: {}
Fri Apr 19 02:28:58 UTC 2019 : Method request body before transformations: {
"streamName": "12345",
"ts": 1555641510000,
"message": "help!"
}
Fri Apr 19 02:28:58 UTC 2019 : Endpoint request URI: https://logs.xxx.amazonaws.com/?Action=PutLogEvents
Fri Apr 19 02:28:58 UTC 2019 : Endpoint request headers: {Authorization=xxx, X-Amz-Date=20190419T022858Z, x-amzn-apigateway-api-id=xxx, Accept=application/json, User-Agent=AmazonAPIGateway_xxx, X-Amz-Security-Token=xxx [TRUNCATED]
Fri Apr 19 02:28:58 UTC 2019 : Endpoint request body after transformations: {
"logGroupName": "FromAPI",
"logStreamName": "12345",
"logEvents": [
{
"timestamp": 1555641510000,
"message": "help!"
}
]
}
Fri Apr 19 02:28:58 UTC 2019 : Sending request to https://logs.xxx.amazonaws.com/?Action=PutLogEvents
Fri Apr 19 02:28:58 UTC 2019 : Received response. Status: 200, Integration latency: 38 ms
Fri Apr 19 02:28:58 UTC 2019 : Endpoint response headers: {x-amzn-RequestId=xxx, Content-Type=application/json, Content-Length=105, Date=Fri, 19 Apr 2019 02:28:58 GMT}
Fri Apr 19 02:28:58 UTC 2019 : Endpoint response body before transformations: {"Output":{"__type":"com.amazon.coral.service#UnknownOperationException","message":null},"Version":"1.0"}
Fri Apr 19 02:28:58 UTC 2019 : Method response body after transformations: {"Output":{"__type":"com.amazon.coral.service#UnknownOperationException","message":null},"Version":"1.0"}
Fri Apr 19 02:28:58 UTC 2019 : Method response headers: {X-Amzn-Trace-Id=Root=xxx, Content-Type=application/json}
Fri Apr 19 02:28:58 UTC 2019 : Successfully completed execution
Fri Apr 19 02:28:58 UTC 2019 : Method completed with status: 200
Nothing gets logged into my Cloudwatch Logs stream - the API Gateway integration request response body contains an UnknownOperationException error.
My best guess is that this request is not mapping to the PutLogEvents action for some reason. Strange that the status code is 200 in this situation.
I'm guessing it's just some typo - or an additional header that I need to send through? Any ideas?
It should work if you add the following lines at the top of your Mapping Template:
#set($context.requestOverride.header['X-Amz-Target'] = "Logs_20140328.PutLogEvents")
#set($context.requestOverride.header['Content-Type'] = "application/x-amz-json-1.1")
This is very tricky and not well documented. You can find those headers in the sample request for PutLogEvents.

How to invoke Lambda asyncronously from API Gateway?

As per here, I believe that setting a header of X-Amz-Invocation-Type: Event should set my Lambda invocation to be asynchronous.
However, by putting a import time;time.sleep(5000) at the beginning of my Lambda function, and sending requests to my API Gateway, I observe that:
$ aws apigateway get-integration --rest-api-id <api-id> \
--resource-id <resource-id> \
--http-method POST | jq -r '.requestParameters'
{
"integration.request.header.X-Amz-Invocation-Type": "'Event'"
}
$ aws apigateway get-integration --rest-api-id <api-id> \
--resource-id <resource-id> \
--http-method POST | jq -r '.uri'
arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:<account-id>:function:[...]Lambda-4HOA0ZSFAYCI/invocations
$ curl https://<api-id>.execute-api.us-east-1.amazonaws.com/LATEST/<path> -d '{}'
{"message": "Endpoint request timed out"}
[here I removed the sleep from my lambda function and made it return immediately]
$ curl https://<api-id>.execute-api.us-east-1.amazonaws.com/LATEST/<path> -d '{}'
{"body": "Request OK", "headers": {"Content-Type": "application/json"}, "statusCode": 200}
Assuming that the documentation is accurate, my best guess is that I've misconfigured the Integration somehow - perhaps the "'Event'" is incorrect and it should be "Event"? I'm pretty sure the single-quotes are required, however, to demarcate a static value (as opposed to value parsed from the request). In particular, I suspect that my responseParameters are not right:
$ aws apigateway get-integration --rest-api-id <api-id> \
--resource-id dzv1zj \
--http-method POST | jq -r '.integrationResponses'
{
"200": {
"responseTemplates": {
"application/json": null
},
"statusCode": "200"
}
}
Should null there be some VTL that staticly returns a 200 OK?
As for alternatives: I see that invoke-async is deprecated. I'd really rather not go to the overhead of going API Gateway -> SNS -> Lambda.
EDIT: Here are the logs from calling the API via the "Test" option on the console:
Execution log for request test-request
Wed Mar 07 17:24:57 UTC 2018 : Starting execution for request: test-invoke-request
Wed Mar 07 17:24:57 UTC 2018 : HTTP Method: POST, Resource Path: /<path>
Wed Mar 07 17:24:57 UTC 2018 : Method request path: {}
Wed Mar 07 17:24:57 UTC 2018 : Method request query string: {}
Wed Mar 07 17:24:57 UTC 2018 : Method request headers: {}
Wed Mar 07 17:24:57 UTC 2018 : Method request body before transformations: {"abc":"def"}
Wed Mar 07 17:24:57 UTC 2018 : Endpoint request URI: https://lambda.us-east-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-east-1:<account-id>:function:[...]Lambda-4HOA0ZSFAYCI/invocations
Wed Mar 07 17:24:57 UTC 2018 : Endpoint request headers: {X-Amz-Date=20180307T172457Z, x-amzn-apigateway-api-id=<api-id>, Accept=application/json, User-Agent=AmazonAPIGateway_<api-id>, Host=lambda.us-east-1.amazonaws.com, X-Amz-Content-Sha256=2c3fbda5f48b04e39d3a87f89e5bd00b48b6e5e3c4a093de65de0a87b8cc8b3b, X-Amzn-Trace-Id=Root=1-5aa02069-8670eb5d98dbc4ade9df03d8, x-amzn-lambda-integration-tag=test-request, Authorization=**********************************************************************************************************************************************************************************************************************************************************************************************************************************************bfe3de, X-Amz-Source-Arn=arn:aws:execute-api:us-east-1:<account-id>:<api-id>/null/POST/<path>, X-Amz-Invocation-Type=Event, X-Amz-Security-Token=[REDACTED] [TRUNCATED]
Wed Mar 07 17:24:57 UTC 2018 : Endpoint request body after transformations: {"abc":"def"}
Wed Mar 07 17:24:57 UTC 2018 : Sending request to https://lambda.us-east-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-east-1:<account-id>:function:[...]Lambda-4HOA0ZSFAYCI/invocations
Wed Mar 07 17:24:57 UTC 2018 : Received response. Integration latency: 43 ms
Wed Mar 07 17:24:57 UTC 2018 : Endpoint response body before transformations:
Wed Mar 07 17:24:57 UTC 2018 : Endpoint response headers: {x-amzn-Remapped-Content-Length=0, Connection=keep-alive, x-amzn-RequestId=75501cbf-222c-11e8-a1fc-2b19f19a9429, Content-Length=0, Date=Wed, 07 Mar 2018 17:24:57 GMT, X-Amzn-Trace-Id=root=1-5aa02069-8670eb5d98dbc4ade9df03d8;sampled=0}
Wed Mar 07 17:24:57 UTC 2018 : Method response body after transformations:
Wed Mar 07 17:24:57 UTC 2018 : Method response headers: {X-Amzn-Trace-Id=sampled=0;root=1-5aa02069-8670eb5d98dbc4ade9df03d8, Content-Type=application/json}
Wed Mar 07 17:24:57 UTC 2018 : Successfully completed execution
Wed Mar 07 17:24:57 UTC 2018 : Method completed with status: 200
Now I feel stupid - I needed to deploy my API. That seems like the API Gateway equivalent of "Have you tried turning it off and turning it on again?" :)
I found scubbo's answer but it didn't enlighten me until much later, so I post the same, but with a screenshot for any future lost soul

AWS ApiGateway customize Request Validation Response

I have configured my AWS APIGateway to validate the requests according to a JSON schema.
E.g. the path /vehicle, which has the following schema attached:
{
"type":"object",
"properties":{
"licensePlate":{
"pattern":"[A-Za-z]{1,3} [A-Za-z]{1,2} \\d{1,4}",
"type":"string"
},
"vehicleType":{
"type":"string",
"enum":[
"Truck",
"Trailer"
]
}
},
"required":[
"licensePlate",
"vehicleType"
]
}
This works fine. If I submit an invalid request the API responds with a 400 {"message": "Invalid request body"}. I would like to customize this message, e.g. to
{
"entity": "vehicleType",
"message": "missing"
}
If I take a look at the logs from the Gateway it seems that a similar message is logged (object has missing required properties (["vehicleType"])). Can I use that one? How can I access it?
Logs:
Execution log for request test-request
Thu Feb 01 13:12:18 UTC 2018 : Starting execution for request: test-invoke-request
Thu Feb 01 13:12:18 UTC 2018 : HTTP Method: POST, Resource Path: /vehicle
Thu Feb 01 13:12:18 UTC 2018 : Method request path: {}
Thu Feb 01 13:12:18 UTC 2018 : Method request query string: {}
Thu Feb 01 13:12:18 UTC 2018 : Method request headers: {}
Thu Feb 01 13:12:18 UTC 2018 : Method request body before transformations: {
"licensePlate": "HH AB 123"
}
Thu Feb 01 13:12:18 UTC 2018 : Request body does not match model schema for content type application/json: [object has missing required properties (["vehicleType"])]
Thu Feb 01 13:12:18 UTC 2018 : Method completed with status: 400
Is this possible with the API Gateway?
Yeah what you want is the $context.error.validationErrorString
Like #Bajwa said -- you need to customize the GatewayReponse template. If you're using cloud formation that looks like this:
"GatewayResponse": {
"Type": "AWS::ApiGateway::GatewayResponse",
"Properties": {
"ResponseParameters": {
"gatewayresponse.header.Access-Control-Allow-Origin": "'*'",
"gatewayresponse.header.Access-Control-Allow-Headers": "'*'"
},
"ResponseTemplates": {
"application/json": "{\"error\":{\"message\":\"$context.error.messageString\",\"errors\":\"$context.error.validationErrorString\"}"
},
"ResponseType": "BAD_REQUEST_BODY",
"RestApiId": {
"Ref": "YouRestApiResource"
},
"StatusCode": "400"
}
}
If you violate the request body validator you'll see something like this:
{
"error": {
"message":" "Invalid request body"",
"errors":"[string \"1\" is too short (length: 1, required minimum: 10)]"
}
It's not perfect -- some of the messaging is vague, would be nice if anyone knows how to for example add the property name that caused the violation.
If the request payload is invalid, you will see the same message:
{
"message": "Invalid request body"
}
API Gateway includes more detail, if the request payload format is valid and parameters format is invalid.
See more detail from below link, especially the bottom part.
Test Basic Request Validation in API Gateway
But you may do some customization in the response message by customizing Gateway responses.
You may customize Bad Request Body 400.
{
"message":"$context.error.messageString",
"Hint":"Check required number of parameters or parameters allowed type and length."
}
Then you will see message in below format.
{
"message": "Invalid request body",
"Hint": "Check required number of parameters or parameters allowed type and length."
}
See hot to update Body Mapping Template from attached screenshot. (APIGateway/Gateway Responses).