使用AWS Lambda+API Gateway测试API端点遇内部错误排查
我帮你梳理下问题,从代码和配置两个层面都能找到导致这个错误的原因,咱们一步步来:
1. 代码里的致命笔误(ClientAuthPOJO类)
先看你的ClientAuthPOJO类,setClientSecret方法明显写错了:
public void setClientSecret(String clientSecret) { this.clientId = clientId; }
这个方法本来是要给clientSecret属性赋值,但你错误地把不存在的变量clientId塞给了this.clientId(而且方法参数明明是clientSecret),正确写法应该是:
public void setClientSecret(String clientSecret) { this.clientSecret = clientSecret; }
这个笔误会直接导致JSON反序列化时,没法正确设置clientSecret,甚至可能搞乱clientId的赋值,这是Lambda接收到的参数全为null的核心原因之一。
另外还有个小坑:你原来用clientCredsMap.forEach里抛异常的写法会出问题,Lambda的forEach循环里不能直接抛出RuntimeException,建议改成普通for循环判断:
// 替换原来的forEach逻辑 for (Map.Entry<String, String> entry : clientCredsMap.entrySet()) { if (clientId.equals(entry.getKey())) { throw new RuntimeException("Client Already exists"); } }
2. API Gateway与Lambda的请求体映射问题
Lambda测试时你用的是直接匹配ClientAuthPOJO的测试事件,但API Gateway默认会把请求包在一个包含body、headers等字段的JSON结构里,比如你发的请求体是:
{ "clientId": "test-id", "clientSecret": "test-secret" }
但Lambda实际收到的会是这种结构:
{ "body": "{\"clientId\":\"test-id\",\"clientSecret\":\"test-secret\"}", "headers": {...}, "requestContext": {...} }
这就导致ClientAuthPOJO没法正确反序列化,因为输入结构完全不匹配。解决这个有两种办法:
办法一:修改Lambda Handler的输入类型
把RequestHandler<ClientAuthPOJO, Object>改成接收API Gateway的请求对象APIGatewayProxyRequestEvent(需要引入AWS SDK的相关依赖),然后手动解析body字段:
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.fasterxml.jackson.databind.ObjectMapper; public class AuthClientCredentialServiceHandler implements RequestHandler<APIGatewayProxyRequestEvent, AuthClientCredentialResponse> { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); @Override public AuthClientCredentialResponse handleRequest(APIGatewayProxyRequestEvent event, Context context) { try { // 从event的body里解析出ClientAuthPOJO对象 ClientAuthPOJO clientIdSecret = OBJECT_MAPPER.readValue(event.getBody(), ClientAuthPOJO.class); // 下面是你原来的业务逻辑(已修复forEach问题) context.getLogger().log("Input: " + clientIdSecret); String clientId = clientIdSecret.getClientId(); String clientSecret = generateClientSecretKey(); Map<String, String> clientCredsMap = getClientCredentials(); if (clientCredsMap.size() > MAX_CLIENT_KEY) { throw new RuntimeException(String.format("Max limit is %d, Please delete some keys", MAX_CLIENT_KEY)); } for (Map.Entry<String, String> entry : clientCredsMap.entrySet()) { if (clientId.equals(entry.getKey())) { throw new RuntimeException("Client Already exists"); } } storeClientCredentials(clientId, clientSecret); return AuthClientCredentialResponse.builder() .success(true) .clientId(clientId) .clientSecret(clientSecret) .build(); } catch (Exception e) { throw new RuntimeException("Failed to process request: " + e.getMessage()); } } // 其他方法保持不变... }
办法二:配置API Gateway的请求映射模板
如果不想改代码,可以在API Gateway里配置映射模板,把请求体直接传给Lambda:
- 打开API Gateway控制台,找到你的API和对应的资源方法
- 点击集成请求,找到映射模板区域
- 点击添加映射模板,选择
application/json - 在模板内容里输入:
$input.json('$') - 保存配置后,重新部署API到对应的阶段(比如test或prod)
3. 最后要检查的几个点
- 每次修改API Gateway配置后,一定要重新部署,不然配置不会生效
- 去CloudWatch里看Lambda的完整日志,确认实际收到的输入结构是不是符合预期
- 确保Lambda的执行角色权限足够(你现在是模拟存储,后续用DB或S3的时候要补全权限)
按照上面的步骤修复后,应该就能解决API Gateway调用时的内部服务器错误,以及参数为null的问题了。
内容的提问来源于stack exchange,提问作者Vir Gandhi

