You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 15:50:27