使用AWS SDK v3向LocalStack SQS发消息时遇反序列化错误
运行Express.js应用,尝试使用AWS SDK v3将字符串化对象消息推送到LocalStack的SQS队列中。已通过awslocal sqs create-queue --queue-name my-queue命令创建队列,依赖版本为"@aws-sdk/client-sqs": "^3.428.0",但出现以下错误:
SyntaxError: Unexpected token '<', "<?xml vers"... is not valid JSON
Deserialization error: to see the raw response, inspect the hidden field {error}.$response on this object.
at JSON.parse
附带代码:
app.js
const express = require("express"); const { SQS, SendMessageCommand } = require("@aws-sdk/client-sqs"); const app = express(); const port = 3000; // Define the LocalStack SQS endpoint const localStackEndpoint = "http://localhost:4566"; // Configure the SQS client const sqs = new SQS({ region: "us-east-1", // Specify any region (LocalStack doesn't enforce region constraints) endpoint: localStackEndpoint, }); // Route to send a message to the SQS queue app.post("/test", async (req, res) => { try { // Specify the URL of your LocalStack SQS queue const queueUrl = `${localStackEndpoint}/000000000000/my-queue`; // Define the message body const messageBody = "Hello, LocalStack!"; // Create the SendMessageCommand const sendMessageCommand = new SendMessageCommand({ QueueUrl: queueUrl, MessageBody: messageBody, }); // Send the message const sendMessageResponse = await sqs.send(sendMessageCommand); console.log("Message sent successfully:", sendMessageResponse); res.send("Message sent successfully"); } catch (error) { console.error("Error sending message:", error); res.status(500).send("Error sending message"); } }); // Start the server app.listen(port, () => { console.log(`Server is running at http://localhost:${port}`); });
docker-compose.yml
version: '3' services: localstack: image: localstack/localstack ports: - '4566-4599:4566-4599' - '8080:8080' environment: - SERVICES=sqs - DEBUG=1 - DATA_DIR=/tmp/localstack/data - PORT_WEB_UI=8080 volumes: - '${TMPDIR:-/tmp/localstack}:/var/lib/localstack'
该错误是因为AWS SDK v3收到了LocalStack返回的XML格式响应,而非预期的JSON,核心原因是客户端配置缺失或队列URL构造错误。以下是修复步骤:
1. 完善SQS客户端配置
AWS SDK v3需要凭证信息才能发起请求,即使LocalStack不验证真实凭证,也需提供占位值;同时开启路径风格适配,确保请求符合LocalStack的接口规范:
const sqs = new SQS({ region: "us-east-1", endpoint: localStackEndpoint, credentials: { accessKeyId: "test", secretAccessKey: "test" }, forcePathStyle: true // 强制使用路径风格的endpoint,适配LocalStack });
2. 动态获取队列URL
避免硬编码队列URL导致格式错误,改用GetQueueUrlCommand从LocalStack获取正确的队列地址:
// 先导入GetQueueUrlCommand const { SQS, SendMessageCommand, GetQueueUrlCommand } = require("@aws-sdk/client-sqs"); // 在/test路由中替换硬编码的queueUrl const getQueueUrlCmd = new GetQueueUrlCommand({ QueueName: "my-queue" }); const queueUrlResult = await sqs.send(getQueueUrlCmd); const queueUrl = queueUrlResult.QueueUrl;
修复后的完整app.js
const express = require("express"); const { SQS, SendMessageCommand, GetQueueUrlCommand } = require("@aws-sdk/client-sqs"); const app = express(); const port = 3000; const localStackEndpoint = "http://localhost:4566"; const sqs = new SQS({ region: "us-east-1", endpoint: localStackEndpoint, credentials: { accessKeyId: "test", secretAccessKey: "test" }, forcePathStyle: true }); app.post("/test", async (req, res) => { try { // 动态获取队列URL const getQueueUrlCmd = new GetQueueUrlCommand({ QueueName: "my-queue" }); const queueUrlResult = await sqs.send(getQueueUrlCmd); const queueUrl = queueUrlResult.QueueUrl; const messageBody = "Hello, LocalStack!"; const sendMessageCommand = new SendMessageCommand({ QueueUrl: queueUrl, MessageBody: messageBody, }); const sendMessageResponse = await sqs.send(sendMessageCommand); console.log("Message sent successfully:", sendMessageResponse); res.send("Message sent successfully"); } catch (error) { console.error("Error sending message:", error); res.status(500).send("Error sending message"); } }); app.listen(port, () => { console.log(`Server is running at http://localhost:${port}`); });
验证
重启Express应用,调用POST /test接口,查看控制台日志是否输出消息发送成功的信息,同时可通过awslocal sqs receive-message --queue-name my-queue命令验证队列中是否收到消息。
内容的提问来源于stack exchange,提问作者a8hok

