Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

APIGatewayV2HTTPEvent does not properly deserialize event #477

Open
kgregory-chariot opened this issue Apr 19, 2024 · 1 comment
Open

APIGatewayV2HTTPEvent does not properly deserialize event #477

kgregory-chariot opened this issue Apr 19, 2024 · 1 comment

Comments

@kgregory-chariot
Copy link

kgregory-chariot commented Apr 19, 2024

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}
@kgregory-chariot
Copy link
Author

Taking a look at the raw JSON data (via reading as InputStream), I wonder if this class is intended to handle the request from my integration: it's missing several top-level attributes, and has different names for others. However, I see that APIGatewayV2ProxyRequestEvent, which I would have thought a better choice, is deprecated and was intended to support WebSocket requests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant