AWS Serverless Requests - JSON body / httpMethod is always null

Viewed 455

When I send POST request I am getting NullPointerException from input.getBody() and input.getHttpMethod(). I have no defined mapping template - It is set to When no template matches the request Content-Type header by default.

My POST request

{
    "shiftStart": "22/02/2021 18:00:00",
    "shiftEnd": "22/02/2021 19:00:00",
    "employee": {
        "id": "6",
        "firstName": "Joe",
        "lastName": "Brown"
    }
}

Once I got this exception so it looks like input.getHttpMethod() passed once:

{
    "errorMessage": "BODY",
    "errorType": "java.lang.NullPointerException",
    "stackTrace": [
        "helloworld.App.handleRequest(App.java:48)",
        "java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)",
        "java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)",
        "java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)",
        "java.base/java.lang.reflect.Method.invoke(Unknown Source)"
    ]
}

but I did not changed anything and now I am getting following exception:

{
    "errorMessage": "java.lang.NullPointerException",
    "errorType": "java.lang.NullPointerException",
    "stackTrace": [
        "helloworld.App.handleRequest(App.java:41)",
        "java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)",
        "java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)",
        "java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)",
        "java.base/java.lang.reflect.Method.invoke(Unknown Source)"
    ]
}

My classes:

public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {

    @Override
    public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) {

        Map<String, String> headers = new HashMap<>();
        headers.put("Content-Type", "application/json");
        headers.put("X-Custom-Header", "application/json");

        APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent()
                .withHeaders(headers);

        AmazonDynamoDB client = AmazonDynamoDBClientBuilder.defaultClient();
        DynamoDBMapper mapper = new DynamoDBMapper(client);

        Shift shift;
        String output;

        if (input.getHttpMethod().equalsIgnoreCase(HttpMethod.GET.name())) {
            shift = mapper.load(Shift.class, context.getAwsRequestId());
            if (shift == null) {
                throw new ResourceNotFoundException("Resource not found");
            }
            output = shift.toString();
        } else if (input.getHttpMethod().equalsIgnoreCase(HttpMethod.POST.name())) {
            if (input.getBody() == null) throw new NullPointerException("BODY");
            shift = new Shift(input.getBody());
            WorkSchedule workSchedule = new WorkSchedule(client);
            if (!workSchedule.isShiftPossible(shift)) {
                throw new ConditionalCheckFailedException("Conditional check failed");
            }
            mapper.save(shift);
            output = "SHIFT SAVED";
        } else {
            output = "Unsupported operation";
        }

        return response
                .withStatusCode(200)
                .withBody(output);
    }
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@DynamoDBTable(tableName = "WORK_SCHEDULE")
public class Shift {

    public Shift(String json) {
        Gson gson = new Gson();
        Shift request = gson.fromJson(json, Shift.class);
        this.id = request.getId();
        this.shiftStart = request.getShiftStart();
        this.shiftEnd = request.getShiftEnd();
        this.employee = request.getEmployee();
    }

    @DynamoDBHashKey
    @DynamoDBAutoGeneratedKey
    private String id;

    @DynamoDBAttribute
    private String shiftStart;

    @DynamoDBAttribute
    private String shiftEnd;

    @DynamoDBAttribute
    private Employee employee;

    @Override
    public String toString() {
        return "Shift{" +
                "id='" + id + '\'' +
                ", shiftStart='" + shiftStart + '\'' +
                ", shiftEnd='" + shiftEnd + '\'' +
                ", employee=" + employee +
                '}';
    }
}
@Getter
@Setter
@AllArgsConstructor
@DynamoDBDocument
public class Employee {

    private String id;
    private String firstName;
    private String lastName;

    @Override
    public String toString() {
        return "Employee{" +
                "id='" + id + '\'' +
                ", firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                '}';
    }
}
1 Answers

Api Gateway has two basic types of Lambda integration LAMBDA AND LAMBDA_PROXY.

Regular LAMBDA will just pass through request body as is.

If we enable Use Lambda Proxy integration in integration request, it will be LAMBDA_PROXY and this will pass through the request by wrapping additional details like queryParms, Api Keys, Http Headers, method type, etc. which can be serialized into APIGatewayProxyRequestEvent class.

queryStringParameters with queryParms.

body with Json body as string.

pathParameters with Path Parameters.

So, in this case, we just need to enable Use Lambda Proxy integration

Related