Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Posting from AWS-API Gateway to Lambda

I have a simple C# Aws Lambda function which succeeds to a test from the Lambda console test but fails with a 502 (Bad Gateway) if called from the API Gateway (which i generated from the Lambda trigger option) and also if I use postman.(this initial function has open access (no security))

// request header
    Content-Type: application/json

//  request body
    {
        "userid":22,
        "files":["File1","File2","File3","File4"]
    }

The error I get in the logs is:

Wed Feb 08 14:14:54 UTC 2017 : Endpoint response body before transformations: {
  "errorType": "NullReferenceException",
  "errorMessage": "Object reference not set to an instance of an object.",
  "stackTrace": [
    "at blahblahmynamespace.Function.FunctionHandler(ZipRequest input, ILambdaContext context)",
    "at lambda_method(Closure , Stream , Stream , ContextInfo )"
  ]
}

It seems like the posted object is not being passed to the lambda input argument.

Code below

// Lambda function
     public LambdaResponse FunctionHandler(ZipRequest input, ILambdaContext context)
        {
            try
            {
                var logger = context.Logger;
                var headers = new Dictionary<string, string>();

                if (input == null || input.files.Count == 0)
                {
                    logger.LogLine($"input was null");
                    headers.Add("testheader", "ohdear");
                    return new LambdaResponse { body = "fail", headers = headers, statusCode = HttpStatusCode.BadRequest };
                }
                else
                {
                    logger.LogLine($"recieved request from user{input?.userid}");
                    logger.LogLine($"recieved {input?.files?.Count} items to zip");
                    headers.Add("testheader", "yeah");
                    return new LambdaResponse { body = "hurrah", headers = headers, statusCode = HttpStatusCode.OK };
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

//Lambda response/ZipRequest class

public class LambdaResponse
{

    public HttpStatusCode statusCode { get; set; }
    public Dictionary<string, string> headers { get; set; }
    public string body { get; set; }
}
public class ZipRequest
{
    public int userid { get; set; }
    public IList<string> files { get; set; }
}
like image 387
Tim Avatar asked Feb 08 '17 14:02

Tim


2 Answers

This might not have been available when the OP asked the question, but when invoking a Lambda function using the API Gateway, specific response objects are provided.

As previously noted in the documentation Api Gateway Simple Proxy for Lambda Input Format, the API Gateway wraps the input arguments in a fairly verbose wrapper. It also expects a similarly verbose response object.

However, it is not necessary to create custom request and response objects. The AWS team provides the Amazon.Lambda.APIGatewayEvents library, which is also available on NuGet. This library includes APIGatewayProxyRequest and APIGatewayProxyResponse objects ready-made.

It is still necessary to manually deserialize the Body of the request, as it is a string, not a JSON object. I assume this was done for flexibility?

An example function could look like this. It's a modification of the default function provided by the AWS tools:

    public APIGatewayProxyResponse FunctionHandler(APIGatewayProxyRequest request, ILambdaContext context)
    {
        var bodyString = request?.Body;

        if (!string.IsNullOrEmpty(bodyString))
        {
            dynamic body = JsonConvert.DeserializeObject(bodyString);

            if (body.input != null)
            {
                body.input = body.input?.ToString().ToUpper();

                return new APIGatewayProxyResponse
                {
                    StatusCode = 200,
                    Body = JsonConvert.SerializeObject(body)
                };
            }
        }

        return new APIGatewayProxyResponse
        {
            StatusCode = 200
        };
    }
like image 140
SouthShoreAK Avatar answered Sep 29 '22 01:09

SouthShoreAK


When using Lambda Proxy Integration in API Gateway, the first parameter to your FunctionHandler is not the body of your POST, but is another API Gateway-created object, which let's call LambdaRequest. Try these changes to your sample code. Add:

public class LambdaRequest
{
   public string body { get; set; }
}

Change your handler prototype to:

public LambdaResponse FunctionHandler(LambdaRequest req, ILambdaContext context)

And inside FunctionHandler add:

ZipRequest input = JsonConvert.DeserializeObject<ZipRequest>(req.Body);

The full LambdaRequest object is documented under Input Format of a Lambda Function for Proxy Integration in the AWS docs, and contains HTTP headers, the HTTP method, the query string, the body, and a few other things.

like image 23
phebert Avatar answered Sep 29 '22 00:09

phebert