Skip to content

APIGatewayV2HTTPEvent does not properly deserialize event #477

Open
@kgregory-chariot

Description

@kgregory-chariot

When processing an invocation from an HTTP API Gateway, the APIGatewayV2HTTPEvent does not appear to deserialize nested objects. These objects are successfully deserialized when using Map<String,Object>.

Note: this is different from #432, although may have the same root cause.

Buildable project attached

aws-lambda-java-core version: 1.2.3
aws-lambda-java-events version: 3.11.5
Java target version: 11
Java runtime version: 21

Runtime ARN: arn:aws:lambda:us-east-1::runtime:02ff9a81932ab0e699171762afcb5aa2f8c2524ac6e34498612b55defb9c2e7f

HTTP gateway configuration (extract from CloudFormation template):

  APIGateway:
    Type:                               "AWS::ApiGatewayV2::Api"
    Properties: 
      Name:                             !Sub "${AWS::StackName}"
      Description:                      "Invokes the bucket-listing Lambda"
      ProtocolType:                     "HTTP"


  APIGatewayGetRoute:
    Type:                               "AWS::ApiGatewayV2::Route"
    Properties: 
      ApiId:                            !Ref APIGateway
      RouteKey:                         "GET /{proxy+}"
      Target:                           !Sub "integrations/${APIGatewayLambdaIntegration}"


  APIGatewayPutRoute:
    Type:                               "AWS::ApiGatewayV2::Route"
    Properties: 
      ApiId:                            !Ref APIGateway
      RouteKey:                         "PUT /{proxy+}"
      Target:                           !Sub "integrations/${APIGatewayLambdaIntegration}"


  APIGatewayPostRoute:
    Type:                               "AWS::ApiGatewayV2::Route"
    Properties: 
      ApiId:                            !Ref APIGateway
      RouteKey:                         "POST /"
      Target:                           !Sub "integrations/${APIGatewayLambdaIntegration}"


  APIGatewayLambdaIntegration:
    Type:                               "AWS::ApiGatewayV2::Integration"
    Properties: 
      ApiId:                            !Ref APIGateway
      Description:                      "Handles all requests for API operations"
      IntegrationMethod:                "POST"
      IntegrationType:                  "AWS_PROXY"
      IntegrationUri:                   !Sub "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunction.Arn}/invocations"
      PayloadFormatVersion:             "1.0"

Invocation command:

curl -XPUT -d '{"foo": 123, "bar":456, "baz": [9, 8]}' 'https://redacted.execute-api.us-east-1.amazonaws.com/something'

Version 1: uses APIGatewayV2HTTPEvent

public class HttpGWLambda1
implements RequestHandler<APIGatewayV2HTTPEvent,APIGatewayV2HTTPResponse>
{
    @Override
    public APIGatewayV2HTTPResponse handleRequest(APIGatewayV2HTTPEvent event, Context context)
    {
        System.out.println(event);
        System.out.println(event.getRequestContext().getHttp());
        
        return APIGatewayV2HTTPResponse.builder()
               .withStatusCode(200)
               .withBody("Hello, world")
               .build();
    }
}

Output from first println(), with account number and GW endpoint redacted, but no other formatting. Note the fields that show null values:

APIGatewayV2HTTPEvent(version=1.0, routeKey=null, rawPath=null, rawQueryString=null, cookies=null, headers={Content-Length=38, Content-Type=application/x-www-form-urlencoded, Host=redacted.execute-api.us-east-1.amazonaws.com, User-Agent=curl/7.68.0, X-Amzn-Trace-Id=Root=1-662273ce-2b40f35d65612f3032a6ea11, X-Forwarded-For=173.49.152.157, X-Forwarded-Port=443, X-Forwarded-Proto=https, accept=*/*}, queryStringParameters=null, pathParameters={proxy=something}, stageVariables=null, body=eyJmb28iOiAxMjMsICJiYXIiOjQ1NiwgImJheiI6IFs5LCA4XX0=, isBase64Encoded=true, requestContext=APIGatewayV2HTTPEvent.RequestContext(routeKey=null, accountId=123456789012, stage=$default, apiId=redacted, domainName=redacted.execute-api.us-east-1.amazonaws.com, domainPrefix=redacted, time=null, timeEpoch=0, http=null, authorizer=null, requestId=WecIThGXIAMEbKQ=))

Output from second println() (attempting to retrieve the HTTP invocation information) is null.

Version 2: uses Map<String,Object>

public class HttpGWLambda2
implements RequestHandler<Map<String,Object>,APIGatewayV2HTTPResponse>
{
    @Override
    public APIGatewayV2HTTPResponse handleRequest(Map<String,Object> event, Context context)
    {
        System.out.println(event);
        
        return APIGatewayV2HTTPResponse.builder()
               .withStatusCode(200)
               .withBody("Hello, world")
               .build();
    }
}

Output from this version (again, with identifying information redacted, but otherwise unchanged). Note that child objects are populated:

{version=1.0, resource=/{proxy+}, path=/something, httpMethod=PUT, headers={Content-Length=38, Content-Type=application/x-www-form-urlencoded, Host=redacted.execute-api.us-east-1.amazonaws.com, User-Agent=curl/7.68.0, X-Amzn-Trace-Id=Root=1-66227575-38824f105e19d3c407288d96, X-Forwarded-For=redacted, X-Forwarded-Port=443, X-Forwarded-Proto=https, accept=*/*}, multiValueHeaders={Content-Length=[38], Content-Type=[application/x-www-form-urlencoded], Host=[redacted.execute-api.us-east-1.amazonaws.com], User-Agent=[curl/7.68.0], X-Amzn-Trace-Id=[Root=1-66227575-38824f105e19d3c407288d96], X-Forwarded-For=[redacted], X-Forwarded-Port=[443], X-Forwarded-Proto=[https], accept=[*/*]}, queryStringParameters=null, multiValueQueryStringParameters=null, requestContext={accountId=123456789012, apiId=redacted, domainName=redacted.execute-api.us-east-1.amazonaws.com, domainPrefix=redacted, extendedRequestId=WedKcjkCIAMEb1Q=, httpMethod=PUT, identity={accessKey=null, accountId=null, caller=null, cognitoAmr=null, cognitoAuthenticationProvider=null, cognitoAuthenticationType=null, cognitoIdentityId=null, cognitoIdentityPoolId=null, principalOrgId=null, sourceIp=redacted, user=null, userAgent=curl/7.68.0, userArn=null}, path=/something, protocol=HTTP/1.1, requestId=WedKcjkCIAMEb1Q=, requestTime=19/Apr/2024:13:45:25 +0000, requestTimeEpoch=1713534325773, resourceId=PUT /{proxy+}, resourcePath=/{proxy+}, stage=$default}, pathParameters={proxy=something}, stageVariables=null, body=eyJmb28iOiAxMjMsICJiYXIiOjQ1NiwgImJheiI6IFs5LCA4XX0=, isBase64Encoded=true}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions