Lambda代理集成下API Gateway调用Java Lambda遇Input value must not null报错
看起来你遇到的是Lambda代理集成里很常见的输入解析和响应格式问题,结合你给的代码和错误信息,我帮你拆解下原因和具体解决方案:
问题1:Input value must not be null(IllegalArgumentException)
用API Gateway的Lambda代理集成时,API Gateway不会直接把请求体传给Lambda,而是会把整个请求封装成一个标准的代理请求对象,结构大概是这样:
{ "resource": "/your-path", "httpMethod": "POST", "body": "{\"id\":\"123\",\"firstName\":\"John\",...}", "headers": {...}, // 其他请求元数据字段 }
而你的Handler直接用PersonRequest作为输入参数,Lambda没办法把这个代理请求对象反序列化成PersonRequest,导致personRequest(或者你代码里的userRequest)为null,后续调用personRequest.getId()就抛出了空值相关的异常。
另外,你的代码里还有明显的变量名笔误:Handler的参数是UserRequest userRequest,但代码里却用了personRequest(比如saveData(personRequest)、personRequest.getId()),这也会直接导致变量未定义或者空值问题。
问题2:Malformed Lambda proxy response
Lambda代理集成要求返回的响应必须严格符合以下格式,缺一不可:
{ "isBase64Encoded": false, "statusCode": 200, "headers": {...}, "body": "string" }
你的代码在catch块里只打印了错误,没有对responseJson做任何错误填充,导致返回的JSON可能缺少必要字段,触发格式错误。同时,如果正常流程中saveData或者getItem抛出异常,也会跳过响应字段的填充,返回不完整的JSON。
修正后的解决方案代码
下面是调整后的完整代码,解决了上述所有问题:
import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.LambdaLogger; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder; import com.amazonaws.services.dynamodbv2.document.DynamoDB; import com.amazonaws.services.dynamodbv2.document.Item; import org.json.JSONObject; import java.util.Map; public class SavePersonHandler implements RequestHandler<Map<String, Object>, JSONObject> { private DynamoDB dynamoDb; private final String DYNAMODB_TABLE_NAME = "XXXX"; private final Regions REGION = Regions.US_WEST_2; private void initDynamoDbClient() { AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard() .withRegion(REGION) .build(); this.dynamoDb = new DynamoDB(client); } @Override public JSONObject handleRequest(Map<String, Object> proxyRequest, Context context) { LambdaLogger logger = context.getLogger(); JSONObject responseJson = new JSONObject(); try { this.initDynamoDbClient(); // 1. 解析API Gateway代理请求中的body,转换成PersonRequest String requestBody = (String) proxyRequest.get("body"); if (requestBody == null || requestBody.isEmpty()) { throw new IllegalArgumentException("Request body cannot be null or empty"); } JSONObject bodyJson = new JSONObject(requestBody); PersonRequest personRequest = new PersonRequest(); personRequest.setId(bodyJson.getString("id")); personRequest.setFirstName(bodyJson.getString("firstName")); personRequest.setLastName(bodyJson.getString("lastName")); personRequest.setAge(bodyJson.getInt("age")); personRequest.setAddress(bodyJson.getString("address")); // 2. 保存数据到DynamoDB saveData(personRequest); // 3. 查询并构建成功响应 Item item = this.dynamoDb.getTable(DYNAMODB_TABLE_NAME) .getItem("id", personRequest.getId(), "id, firstName, lastName, age, address", null); JSONObject responseBody = new JSONObject(); responseBody.put("message", "Saved Successfully-" + item.toJSON()); JSONObject headerJson = new JSONObject(); headerJson.put("x-custom-header", "custom header value"); responseJson.put("isBase64Encoded", false); responseJson.put("statusCode", 200); responseJson.put("headers", headerJson); responseJson.put("body", responseBody.toString()); } catch (Exception e) { logger.log("Error occurred: " + e.getMessage()); // 异常情况下也要返回符合格式的错误响应 JSONObject errorBody = new JSONObject(); errorBody.put("errorMessage", e.getMessage()); errorBody.put("errorType", e.getClass().getName()); responseJson.put("isBase64Encoded", false); responseJson.put("statusCode", 500); responseJson.put("headers", new JSONObject().put("Content-Type", "application/json")); responseJson.put("body", errorBody.toString()); } return responseJson; } // 实现你的saveData方法 private void saveData(PersonRequest personRequest) { Item item = new Item() .withPrimaryKey("id", personRequest.getId()) .withString("firstName", personRequest.getFirstName()) .withString("lastName", personRequest.getLastName()) .withInt("age", personRequest.getAge()) .withString("address", personRequest.getAddress()); this.dynamoDb.getTable(DYNAMODB_TABLE_NAME).putItem(item); } // 你的PersonRequest实体类 public static class PersonRequest { private String id; private String firstName; private String lastName; private int age; private String address; // getter和setter方法 public String getId() { return id; } public void setId(String id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } } }
关键修改点说明
- 输入参数类型调整:把
RequestHandler<PersonRequest, JSONObject>改成RequestHandler<Map<String, Object>, JSONObject>,这样能接收API Gateway传过来的完整代理请求对象。 - 请求体手动解析:从代理请求的
body字段中提取JSON字符串,手动转换成PersonRequest对象,避免自动反序列化失败的问题。 - 变量名统一修正:把代码里的变量名统一成
personRequest,解决原代码中变量名不一致的笔误。 - 异常处理完善:在
catch块中构建符合格式的错误响应,确保无论成功还是失败,返回的JSON都包含代理集成要求的所有字段。 - 响应格式严格对齐:确保返回的
responseJson始终包含isBase64Encoded、statusCode、headers和body四个必填字段。
测试的时候,记得在API Gateway的测试界面中,把请求体写成标准JSON格式(比如{"id":"1","firstName":"Alice","lastName":"Smith","age":30,"address":"123 Main St"}),这样Lambda才能正确解析。
内容的提问来源于stack exchange,提问作者user3567195

