Node.js 16版Lambda向DynamoDB存储时间戳失败求助
问题:Lambda写入DynamoDB时触发ValidationException(测试正常,实际执行失败)
问题描述
使用Node.js 16版本的Lambda函数向DynamoDB存储数据时,本地测试用例能成功写入,但实际由Amazon Connect触发时抛出ValidationException,错误提示为Supplied AttributeValue is empty, must contain exactly one of the supported datatypes。需要存储的LastUpdateTimestamp是联系人最后更新的UTC时间字符串,格式为yyyy-mm-ddThh:mm:ssZ。
相关Lambda代码
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: MIT-0 const AWS = require('aws-sdk'); const ddb = new AWS.DynamoDB(); exports.handler = async (event) => { var surveyResults = {}; var data = event.Details.ContactData.Attributes; // extract the contact attributes from the Amazon Connect event Object.keys(data).forEach(element => { if (element.startsWith("survey_result_")) { surveyResults[element] = { S: data[element] }; } }); var params = { TableName: process.env.TABLE, Item: { contactId: { S: event.Details.ContactData.ContactId}, date: { S: event.Details.ContactData.LastUpdateTimestamp}, surveyId: { S: event.Details.ContactData.Attributes.surveyId }, ...surveyResults } } try { await ddb.putItem(params).promise(); // write the object to the DynamoDB table } catch (err) { console.log(err); } const response = { statusCode: 200, body: JSON.stringify('OK'), }; return response; };
测试数据
{ "Name": "ContactFlowEvent", "Details": { "ContactData": { "Attributes": { "surveyId": "123456", "survey_result_1": "4", "survey_result_2": "5" }, "Channel": "VOICE", "ContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe", "LastUpdateTimestamp": "2023-02-14T06:52:29Z", "CustomerEndpoint": { "Address": "+11234567890", "Type": "TELEPHONE_NUMBER" }, "InitialContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe", "InitiationMethod": "API", "InstanceARN": "arn:aws:connect:us-east-1:123456789012:instance/9308c2a1-9bc6-4cea-8290-6c0b4a6d38fa", "MediaStreams": { "Customer": { "Audio": { "StartFragmentNumber": "91343852333181432392682062622220590765191907586", "StartTimestamp": "1565781909613", "StreamARN": "arn:aws:kinesisvideo:us-east-1:123456789012:stream/connect-contact-a3d73b84-ce0e-479a-a9dc-5637c9d30ac9/1565272947806" } } }, "PreviousContactId": "5ca32fbd-8f92-46af-92a5-6b0f970f0efe", "Queue": null, "SystemEndpoint": { "Address": "+11234567890", "Type": "TELEPHONE_NUMBER" } }, "Parameters": {} } }
触发时的错误信息
2023-02-15T14:10:26.426Z 94fad25e-bcdb-443b-95c0-47640bfaba34 INFO ValidationException: Supplied AttributeValue is empty, must contain exactly one of the supported datatypes at Request.extractError (/var/runtime/node_modules/aws-sdk/lib/protocol/json.js:52:27) at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:106:20) at Request.emit (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:78:10) at Request.emit (/var/runtime/node_modules/aws-sdk/lib/request.js:686:14) at Request.transition (/var/runtime/node_modules/aws-sdk/lib/request.js:22:10) at AcceptorStateMachine.runTo (/var/runtime/node_modules/aws-sdk/lib/state_machine.js:14:12) at /var/runtime/node_modules/aws-sdk/lib/state_machine.js:26:10 at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:38:9) at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:688:12) at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:116:18) { code: 'ValidationException', time: 2023-02-15T14:10:26.328Z, requestId: 'F5JJOCF20D507JCFRO7FEMAH6VVV4KQNSO5AEMVJF66Q9ASUAAJG', statusCode: 400, retryable: false, retryDelay: 15.458319111471575 }
问题分析与解决方案
核心原因
错误提示明确说明:DynamoDB收到了空的属性值(AttributeValue),也就是你组装的Item里,某个字段的{ S: 值 }中,值为空或未定义。
测试数据里所有必填字段都有有效值,但实际触发时,Amazon Connect传递的event可能存在以下情况:
LastUpdateTimestamp字段未生成或为空Attributes.surveyId未被正确传入- 部分
survey_result_*属性为空
这些情况都会导致组装出{ S: undefined }的无效结构,触发DynamoDB的校验错误。
修复方案
方案1:添加字段校验与过滤(基于原代码修改)
在组装params前,先校验必填字段是否存在,同时过滤掉空的survey结果:
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: MIT-0 const AWS = require('aws-sdk'); const ddb = new AWS.DynamoDB(); exports.handler = async (event) => { var surveyResults = {}; // 先获取关键节点,避免链式调用报错 const contactData = event.Details?.ContactData; const attributes = contactData?.Attributes || {}; // 校验必填字段是否存在且有值 if (!contactData?.ContactId || !contactData?.LastUpdateTimestamp || !attributes?.surveyId) { console.error('缺失必填字段:', { ContactId: contactData?.ContactId, LastUpdateTimestamp: contactData?.LastUpdateTimestamp, surveyId: attributes?.surveyId }); return { statusCode: 400, body: JSON.stringify('缺失必填字段') }; } // 仅保留有值的survey结果 Object.keys(attributes).forEach(element => { if (element.startsWith("survey_result_") && attributes[element]) { surveyResults[element] = { S: attributes[element] }; } }); var params = { TableName: process.env.TABLE, Item: { contactId: { S: contactData.ContactId }, date: { S: contactData.LastUpdateTimestamp }, surveyId: { S: attributes.surveyId }, ...surveyResults } } try { await ddb.putItem(params).promise(); console.log('数据保存成功'); } catch (err) { console.error('数据保存失败:', err); return { statusCode: 500, body: JSON.stringify('数据保存失败') }; } return { statusCode: 200, body: JSON.stringify('OK'), }; };
方案2:使用DocumentClient简化操作(推荐)
AWS SDK的DocumentClient可以自动处理数据类型映射,无需手动写{ S: 值 },大幅降低出错概率:
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: MIT-0 const AWS = require('aws-sdk'); const ddb = new AWS.DynamoDB.DocumentClient(); // 改用DocumentClient exports.handler = async (event) => { var surveyResults = {}; const contactData = event.Details?.ContactData; const attributes = contactData?.Attributes || {}; if (!contactData?.ContactId || !contactData?.LastUpdateTimestamp || !attributes?.surveyId) { console.error('缺失必填字段:', { ContactId: contactData?.ContactId, LastUpdateTimestamp: contactData?.LastUpdateTimestamp, surveyId: attributes?.surveyId }); return { statusCode: 400, body: JSON.stringify('缺失必填字段') }; } Object.keys(attributes).forEach(element => { if (element.startsWith("survey_result_") && attributes[element]) { surveyResults[element] = attributes[element]; // 无需手动指定类型 } }); var params = { TableName: process.env.TABLE, Item: { contactId: contactData.ContactId, date: contactData.LastUpdateTimestamp, surveyId: attributes.surveyId, ...surveyResults } } try { await ddb.put(params).promise(); // 使用put方法替代putItem console.log('数据保存成功'); } catch (err) { console.error('数据保存失败:', err); return { statusCode: 500, body: JSON.stringify('数据保存失败') }; } return { statusCode: 200, body: JSON.stringify('OK'), }; };
内容的提问来源于stack exchange,提问作者Raja
相关产品推荐
相关产品推荐

