How to let API Gateway Proxy Error Responses? - amazon-web-services

I've got an API Gateway in front of a Lambda.
Successful responses from the Lambda (HTTP 2xx) have the response body forwarded.
However, for error responses (HTTP 5xx and others), the API Gateway transforms the response body using response templates.
Is there a way to avoid this? To have the original error response body from the Lambda?
In my Lambda I have this:
return callback(generalError, {
statusCode: 500,
headers:{
"content-type": "application/json"
},
body: JSON.stringify({
error: 'INTERNAL_ERROR',
description: error.message,
})
});
However, as output from the Gateway I get this:
{ "error": "Internal server error" }
Which doesn't match. The Lambdas response. It does match the response template in API Gateway:
{"message":$context.error.messageString}
However, is there a way to just proxy the original Lambda response instead of having this transformation in place?

I've found the reason why it doesn't work.
If you set a callback to a 500 error, including an object with an error field, somehow this will become the full response, regardless of the other output or real error.
Avoiding using an error field does the trick! I renamed it to serviceError and now I'm getting the responses I expected.

Related

API Gateway Mapping Template for SNS not recognising TopicArn parameter

I have a POST method which integrates with SNS. It initially accepted a TopicArn query string parameter and a Message, however to avoid client applications having knowledge of the ARN, and to move the message content into the request body, I needed to make use of an API Gateway mapping template.
I added the below Velocity (VTL) code under an application/json mapping template (fake account ID):
TopicArn=$util.urlEncode('arn:aws:sns:eu-west-2:1234567890123:MyTopic')
&Subject=$util.urlEncode('Contact form message')
&Message=$util.urlEncode($input.body)
I ran a test using the console with a body of:
{
"name": "test",
"email": "test#test.com",
"message": "test message"
}
but this returns the error:
Invalid parameter: TopicArn or TargetArn Reason: no value for required parameter
Endpoint request body after transformations: TopicArn=arn%3Aaws%3Asns%3Aeu-west-2%1234567890123%3AMyTopic
&Subject=Contact+form+message
&Message=%7B%0A++++%22name%22%3A+%22test%22%2C%0A++++%22email%22%3A+%22test%40test.com%22%2C%0A++++%22message%22%3A+%22test+message%22%0A%7D
Sending request to https://sns.eu-west-2.amazonaws.com/?Action=Publish
Received response. Status: 400
I'm not particularly familiar with VTL, but I literally copied some example syntax from AWS docs yet the TopicArn parameter doesn't appear to be passed to SNS.
Can anyone see what I've done wrong here?

API GATEWAY not retreiving request body

I'm trying to create a POST endpoint in API gateway with request body in application/json type. Now, in input body mapping template, I want to check if the input is there and if the required fields are present or not. I tried getting input body using $input.body and also tried $input.json('$') and $input.path('$'). Nothing works, input body is always empty, although the $input.body == "" check always returns false. But in the test logs i can see that the body is passed through. I'm using Mock as integration type. What can be an issue?
I found out the answer to this is that, actually the reponse body isn't accessible when we use MOCK integration as provided by AWS, but we can still access body using a hacky method by:
First, in the integration request mapping template you store the body in a path parameter.
#set($context.requestOverride.path.body = $input.body)
{
"statusCode": 200,
}
Then, in the integration response mapping template you fetch it back and return it.
#set($body = $context.requestOverride.path.body)
{
"statusCode": 200,
"body": $body,
}
And then to parse it:
$util.parseJson($body).varName

XMLHttpRequest error while using http post flutter web

I am facing this error XMLHttpRequest error. while making an HTTP post call to my API-AWS API Gateway. My current Flow is Flutter web -> API gateway -> lambda -> rds.
I know there are already a couple of question-related to this like but as suggested in one of the answers to add some headers in response to lambda. but it doesn't work for me.
After doing some research I found out that the problem is regarding to CORS. now disabling cors in chrome is a temporary fix and suggested in this question.
some other solution that I found after researching suggested to enabled cors in my API and also in the frontend part I have added headers but none of them works.
fetchData() async {
String url =
"myUrl";
Map<String, String> headers = {
"Access-Control-Allow-Origin": "*", // Required for CORS support to work
};
String json = '{"emailId":"emailId"}';
http.Response response =
await http.post(Uri.parse(url), headers: headers, body: json);
print(response.body);
return response.body;
}
what is the correct way of solving this problem?
1- Go to flutter\bin\cache and remove a file named: flutter_tools.stamp
2- Go to flutter\packages\flutter_tools\lib\src\web and open the file chrome.dart.
3- Find '--disable-extensions'
4- Add '--disable-web-security'
I have Solved my problem, and not going to delete this question because there aren't many well-defined solutions to this problem.
For Future viewer who is using flutter web and AWS API-gateway.
if you encounter this problem it means its from backend side not from flutter side
XMLHttpRequest error. is caused due to CORS
The solution to the problem you have to enable CORS in api-gateway follow this link.
but if you are using proxy integration with lambda and api-gateway then in that case enabling CORS doesn't going to help, you have to pass on headers from the response of lambda function. like
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*", // Required for CORS support to work
"Access-Control-Allow-Credentials": true, // Required for cookies, authorization headers with HTTPS
"Access-Control-Allow-Headers": "Origin,Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,locale",
"Access-Control-Allow-Methods": "POST, OPTIONS"
},
body: JSON.stringify(item)
};
the format needs to be the same. also, one particular question that helps me a lot to understand this whole issue is going through the various answer of the question link.
Now comes my problem, what I'm doing wrong i that i am passing "Access-Control-Allow-Origin": "*", from frontend and enabling CORS in API gateway also send similar headers which are creating a problem for me
Access to XMLHttpRequest at 'API-URL' from origin 'http://localhost:63773' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. //this particular line
So after changing my function to this everything works perfectly fine
fetchData() async {
String url =
"API-url";
Map<String, String> headers = {
"Content-Type": "text/plain",
};
String json = '{"emailId":"emailId"}';
Map<String, String> map = Map();
map["emailId"] = "fake#gmail.com";
http.Response response = await http
.post(Uri.parse(url), headers: headers, body: jsonEncode(map))
.then((value) {
print("onThen> " + value.body.toString());
}).onError((error, stackTrace) {
print("onError> " +
error.toString() +
" stackTrace> " +
stackTrace.toString());
});
}
In flutter web api Access-Control-Allow-Origin use in header to might resolve this issue.
header("Access-Control-Allow-Origin: header");
in your backend php file add this code
<?php
header("Access-Control-Allow-Origin: *");
finish!

