AWS Lambda Authorizer返回BadRequestException问题排查求助
你遇到的BadRequestException: Unknown error at buildRestApiServiceError通常是Lambda授权器返回了不符合AppSync要求的响应,或者代码内部抛出未处理的错误导致的。以下是具体排查点和修正方案:
一、Lambda代码中的明显错误
1. 未定义变量导致运行时错误
代码中直接使用了purchasedPhones和userId两个未声明的变量,会触发ReferenceError,导致Lambda返回异常响应:
// 错误:purchasedPhones未定义 for (let index = 0; index < purchasedPhones.length; index++) { // ... } // 错误:userId未定义 return { isAuthorized: isPurchased, resolverContext: { userid: userId, // ... }, // ... };
修正方案:
- 从DynamoDB返回结果中提取已购手机列表(假设RecordTable的Item包含
purchasedPhones字段):// 假设字段为字符串集合类型(SS) const purchasedPhones = response.Item?.purchasedPhones?.SS || []; // 如果是列表类型(L),则用: // const purchasedPhones = response.Item?.purchasedPhones?.L?.map(item => item.S) || []; - 从Cognito用户信息中获取userId(推荐用用户sub而非username):
const userId = user.UserAttributes.find(attr => attr.Name === 'sub').Value;
2. DynamoDB查询结果未做空值判断
如果用户在RecordTable中无记录,response.Item会是undefined,直接访问字段会报错,需先判断:
let purchasedPhones = []; if (response.Item) { purchasedPhones = response.Item.purchasedPhones.SS || []; }
3. 错误获取GraphQL查询参数
AppSync传递给Lambda的event中不存在requestContext.queryString,GraphQL变量在event.arguments里,目标手机ID应从这里获取:
// 错误写法 const { requestContext: { queryString } } = event; // 正确写法 const targetPhoneId = event.arguments.id;
同时修改授权判断逻辑:
const isPurchased = purchasedPhones.includes(targetPhoneId);
4. Catch块直接返回Error对象
Lambda自定义授权器必须返回符合AppSync要求的JSON结构,直接返回Error对象会导致解析失败,修改catch块:
catch (error) { console.error('Authorization error:', error); return { isAuthorized: false, ttlOverride: 300 }; }
5. Cognito客户端未指定区域
若Lambda与Cognito用户池区域不同,未指定区域会导致请求失败,需显式配置:
const cognitoClient = new CognitoIdentityProviderClient({ region: process.env.REGION }); const dynamoClient = new DynamoDBClient({ region: process.env.REGION });
二、其他排查点
1. 确认授权令牌类型
GetUserCommand要求传入Cognito AccessToken,如果前端传递的是IdToken,会触发Cognito错误。若使用IdToken,可直接解析JWT获取sub:
const jwt = require('jsonwebtoken'); const decoded = jwt.decode(authorizationToken); const userId = decoded.sub;
2. 检查AppSync自定义授权器配置
- 确保在AppSync控制台将该Lambda配置为自定义授权器,并关联到read操作。
- 验证令牌来源是否设置为
Authorization头。
3. 查看Lambda CloudWatch日志
AppSync返回的错误只是表层信息,真正原因在Lambda执行日志中。去CloudWatch控制台找到对应日志组,查看具体错误(如变量未定义、Cognito调用失败等),这是最关键的排查步骤。
修正后的Lambda代码示例
exports.handler = async (event) => { console.log(`EVENT: ${JSON.stringify(event)}`); const { authorizationToken, arguments: { id: targetPhoneId } } = event; try { // 初始化客户端并指定区域 const cognitoClient = new CognitoIdentityProviderClient({ region: process.env.REGION }); const cognitoInput = { AccessToken: authorizationToken }; const cognitoCommand = new GetUserCommand(cognitoInput); const user = await cognitoClient.send(cognitoCommand); // 获取用户sub作为userId const userId = user.UserAttributes.find(attr => attr.Name === 'sub').Value; // 查询RecordTable(注意主键用sub而非username,需与表结构匹配) const dynamoClient = new DynamoDBClient({ region: process.env.REGION }); const dynamoInput = { Key: { id: { S: userId } }, TableName: "RecordTable", }; const dynamoCommand = new GetItemCommand(dynamoInput); const response = await dynamoClient.send(dynamoCommand); // 提取已购手机列表 let purchasedPhones = []; if (response.Item?.purchasedPhones) { purchasedPhones = response.Item.purchasedPhones.SS || []; } // 判断是否购买过目标手机 const isPurchased = purchasedPhones.includes(targetPhoneId); return { isAuthorized: isPurchased, resolverContext: { userid: userId, info: user, more_info: response, }, ttlOverride: 300, }; } catch (error) { console.error('Authorization failed:', error); return { isAuthorized: false, ttlOverride: 300 }; } };
内容的提问来源于stack exchange,提问作者user16257618

