I am hoping that someone can help me get a URL Parameter passed from the AWS API Gateway in Lambda.
I am a little confused how to access this inside the Lambda code.
For the API Gateway I have followed these steps:
1) Went to Resources -> Integration Request
2) Clicked on the plus icon next to templates dropdown
3) Explicitly typed application/json in the content-type field
4) For input mapping Entered:
{ "apiToken": "$input.params('apiToken')" }
So for clarification, How do I access the parameter apiToken in my Lambda Function?
I have been looking at the walkthroughts and other docs for a while now and my brain is a little frazzled.
I appreciate all help and feedback :)
Update
So, the issue seemed to be that the AWS website AJAX save did not save what I entered in the mapping (I am on hotel wifi so could be that).
Now, When I test it I get:
Received event: { appToken: '123456' }
In the console log for lambda. However, When I go to the API Gateway, and deploy The response is:
"errorMessage": "Process exited before completing request"
Any Ideas?
P.S The function works in the Lambda Console however, when I test it using Postman [POST + Param] it does not work, with the error message supplied just above
Update 2
Here is my Entire Lambda Function
console.log('Loading event');
var AWS = require('aws-sdk');
var dynamodb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
var http = require('http');
exports.handler = function(event, context) {
var tableName = "clients";
var appToken = event.params.appToken; //gr5f4sgnca25hki98
dynamodb.getItem({
TableName: tableName,
Key: {
ClientID: { S: appToken } }
}, function (err, data)
{
if (err) {
context.done(err);
} else {
var url = data.Item.url.S + data.Item.api.S;
console.log(url);
console.log('Received event:', event.params.appToken);
http.get(url, function(res) {
// Continuously update stream with data
var body = '';
res.on('data', function(d) {
body += d;
});
res.on('end', function() {
var result = body.replace('\\', '');
context.succeed(JSON.parse(result));
});
res.on('error', function(e) {
context.fail("Got error: " + e.message);
});
});
}
});
};
In my test event I have:
{
"method": "POST",
"params": {
"appToken": "gr5f4sgnca25hki98"
}
}
When I run this in Lambda the respnse works, it connects ro an external JSON file and returns it.
However, In Postman I get the error:
"errorMessage": "Process exited before completing request"
Can anyone see why in the console it all works fine and when I try it out in deployment I still get the error?
When I open XCODE and test this url with Alamofire I get this error (if it helps)
Invalid Sequence around character 72
Update 3 (Test from API Console)
When tested from the API console, using:
{
"method": "POST",
"params": {
"appToken": "gr5f4sgnca25hki98"
}
}
in the Request Body. I get:
Request: /sendtoclient
Status: 200
Latency: 1839 ms
Response Body
{ "errorMessage": "Process exited before completing request" }
Response Headers
{"Content-Type":"application/json"}
Logs
Execution log for request test-request
Tue Mar 22 17:28:27 UTC 2016 : Starting execution for request: test-invoke-request
Tue Mar 22 17:28:27 UTC 2016 : HTTP Method: POST, Resource Path: /sendtoclient
Tue Mar 22 17:28:27 UTC 2016 : Method request path: {}
Tue Mar 22 17:28:27 UTC 2016 : Method request query string: {}
Tue Mar 22 17:28:27 UTC 2016 : Method request headers: {}
Tue Mar 22 17:28:27 UTC 2016 : Method request body before transformations: {
"method": "POST",
"params": {
"appToken": "gr5f4sgnca25hki98"
}
}
Tue Mar 22 17:28:27 UTC 2016 : Endpoint request body after transformations: { "appToken": "" }
Tue Mar 22 17:28:29 UTC 2016 : Endpoint response body before transformations: {"errorMessage":"Process exited before completing request"}
Tue Mar 22 17:28:29 UTC 2016 : Endpoint response headers: {x-amzn-Remapped-Content-Length=0, x-amzn-RequestId=123456789, Connection=keep-alive, Content-Length=59, X-Amz-Function-Error=Unhandled, Date=Tue, 22 Mar 2016 17:28:29 GMT, Content-Type=application/json}
Tue Mar 22 17:28:29 UTC 2016 : Method response body after transformations: {"errorMessage":"Process exited before completing request"}
Tue Mar 22 17:28:29 UTC 2016 : Method response headers: {Content-Type=application/json}
Tue Mar 22 17:28:29 UTC 2016 : Successfully completed execution
Tue Mar 22 17:28:29 UTC 2016 : Method completed with status: 200
I'm not sure how to decipher this. Any help would be very much appreciated. I have however noticed this:
Endpoint request body after transformations: { "appToken": "" }
Which for some reason I feel may be significant
You should be using 'event.apiToken' in your function based upon the mapping you gave us. {apiToken} is in your resource name right?
Related
I have the following handler code:
export default function handler(lambda) {
return function (event, context) {
return Promise.resolve()
// Run the Lambda
.then(() => lambda(event, context))
// On success
.then((responseBody) => [200, responseBody])
// On failure
.catch((e) => {
return [500, { error: e.message }];
})
// Return HTTP response
.then(([statusCode, body]) => ({
statusCode,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": true,
},
body: JSON.stringify(body),
}))
// On failure
.catch((e) => {
return [500, { error: e.message }];
});
};
}
whenever I created a new Lambda function, I import this code and write the code base on it, like this:
import handler from "../libs/handler-lib";
export const main = handler(async (event, context) => {
"my code"
}
which works like a charm, but the problem is, I need to access the response body in aws stepfunctions to determine which function to execute next, so I change
body: JSON.stringify(body), to body: body,
Then every function works only in the lambda function console but fails if the lambda is triggered by the aws api gateway.
I think I am missing something obvious here but I cant tell.
Thanks in advance.
here is the full api gateway log:
Execution log for request 8677ed2f-5e9e-4913-8fe9-f1d2f9b2ac12
Sun Nov 15 16:00:51 UTC 2020 : Starting execution for request: 8677ed2f-5e9e-4913-8fe9-f1d2f9b2ac12
Sun Nov 15 16:00:51 UTC 2020 : HTTP Method: GET, Resource Path: /latest-patch-json-data
Sun Nov 15 16:00:51 UTC 2020 : Method request path: {}
Sun Nov 15 16:00:51 UTC 2020 : Method request query string: {}
Sun Nov 15 16:00:51 UTC 2020 : Method request headers: {}
Sun Nov 15 16:00:51 UTC 2020 : Method request body before transformations:
Sun Nov 15 16:00:51 UTC 2020 : Endpoint request URI: https://lambda.ap-northeast-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:xxxxxxxx:function:tactics-api-dev-getLatestPatchJsonData/invocations
Sun Nov 15 16:00:51 UTC 2020 : Endpoint request headers: {x-amzn-lambda-integration-tag=8677ed2f-5e9e-4913-xxxxxxxx, Authorization=*****************************************************************************************************************************************************************************************************************************************************************************************************************************7603ee, X-Amz-Date=20201115T160051Z, x-amzn-apigateway-api-id=f8brp6pbai, X-Amz-Source-Arn=arn:aws:execute-api:ap-northeast-1:296545255814:f8brp6pbai/test-invoke-stage/GET/latest-patch-json-data, Accept=application/json, User-Agent=AmazonAPIGateway_f8brp6pbai, X-Amz-Security-Token=IQoJb3JpZ2luX2VjEIf//////////wEaDmFwLW5vcnRoZWFzdC0xIkgwRgIhALgraV2CJa+NzHo/sSqH4MgbwVveHpse1s9DNkN5B/5JAiEAnghcO0tmUqhMhB+PxZ/lWbApusJKxJSM1zoIAtqFHycqxwMI8P//////////ARABGgw5NjkyMzY4NTQ2MjYiDI4NEh+wCOuUEqvN/iqbA+UIa11YzWxKIHWZ5hBsK7z/zSPD7Jz9S/3SH3L1M/IpjgbFC3g/y7B+tDj1lZ5nuzXbqlpg092hCt5JR1otT7w45xu0yQdZwcVPT9LV [TRUNCATED]
Sun Nov 15 16:00:51 UTC 2020 : Endpoint request body after transformations: {"resource":"/latest-patch-json-data","path":"/latest-patch-json-data","httpMethod":"GET","headers":null,"multiValueHeaders":null,"queryStringParameters":null,"multiValueQueryStringParameters":null,"pathParameters":null,"stageVariables":null,"requestContext":{"resourceId":"b31s1i","resourcePath":"/latest-patch-json-data","httpMethod":"GET","extendedRequestId":"WDmMCE8mtjMFqQg=","requestTime":"15/Nov/2020:16:00:51 +0000","path":"/latest-patch-json-data","accountId":"296545255814","protocol":"HTTP/1.1","stage":"test-invoke-stage","domainPrefix":"testPrefix","requestTimeEpoch":1605456051327,"requestId":"8677ed2f-5e9e-4913-8fe9-f1d2f9b2ac12","identity":{"cognitoIdentityPoolId":null,"cognitoIdentityId":null,"apiKey":"test-invoke-api-key","principalOrgId":null,"cognitoAuthenticationType":null,"userArn":"arn:aws:iam::296545255814:root","apiKeyId":"test-invoke-api-key-id","userAgent":"aws-internal/3 aws-sdk-java/1.11.864 Linux/4.9.217-0.3.ac.206.84.332.metal1.x86_64 OpenJD [TRUNCATED]
Sun Nov 15 16:00:51 UTC 2020 : Sending request to https://lambda.ap-northeast-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:xxxxxxxxxxxx:function:tactics-api-dev-getLatestPatchJsonData/invocations
Sun Nov 15 16:00:52 UTC 2020 : Received response. Status: 200, Integration latency: 805 ms
Sun Nov 15 16:00:52 UTC 2020 : Endpoint response headers: {Date=Sun, 15 Nov 2020 16:00:52 GMT, Content-Type=application/json, Content-Length=284, Connection=keep-alive, x-amzn-RequestId=753115e8-0264-4517-9a2a-f67f045b7011, x-amzn-Remapped-Content-Length=0, X-Amz-Executed-Version=$LATEST, X-Amzn-Trace-Id=root=1-5fb150b3-b7a44f36147dc1a10b321a85;sampled=0}
Sun Nov 15 16:00:52 UTC 2020 : Endpoint response body before transformations: {"statusCode":200,"headers":{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Credentials":true},"body":{"latestPatchVersion":"10.23","message":"PDH is up-to-date, PDH record: cc01f980-273a-11eb-b79f-676006f2be2a lastCheck time: 2020-11-15T16:00:51.996Z","type":"skip update"}}
Sun Nov 15 16:00:52 UTC 2020 : Execution failed due to configuration error: Malformed Lambda proxy response
Sun Nov 15 16:00:52 UTC 2020 : Method completed with status: 502
For lambda proxy, the lambda return object has to be of this shape :
{
"isBase64Encoded": true|false,
"statusCode": httpStatusCode,
"headers": { "headerName": "headerValue", ... },
"body": "..."
}
That means, the body needs to be a string, not an object.
Following is my Lambda handler which is expecting the users data from the queryStringParameters:-
export const lambdaHandler = async (event, context) => {
try {
const numberOfUsersRequested= (event && event.queryStringParameters.users) ? event.queryStringParameters.users : 10;
const users = await generateUsers(numberOfUsersRequested).then(data => data.users);
I'm using AWS SAM to develop my Lambda and I can test it very well using the event.json as an input event to this Lambda locally. Here is a chunk of event.json where I'm passing the queryStringParamters users like this:-
{
"body": "{\"message\": \"mock data\"}",
"resource": "/{proxy+}",
"path": "/path/to/resource",
"httpMethod": "POST",
"isBase64Encoded": false,
"queryStringParameters": {
"users": 200
},
Now, may I know how to pass the same QueryStringParameters from the AWS API Gateway console. Currently, I'm getting this 500 error on the AWS console for the API Gateway:-
{
"message": "Internal server error"
}
Mon Sep 28 01:24:15 UTC 2020 : Endpoint response headers: {Date=Mon, 28 Sep 2020 01:24:15 GMT, Content-Type=application/json, Content-Length=2, Connection=keep-alive, x-amzn-RequestId=0e1f110c-e80c-4ff1-870a-5cafd04167db, x-amzn-Remapped-Content-Length=0, X-Amz-Executed-Version=$LATEST, X-Amzn-Trace-Id=root=1-5f713b3d-4762f9b07ee8c1d7c6623574;sampled=0}
Mon Sep 28 01:24:15 UTC 2020 : Endpoint response body before transformations: {}
Mon Sep 28 01:24:15 UTC 2020 : Execution failed due to configuration error: Output mapping refers to an invalid method response: 200
Mon Sep 28 01:24:15 UTC 2020 : Method completed with status: 500
I have performed the following steps to mitigate the issue but not working. It looks like something is missing :-
1)Added the url query string parameters as users in the Method Request (refer screenshot)
In the Integration Request -> Mapping Templates, added the mapping as application/json:-
{
"users": "$input.params('users')"
}
And finally passing the query string as users=6.
In your case, the event should just be:
{
"users": "6"
}
You can add the following to the beginning of your handler to confirm:
console.log(JSON.stringify(event, null, 2));
Therefore, to get users value you should just use event.users, not event.queryStringParameters.users.
I'm moving some scala code to AWS Lambda and I intend to have it exposed via AWS API Gateway, but I've been struggling to make the whole thing work as soon as I have one parameter.
My (very simple) code looks like this:
class HelloService {
def hello(name: String) = {
"hello there, " + name
}
}
I uploaded the built jar to Lambda and tested it in the AWS console by creating a test event. It returns the right response, as expected.
However, I want this Lambda to be invoked by the API Gateway. I've used both Lambda Proxy Integration and also defined my own Body Mapping Templates. I can't seem to make it work and I keep getting:
{
"message": "Internal server error"
}
with logs:
Execution log for request test-request Mon Jul 03 16:23:21 UTC 2017 :
Starting execution for request: test-invoke-request Mon Jul 03
16:23:21 UTC 2017 : HTTP Method: GET, Resource Path: /car/aaa Mon Jul
03 16:23:21 UTC 2017 : Method request path: {carReg=aaa} Mon Jul 03
16:23:21 UTC 2017 : Method request query string: {} Mon Jul 03
16:23:21 UTC 2017 : Method request headers: {} Mon Jul 03 16:23:21 UTC
2017 : Method request body before transformations: Mon Jul 03
16:23:21 UTC 2017 : Endpoint request URI:
https://lambda.eu-west-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:eu-west-1:879461422967:function:getCarData/invocations
Mon Jul 03 16:23:21 UTC 2017 : Endpoint request headers:
{x-amzn-lambda-integration-tag=test-request,
Authorization=****************************************************************************************************************************************************************************************************************************************************************************************************************************************f8c749, X-Amz-Date=20170703T162321Z, x-amzn-apigateway-api-id=9dwaaf2mdg,
X-Amz-Source-Arn=arn:aws:execute-api:eu-west-1:879461422967:9dwaaf2mdg/null/GET/car/{carReg+},
Accept=application/json, User-Agent=AmazonAPIGateway_9dwaaf2mdg,
X-Amz-Security-Token=FQoDYXdzENn//////////wEaDMO73KD0CHVmggvYvSK3A8H1fpDgYiNK3HDD3ESe1aKYbv1HlGSQ85at3gRGA3kunmxVCxWbXNqR4ojBCn4hvBzdv1/iWD9xRzZQEtnQeDoO9NTuiBdYaXKgwjGozPKF/46X71f0sCt/Mm9i8EDtt3igEezJIhAF3OvYcdv2NBF3L0mRMMQKp4Vy+aC0mKu4ggadyLe+KYvmch8/AiZPlrxC1AtqwNGyWpSe1JqxeEXQGXIA5JsfwGpnpAB5IUec2r3Bd09zUFk/DCC80l9d4BLnhYAUn7xzrKYzisSEQitmhnTR3HijEYE6AJzJjFR+z2PqqVKvtgKQ
[TRUNCATED] Mon Jul 03 16:23:21 UTC 2017 : Endpoint request body after
transformations: { "message" : "foo" } Mon Jul 03 16:23:21 UTC 2017
: Endpoint response body before transformations: {"errorMessage":"An
error occurred during JSON
parsing","errorType":"java.lang.RuntimeException","stackTrace":[],"cause":{"errorMessage":"com.fasterxml.jackson.databind.JsonMappingException:
Can not deserialize instance of java.lang.String out of START_OBJECT
token\n at [Source:
lambdainternal.util.NativeMemoryAsInputStream#e720b71; line: 1,
column:
1]","errorType":"java.io.UncheckedIOException","stackTrace":[],"cause":{"errorMessage":"Can
not deserialize instance of java.lang.String out of START_OBJECT
token\n at [Source:
lambdainternal.util.NativeMemoryAsInputStream#e720b71; line: 1,
column:
1]","errorType":"com.fasterxml.jackson.databind.JsonMappingException","stackTrace":["com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)","com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:857)","com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java
[TRUNCATED] Mon Jul 03 16:23:21 UTC 2017 : Endpoint response headers:
{x-amzn-Remapped-Content-Length=0,
x-amzn-RequestId=ede9aaed-600b-11e7-834e-47baf0a4e23f,
Connection=keep-alive, Content-Length=1252,
X-Amz-Function-Error=Unhandled, Date=Mon, 03 Jul 2017 16:23:20 GMT,
X-Amzn-Trace-Id=root=1-595a6f79-c065d6038ba3209743378112;sampled=0,
Content-Type=application/json} Mon Jul 03 16:23:21 UTC 2017 :
Execution failed due to configuration error: Output mapping refers to
an invalid method response: 200 Mon Jul 03 16:23:21 UTC 2017 : Method
completed with status: 500
There's a null in the path of my ARN, but I guess that's because I have no authentication set, which is what's intended at this point. I don't think this would be the cause of the error.
Other than that, I've tried defining the Content-Type for the body both as application/json and text/plain. None seems to work and even with text/plain, AWS seems to be expecting json. I'd expected a string to be valid json anyway.
What am I doing wrong? What's the full expression I should put in my Body Mapping template? And how should the schema definition in my Model look like? I don't seem to be able to define a proper model for plain text.
I'm sure this is something really simple and I'm just missing something...
The response you send back to the server depends on whether you're using the Lambda proxy integration or not. Using proxy integration is easier to set up on the API Gateway side, but your Lambda needs to do a little more work because the gateway is going to send you a bunch of stuff and demand a specific format in the response. For proxy integration the response format needs to look like this:
{
"isBase64Encoded": true|false,
"statusCode": httpStatusCode,
"headers": { "headerName": "headerValue", ... },
"body": "..."
}
There's more here: http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-set-up-simple-proxy.html#api-gateway-set-up-lambda-proxy-integration-on-proxy-resource
If you are not using proxy integration you will need to setup body mapping for your parameters in the API Gateway integration for the HTTP verb in question to match the api parameters to the lambda parameters. There's a good explanation in the accepted answer here: How to pass a querystring or route parameter to AWS Lambda from Amazon API Gateway
With Lambda Proxy, you have to return a stringified JSON.
With Lambda, you can return JSON from your Lambda and then have your API Gateway body mapping template stringify it for you.
For additional context and example, see this page from Serverless docs.
I have a very basic lambda function written in Scala deployed to AWS Lambda. The function works just fine when I test it via the AWS Lambda console.
Here's the function with some additional logging added for debug purposes.
package com.spacecorpshandbook.ostium.lambda.handler
import java.util
import com.google.gson.Gson
import temp.{ApiGatewayProxyResponse, Appointment, CancelResponse}
/**
* Amazon Lambda handler adapter for the Cancellation application
*/
class CancellationHandler {
def cancelAppointment(appointment: Appointment): ApiGatewayProxyResponse = {
System.out.println("++++ appointmentId is: " + appointment.getAppointmentId)
val apiGatewayProxyResponse = new ApiGatewayProxyResponse
val cancelResponse = new CancelResponse
cancelResponse.setMessage("Cancelled appointment with id " + appointment.getAppointmentId)
val gson: Gson = new Gson
apiGatewayProxyResponse.setBody(gson.toJson(cancelResponse))
apiGatewayProxyResponse.setStatusCode("200")
val headerValues = new util.HashMap[String, String]
headerValues put("Content-Type", "application/json")
apiGatewayProxyResponse.setHeaders(headerValues)
System.out.println("+++++ message before returning: " + apiGatewayProxyResponse.getBody)
apiGatewayProxyResponse
}
}
I was concerned that the POJO input/outputs being Scala beans might have been causing issues so I implemented Java versions temporarily just to rule that out.
The Integration Request on AWS API gateway is setup by default as Resources ANY with Lambda Proxy Integration enabled. Note that in this configuration when I test from the AWS API Gateway console the data is transformed and goes in but does't make it all the way to the lambda function
Execution log for request test-request
Fri Dec 09 11:14:40 UTC 2016 : Starting execution for request: test-invoke-request
Fri Dec 09 11:14:40 UTC 2016 : HTTP Method: PUT, Resource Path: /cancel-appointment
Fri Dec 09 11:14:40 UTC 2016 : Method request path: {}
Fri Dec 09 11:14:40 UTC 2016 : Method request query string: {}
Fri Dec 09 11:14:40 UTC 2016 : Method request headers: {Content-Type= application/json}
Fri Dec 09 11:14:40 UTC 2016 : Method request body before transformations: {
"applicationId": "asdfsfa"
}
Fri Dec 09 11:14:40 UTC 2016 : Endpoint request headers: {x-amzn-lambda-integration-tag=test-request, Authorization=****************************************************************************************************************************************************************************************************************************************************************************************************************************************5c044d, X-Amz-Date=20161209T111440Z, x-amzn-apigateway-api-id=l5tcmj0vlk, Accept=application/json, User-Agent=AmazonAPIGateway_l5tcmj0vlk, Host=lambda.us-east-1.amazonaws.com, X-Amz-Content-Sha256=857a062940a7fbb8134bad1c007e9975a10bd8323c39f6040e797a98e87ea1f6, X-Amzn-Trace-Id=Root=1-584a9220-9cd537954952cca7daee32bf, Content-Type=application/json}
Fri Dec 09 11:14:40 UTC 2016 : Endpoint request body after transformations: {"resource":"/cancel-appointment","path":"/cancel-appointment","httpMethod":"PUT","headers":{"Content-Type":" application/json"},"queryStringParameters":null,"pathParameters":null,"stageVariables":null,"requestContext":{"accountId":"456204981758","resourceId":"xznq3u","stage":"test-invoke-stage","requestId":"test-invoke-request","identity":{"cognitoIdentityPoolId":null,"accountId":"456204981758","cognitoIdentityId":null,"caller":"456204981758","apiKey":"test-invoke-api-key","sourceIp":"test-invoke-source-ip","accessKey":"ASIAJ5D7KU524H7CTTTQ","cognitoAuthenticationType":null,"cognitoAuthenticationProvider":null,"userArn":"arn:aws:iam::456204981758:root","userAgent":"Apache-HttpClient/4.5.x (Java/1.8.0_102)","user":"456204981758"},"resourcePath":"/cancel-appointment","httpMethod":"PUT","apiId":"l5tcmj0vlk"},"body":"{\n \"applicationId\": \"asdfsfa\"\n}","isBase64Encoded":false}
Fri Dec 09 11:14:40 UTC 2016 : Endpoint response body before transformations: {"statusCode":"200","headers":{"Content-Type":"application/json"},"body":"{\"message\":\"Cancelled appointment with id null\"}"}
Fri Dec 09 11:14:40 UTC 2016 : Endpoint response headers: {x-amzn-Remapped-Content-Length=0, x-amzn-RequestId=adcadf25-be00-11e6-8855-75e96d772946, Connection=keep-alive, Content-Length=128, Date=Fri, 09 Dec 2016 11:14:39 GMT, Content-Type=application/json}
Fri Dec 09 11:14:40 UTC 2016 : Method response body after transformations: {"message":"Cancelled appointment with id null"}
Fri Dec 09 11:14:40 UTC 2016 : Method response headers: {Content-Type=application/json, X-Amzn-Trace-Id=Root=1-584a9220-9cd537954952cca7daee32bf}
Fri Dec 09 11:14:40 UTC 2016 : Successfully completed execution
Fri Dec 09 11:14:40 UTC 2016 : Method completed with status: 200
If I add a specific method, like POST and do not set it as a Lambda Proxy Integration I do indeed see the that the provide request body data makes it to the lambda function, is correctly de-serialized into my POJO and is returned
Execution log for request test-request
Fri Dec 09 11:22:02 UTC 2016 : Starting execution for request: test-invoke-request
Fri Dec 09 11:22:02 UTC 2016 : HTTP Method: POST, Resource Path: /cancel-appointment
Fri Dec 09 11:22:02 UTC 2016 : Method request path: {}
Fri Dec 09 11:22:02 UTC 2016 : Method request query string: {}
Fri Dec 09 11:22:02 UTC 2016 : Method request headers: {}
Fri Dec 09 11:22:02 UTC 2016 : Method request body before transformations: {
"appointmentId" : "sfssdf"
}
Fri Dec 09 11:22:02 UTC 2016 : Endpoint request headers: {x-amzn-lambda-integration-tag=test-request, Authorization=****************************************************************************************************************************************************************************************************************************************************************************************************************************************a8dc41, X-Amz-Date=20161209T112202Z, x-amzn-apigateway-api-id=l5tcmj0vlk, Accept=application/json, User-Agent=AmazonAPIGateway_l5tcmj0vlk, Host=lambda.us-east-1.amazonaws.com, X-Amz-Content-Sha256=875dad4d4e05f8c12a7ca8aeaf69046d4153fc7f910e1eff1959cb011e8313a0, X-Amzn-Trace-Id=Root=1-584a93da-f841704d9feb371b31e41cb9, Content-Type=application/json}
Fri Dec 09 11:22:02 UTC 2016 : Endpoint request body after transformations: {
"appointmentId" : "sfssdf"
}
Fri Dec 09 11:22:02 UTC 2016 : Endpoint response body before transformations: {"statusCode":"200","headers":{"Content-Type":"application/json"},"body":"{\"message\":\"Cancelled appointment with id sfssdf\"}"}
Fri Dec 09 11:22:02 UTC 2016 : Endpoint response headers: {x-amzn-Remapped-Content-Length=0, x-amzn-RequestId=b4f5efce-be01-11e6-91c3-5b1e06f831e2, Connection=keep-alive, Content-Length=130, Date=Fri, 09 Dec 2016 11:22:02 GMT, Content-Type=application/json}
Fri Dec 09 11:22:02 UTC 2016 : Method response body after transformations: {"statusCode":"200","headers":{"Content-Type":"application/json"},"body":"{\"message\":\"Cancelled appointment with id sfssdf\"}"}
Fri Dec 09 11:22:02 UTC 2016 : Method response headers: {X-Amzn-Trace-Id=Root=1-584a93da-f841704d9feb371b31e41cb9, Content-Type=application/json}
Fri Dec 09 11:22:02 UTC 2016 : Successfully completed execution
Fri Dec 09 11:22:02 UTC 2016 : Method completed with status: 200
So everything looks great now, however when I actually do a real test against the AWS API URL from PostMan using HTTP method POST I get the response with null as the appointment Id and I can see in the CloudWatch logs that the appointmentId did not get set on the input Appointment object.
I feel like I'm missing something basic here. Any help would be greatly appreciated.
source code can be found here
Update
Resolved this problem by switching the lambda handler function to use Stream rather than attempting to serialize/deserialize JSON into POJOs. When using the API Gateway Lambda Proxy the input to handler is a complicated JSON structure that I didn't want to try and replicate as a Java/Scala class. It was easier to process the input as a stream, parse it into a JsonObject, and then convert the body of the message into my POJO using Gson or equivalent library. Sample handler below, you can also see a larger example here
class CancellationHandler {
def cancelAppointment(request: InputStream, response: OutputStream, context: Context): Unit = {
val logger = context.getLogger
val parser: JsonParser = new JsonParser
var inputObj: JsonObject = null
val gson: Gson = new Gson
try {
inputObj = parser.parse(IOUtils.toString(request, "UTF-8")).getAsJsonObject
} catch {
case e: IOException =>
logger.log("Error while reading request\n" + e.getMessage)
throw new RuntimeException(e.getMessage)
}
val body: String = inputObj.get("body").getAsString
val appointment: Appointment = gson.fromJson(body, classOf[Appointment])
val apiGatewayProxyResponse = new ApiGatewayProxyResponse
val cancelResponse = new CancelResponse
cancelResponse.setMessage("Cancelled appointment with id " + appointment.getAppointmentId)
apiGatewayProxyResponse.setBody(gson.toJson(cancelResponse))
apiGatewayProxyResponse.setStatusCode("200")
val headerValues = new util.HashMap[String, String]
headerValues put("Content-Type", "application/json")
apiGatewayProxyResponse.setHeaders(headerValues)
val output: String = gson.toJson(apiGatewayProxyResponse)
IOUtils.write(output, response, "UTF-8")
}
}
The input shape for the Lambda proxy will be different than the shape for the regular non-proxy Lambda integration. This is important for your use case of course because you're using Java/Scala where you have to explicitly structure the input POJO.
Here's what the proxy input will look like:
{
"resource": "\/pets",
"path": "\/pets",
"httpMethod": "POST",
"headers": null,
"queryStringParameters": null,
"pathParameters": null,
"stageVariables": null,
"requestContext": {
...
"stage": "test-invoke-stage",
"requestId": "test-invoke-request",
"identity": {
...
},
"resourcePath": "\/pets",
"httpMethod": "POST"
},
"body": "{\n \"foo\":\"bar\"\n}", <---- here's what you're looking for
"isBase64Encoded": false
}
Docs: http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html
I know there are a lot of questions about mapping the request data, but neither one helped me.
So, what i am trying to achieve is an API endpoint mapped to a lambda. The request to that endpoint is forwarded when a bucket triggers a 404, and the parameters are passed to the lambda via the request path, like: /{image_name}/{width}/{height}.
My lambda's code simply calls context.succeed(event, context);
In the Method request configuration the request path's parameters were automatically created, .
In the integration request I have created three mapping templates: plain/text, plain/html, application/json with the same definition as bellow:
#set($inputRoot = $input.path('$'))
{
"name": $input.params('name'),
"width" : $input.params('width'),
"height" : $input.params('height'),
"params": $input.params(),
"resourcePath": $context.resourcePath,
}
When calling form an chrome rest client i get:
When calling the test from the console, i get the following response:
{"Type":"User","message":"Could not parse request body into json."}
The same response i get when I call curl or when I simply open the URL in the browser.
But in the logs from the console's test call I see:
Execution log for request test-request
Tue Sep 08 09:10:20 UTC 2015 : Starting execution for request: test-invoke-request
Tue Sep 08 09:10:20 UTC 2015 : API Key: test-invoke-api-key
Tue Sep 08 09:10:20 UTC 2015 : Method request path: {name=name, width=100, height=100}
Tue Sep 08 09:10:20 UTC 2015 : Method request query string: {}
Tue Sep 08 09:10:20 UTC 2015 : Method request headers: {}
Tue Sep 08 09:10:20 UTC 2015 : Method request body before transformations: null
Tue Sep 08 09:10:20 UTC 2015 : Endpoint request URI: <endpoint>:function:Magic/invocations
Tue Sep 08 09:10:20 UTC 2015 : Endpoint request headers: {
Authorization=<authorization>
Credential=<credential>,
SignedHeaders=accept;content-type;host;user-agent;x-amz-content-sha256;x-amz-date;x-amz-source-arn,
Signature=<signature>,
X-Amz-Date=20150908T091020Z,
X-Amz-Source-Arn=<ARN>/null/GET/image/{name}/{width}/{height},
Accept=application/json,
User-Agent=AmazonAPIGateway_ebkkwbbpo0,
Host=lambda.us-east-1.amazonaws.com,
X-Amz-Content-Sha256=<key>,
Content-Type=application/json
}
Tue Sep 08 09:10:20 UTC 2015 : Endpoint request body after transformations: {
"name": name,
"width" : 100,
"height" : 100,
"params": {path={name=name, width=100, height=100}, querystring={}, header={}},
"resourcePath": /image/{name}/{width}/{height},
}
Tue Sep 08 09:10:20 UTC 2015 : Endpoint response body before transformations: {"Type":"User","message":"Could not parse request body into json."}
Tue Sep 08 09:10:20 UTC 2015 : Endpoint response headers: {
x-amzn-ErrorType=InvalidRequestContentException:http://internal.amazon.com/coral/com.amazonaws.awsgirapi/,
x-amzn-RequestId=<RequestId>,
Connection=keep-alive,
Content-Length=68,
Date=Tue, 08 Sep 2015 09:10:20 GMT,
Content-Type=application/json}
Tue Sep 08 09:10:20 UTC 2015 : Method response body after transformations: {"Type":"User","message":"Could not parse request body into json."}
Tue Sep 08 09:10:20 UTC 2015 : Method response headers: {Content-Type=application/json}
Tue Sep 08 09:10:20 UTC 2015 : Successfully completed execution
As I see at some point, the URL path is parsed correctly, but I do not know what goes wrong.
Also, I don't know why there is in the X-Amz-Source-Arn a null value in the path.
Thank you.
The problem is the integration request mapping template. You should double quote the fields that are string type, so they can later be converted to JSON.
So in this example you should write:
#set($inputRoot = $input.path('$'))
{
"name": "$input.params('name')",
"width" : $input.params('width'),
"height" : $input.params('height'),
"params": "$input.params()",
"resourcePath": "$context.resourcePath",
}
It seemed odd to me, but this is the solution.
Also you don't need to write three mapping templates for this case, you should leave only the application/json
In case of lambda integration with path parameters, the path parameters should be mapped in the Integration request as follows.
Go to Integration Response -> Mapping Templates and add the following mapping of the path parameter to input values:
{ "itemId": "$input.params('catalogitemid')"}