DynamoDB JavaScript PutItemCommand执行报错:NUMBER_VALUE无法转String
问题描述
注意: 尽管本问题提及AWS SAM,但核心是100%的DynamoDB JavaScript SDK问题,任何有使用AWS DynamoDB客户端/SDK编写JavaScript Lambda(或客户端应用)经验的人都可解答。
我用AWS SAM创建了一个DynamoDB表,配置如下:
FeedbackDynamoDB: Type: AWS::DynamoDB::Table Properties: TableName: commentary AttributeDefinitions: - AttributeName: id AttributeType: S KeySchema: - AttributeName: id KeyType: HASH ProvisionedThroughput: ReadCapacityUnits: 5 WriteCapacityUnits: 5 StreamSpecification: StreamViewType: NEW_IMAGE
该配置成功创建了名为commentary的表,控制台查看细节:
- 分区键为id(类型S)
- 无排序键
- 无索引
- 读写容量模式为预置容量(5单位)
我写了一个TypeScript Lambda函数,用JS SDK向表插入记录,代码如下:
app.ts
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda'; import { Commentary } from './commentary'; import { PutItemCommand } from "@aws-sdk/client-dynamodb"; import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; export const lambdaHandler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => { try { const ddbClient = new DynamoDBClient({ region: "us-east-1" }); let status: number = 200; let responseBody: string = "\"message\": \"hello world\""; const { id, content, createdAt, providerId, receiverId } = JSON.parse(event.body); const commentary = new Commentary(id, content, createdAt, providerId, receiverId); console.log("deserialized this into commentary"); console.log("and the deserialized commentary has content of: " + commentary.getContent()); await provideCommentary(ddbClient, commentary); responseBody = "\"message\": \"received commentary -- check dynamoDb!\""; return { statusCode: status, body: responseBody }; } catch (err) { console.log(err); return { statusCode: 500, body: JSON.stringify({ message: err.stack, }), }; } }; const provideCommentary = async (ddbClient: DynamoDBClient, commentary: Commentary) => { const params = { TableName: "commentary", Item: { id: { S: commentary.getId() }, content: { S: commentary.getContent() }, createdAt: { S: commentary.getCreatedAt() }, providerId: { N: commentary.getProviderId() }, receiverId: { N: commentary.getReceiverId() } } }; console.log("about to try to insert commentary into dynamo..."); try { console.log("wait for it...") const rc = await ddbClient.send(new PutItemCommand(params)); console.log("DDB response:", rc); } catch (err) { console.log("hmmm something awry. something....in the mist"); console.log("Error", err.stack); throw err; } };
commentary.ts
class Commentary { private id: string; private content: string; private createdAt: Date; private providerId: number; private receiverId: number; constructor(id: string, content: string, createdAt: Date, providerId: number, receiverId: number) { this.id = id; this.content = content; this.createdAt = createdAt; this.providerId = providerId; this.receiverId = receiverId; } public getId(): string { return this.id; } public getContent(): string { return this.content; } public getCreatedAt(): Date { return this.createdAt; } public getProviderId(): number { return this.providerId; } public getReceiverId(): number { return this.receiverId; } } export { Commentary };
用以下curl命令触发Lambda:
curl -i --request POST 'https://<my-api-gateway>.execute-api.us-east-1.amazonaws.com/Stage/feedback' \ --header 'Content-Type: application/json' -d '{"id":"123","content":"test feedback","createdAt":"2022-12-02T08:45:26.261-05:00","providerId":457,"receiverId":789}'
返回HTTP 500错误:
{"message":"SerializationException: NUMBER_VALUE cannot be converted to String\n
请问是请求体问题,还是需要调整app.ts或commentary.ts中的代码?
解决方案
你遇到的SerializationException是两处代码错误导致的:
1. createdAt字段类型不匹配
- 在
lambdaHandler中,你直接将JSON解析后的字符串createdAt传给Commentary构造函数,但构造函数期望的是Date类型,需要先转换:const { id, content, createdAt, providerId, receiverId } = JSON.parse(event.body); // 转换为Date对象 const commentary = new Commentary(id, content, new Date(createdAt), providerId, receiverId); - 在
provideCommentary函数中,不能直接将Date对象作为DynamoDB的S类型值传入,需要转为字符串格式:createdAt: { S: commentary.getCreatedAt().toISOString() }
2. 数字类型字段格式错误
DynamoDB的N类型要求传入字符串形式的数字,而非JavaScript原生number类型。你当前直接传递number类型的providerId和receiverId,会触发类型转换错误,需要转为字符串:
providerId: { N: commentary.getProviderId().toString() }, receiverId: { N: commentary.getReceiverId().toString() }
额外优化建议
如果不想手动处理DynamoDB的类型格式,可以使用@aws-sdk/util-dynamodb包中的marshall工具函数,自动将JavaScript对象转换为DynamoDB接受的格式,简化代码:
import { marshall } from "@aws-sdk/util-dynamodb"; // 在provideCommentary中替换Item定义 Item: marshall({ id: commentary.getId(), content: commentary.getContent(), createdAt: commentary.getCreatedAt().toISOString(), providerId: commentary.getProviderId(), receiverId: commentary.getReceiverId() })
内容的提问来源于stack exchange,提问作者hotmeatballsoup
相关产品推荐
相关产品推荐

