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?
Related
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.
I'm trying to receive a POST from a twilio widget, but my aws lambda (nodejs) function fails.
{"message": "Could not parse request body into json: Unrecognized token \'RecordingSource\': was expecting (\'true\', \'false\' or \'null\')\n at [Source: (byte[])\"RecordingSource=RecordVerb&RecordingSid=REd9475d9sdfw616e81995366d5f02291506b0&RecordingUrl=https%3A%2F%2Fapi.twilio.com%2F2010-04-01%2FAccounts%2FAC87e46c891a699385%2FRecordings%2FREd9466d5f02291506b0&RecordingStatus=completed&RecordingChannels=1&ErrorCode=0&CallSid=CA4a7f45753ef87894245dc95d445d8672&RecordingStartTime=Sat%2C%2021%20Mar%202020%2014%3A50%3A32%20%2B0000&AccountSid=AC8799385&RecordingDuration=2\"; line: 1, column: 17]"}
My AWS lambda function is very simple.
exports.handler = async (event) => {
console.log('-------------------------');
console.log(event);
console.log('-------------------------');
Your twilio widget is sending in application/x-www-form-urlencoded but your server is attempting to handle application/json. To send json instead is a client side configuration, and your widget will have to support such a configuration.
As #Alan suggests,
you could set the Recording Status Callback to a Twilio Function URL, which can do what you need it to do. Twilio Functions uses Node/JavaScript.
How do I refer to API Gateway GET query string parameters in a Lambda function ?
When I test I can use my local test event with "username": "larry"
When I test with a post I can use the body parameters as the event with "username": "larry"
With a get request, I don't have a body. How could I use query string parameters and then refer to them in the request. What event or other attribute do I use to get at the query param or what setting or change do I need to make?
Method request
Integration request
Query String
When testing I've referred to event["username", what do I do for an API request passing it as a query string parameter?
Login and click to API gateway and click on method - GET method
Go to Method Execution - select URL Query String Parameter and add query String parameter username
Now go to Integration Request tab Choose Body Mapping Template,
"content type application/json"
Generate Template like below
{
"username": "$input.params('username')"
}
Now write ;ambda which accept key value in pair .
module.exports.get = (event, context, callback) => {
const { username } = event.pathParameters;
console.log("username", username);
}
Now go and deploy your api on apigetway and find url and hit in browser example
https://xxx.yyy-api.us-east-2.amazonaws.com/prod/username?vaquarkhan
Hope it will help
https://docs.aws.amazon.com/de_de/apigateway/latest/developerguide/welcome.html
https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-set-up-simple-proxy.html#api-gateway-set-up-lambda-proxy-integration-on-proxy-resource
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/
I have a http endpoint name - HE. This 'HE' endpoint connected with Lambda function 'L'.
So HE-->L
On some situation 'L' return Exception in this format :
{
"errorMessage": "Name John Doe is invalid. Exception occurred...",
"errorType": "java.lang.Exception",
"stackTrace": [
"example.Hello.handler(Hello.java:9)",
"sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)",
"sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)",
"sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)",
"java.lang.reflect.Method.invoke(Method.java:497)"
]
}
And the same returned by the 'HE' as is.
How I can use the Integration Response(AWS API Gateway) at 'HE' so it should only display the following :
{
"errorMessage": "Name John Doe is invalid. Exception occurred..."
}
I'm not sure I understand your set-up correctly. I'm assuming that your API Gateway is calling your Lambda function and that your Lambda function is returning the string you gave as an error object. In that case...
Add a new method response to your method. Set HTTP status of the method response to the HTTP status code you want to return, maybe 400 for this case.
Add a new integration response to your method.
Set the "Lambda Error Regex" of the integration response to a regex which will uniquely identify the error. ".Name . is invalid.*" should work for this case.
Set the "Method response status" to the HTTP status of the method response (400 in my example).
Hit save. Expand the integration response. Expand "Body Mapping Templates". Click "Add mapping template". Set the Content-Type to "application/json" (or whatever content type you want). Click the check box.
In the mapping template editor box, add a mapping template like this...
#set($inputRoot = $input.path('$'))
{"errorMessage" : "$input.path('$.errorMessage')"}
If I misunderstood your set-up and your API Gateway is calling an HTTP endpoint, then the approach is similar, but the terminology of some of the terms changes.