Custom response Lambda Authorizer for 401

Calling the Lambda callback function from a Lambda Authorizer with the string Unauthorized in the error parameter returns a 401 response with the body:
{ "message": "Unauthorized" }
Trying to use any other string in the response results in the response:
{ "message": null }
If instead you return a Deny Policy Document in the result parameter of the callback, you'll get a 403 with the response something like:
{ "message": "Unable to access resource with an explicit deny" }
After looking around it seems you need to configure a Gateway Response to return a custom response from a Lambda Authorizer, which I have working for the 403 response, but can't figure out how to do this for a 401.
For the 403 I created a Gateway Response with the template:
{\"message\":\"$context.authorizer.stringKey\"}
Then on the result object I set the following
ResultObject.context.stringKey = 'My custom response'
This works and is documented here.
However, for the 401, because I am not returning a policy document I don't know how to use a custom response. I created the same Gateway Response as I did for the 403, but if I hit the callback with any string (other than 'Unauthorized') in the error param I get the null message. I can't return in the result param because this needs to be a response structure containing the Policy Document.
Any ideas on how I can return a custom response with a 401?
Sorry to not answer your direct question, but I do think people (like me) might encounter this thread when looking on how to implement the first part of your question (return a 401 response from the authorizer lambda). You can follow AWS example here.
TL;DR:
For async functions, throw an error whose message exactly match the string "Unauthorized":
exports.handler = async function (event) {
...
throw Error("Unauthorized");
}
For sync. functions, call the callback function with its first parameter (the error response) exactly match the string "Unauthorized":
exports.handler = function(event, context, callback) {
..
callback("Unauthorized"); // Compared to a successful response `callback(null, ...)`
}
In both cases the response from the API gateway endpoint protected by your authorizer lambda would be:
401
{
"message": "Unauthorized"
}
You need to raise an exception, so when using node:
context.fail("Unauthorized");
For C# see http://yogivalani.com/aws-custom-lambda-authorizer-returns-401-unauthorized/

AWS API Gateway error response generates 502 "Bad Gateway"

I have an API Gateway with a LAMBDA_PROXY Integration Request Type. Upon calling context.succeed in the Lambda, the response header is sent back with code 302 as expected (shown below). However, I want to handle 500 and 404 errors, and the only thing I am sure about so far, is that I am returning the error incorrectly as I am getting 502 Bad Gateway. What is wrong with my context.fail?
Here is my handler.js
const handler = (event, context) => {
//event consists of hard coded values right now
getUrl(event.queryStringParameters)
.then((result) => {
const parsed = JSON.parse(result);
let url;
//handle error message returned in response
if (parsed.error) {
let error = {
statusCode: 404,
body: new Error(parsed.error)
}
return context.fail(error);
} else {
url = parsed.source || parsed.picture;
return context.succeed({
statusCode: 302,
headers: {
Location : url
}
});
}
});
};
If you throw an exception within the Lambda function (or context.fail), API Gateway reads it as if something had gone wrong with your backend and returns 502. If this is a runtime exception you expect and want to return a 500/404, use the context.succeed method with the status code you want and message:
if (parsed.error) {
let error = {
statusCode: 404,
headers: { "Content-Type": "text/plain" } // not sure here
body: new Error(parsed.error)
}
return context.succeed(error);
I had the same problem, in my case the issue was that my function was not returning anything in context.done(). So instead of context.done(null), I did context.done(null, {});
I've gotten 502's from multiple things. Here are the ones I have figured out so far.
Answer 1:
claudia generate-serverless-express-proxy --express-module {src/server?}
If you are not using claudia and express, this answer won't help you.
Answer 2:
Lambda function->Basic Settings->Timeout. Increase it to something reasonable. It defaults to 3 seconds. But the first time building it typically takes longer.
I had a problem like this, I was returning JSON as a JavaScript Object in the body, but you are supposed to return it as a string. All I had to do was do a JSON.stringify(dataobject) to convert the JSON into a string before returning it.
https://aws.amazon.com/premiumsupport/knowledge-center/malformed-502-api-gateway/