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

Lambda中从SQS事件构建HTTP请求时无法读取请求体的问题排查

问题

我正在Lambda中使用Go语言代码从SQS事件构建HTTP请求。目前我能获取到请求头,但请求体是空的。如果原始请求带有Content-Length头,请求体里就有内容,但这个请求是外部系统生成的,并不包含这个头。请问我哪里操作出错了?

代码示例

package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
)

func main() {
	sqsEvent := `{
	"Type" : "Notification",
	"MessageId" : "d66493a6-7e62-5104-9e8f-c8de60b5fb85",
	"TopicArn" : "arn:aws:sns:us-east-1:1234567890:msgbridge-my-sns-topic",
	"Message" : "POST /endpoint HTTP/1.1\r\nAccept: */*\r\nAccept-Encoding: deflate, gzip\r\nContent-Type: application/json\r\nX-Amzn-Trace-Id: Root=1-62fe1c96-098f3f26680ffa54742d9aa2\r\nX-Forwarded-For: 173.252.127.12\r\nX-Forwarded-Port: 443\r\nX-Forwarded-Proto: https\r\n\r\n{\"status\":\"ok\"}",
	"Timestamp" : "2022-08-18T11:03:51.069Z",
	"SignatureVersion" : "1",
	"Signature" : "s+M8QtMEYC5XfBIFIokrI8+ChHSTAzp7i+/1+hSkVQx/0vzQA7g6GQle5jrYPclrob2oIWy7FeEs+EGoKPZQ8YuxdZf6xrGIpV7qyVUcrLWpxm0HyYViJ3q/qbcjZPWmVxaYrOV30m1CBkpDcwYvFGHl0neFefkOpybZAdRgEXkbChAlanAiIgh91Sm1IWD4J1Doth1UF84afMov9TfSZMSgGQ+e7LCnNMtcSpUT34aDQDUxOB51NsLd9+tmYSzdqIanUQItcaB2zGJFp/yk2u6pJUjLJ1oybh3olgjhrmVQrO8mBTOVbMH3i/TyZUl1aogikhe+3hKQoDoCuMc/KQ=="
}`
	var req struct {
		Message string `json:"Message"`
	}
	if err := json.Unmarshal([]byte(sqsEvent), &req); err != nil {
		fmt.Printf("error unmarshalling; error=%s\n", err)
		return
	}
	hreq, err := http.ReadRequest(bufio.NewReader(strings.NewReader(req.Message)))
	if err != nil {
		fmt.Printf("failed to convert message body to http request, error=%s\n", err)
		return
	}
	fmt.Printf("X-Amzn-Trace-Id=%s\n", hreq.Header.Get("X-Amzn-Trace-Id"))
	body, err := ioutil.ReadAll(hreq.Body)
	if err != nil {
		fmt.Printf("failed to read body, error=%s\n", err)
		return
	}
	fmt.Printf("Body = %s\n", string(body))
}

运行输出

go run .
X-Amzn-Trace-Id=Root=1-62fe1c96-098f3f26680ffa54742d9aa2
Body =

解决方案

这个问题我之前也碰到过,核心原因是Go标准库的http.ReadRequest严格遵循HTTP/1.1规范:当请求里没有Content-Length或者Transfer-Encoding头时,它会默认认为请求没有体部,直接跳过后面的内容读取。你的外部系统生成的请求虽然有正确的\r\n\r\n分界符,但因为缺少Content-Length,所以体部被忽略了。

解决办法很直接,我们手动拆分请求的头部和体部,再把体部内容赋值给HTTP请求对象即可:

修改后的完整代码

package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
)

func main() {
	sqsEvent := `{
	"Type" : "Notification",
	"MessageId" : "d66493a6-7e62-5104-9e8f-c8de60b5fb85",
	"TopicArn" : "arn:aws:sns:us-east-1:1234567890:msgbridge-my-sns-topic",
	"Message" : "POST /endpoint HTTP/1.1\r\nAccept: */*\r\nAccept-Encoding: deflate, gzip\r\nContent-Type: application/json\r\nX-Amzn-Trace-Id: Root=1-62fe1c96-098f3f26680ffa54742d9aa2\r\nX-Forwarded-For: 173.252.127.12\r\nX-Forwarded-Port: 443\r\nX-Forwarded-Proto: https\r\n\r\n{\"status\":\"ok\"}",
	"Timestamp" : "2022-08-18T11:03:51.069Z",
	"SignatureVersion" : "1",
	"Signature" : "s+M8QtMEYC5XfBIFIokrI8+ChHSTAzp7i+/1+hSkVQx/0vzQA7g6GQle5jrYPclrob2oIWy7FeEs+EGoKPZQ8YuxdZf6xrGIpV7qyVUcrLWpxm0HyYViJ3q/qbcjZPWmVxaYrOV30m1CBkpDcwYvFGHl0neFefkOpybZAdRgEXkbChAlanAiIgh91Sm1IWD4J1Doth1UF84afMov9TfSZMSgGQ+e7LCnNMtcSpUT34aDQDUxOB51NsLd9+tmYSzdqIanUQItcaB2zGJFp/yk2u6pJUjLJ1oybh3olgjhrmVQrO8mBTOVbMH3i/TyZUl1aogikhe+3hKQoDoCuMc/KQ=="
}`
	var req struct {
		Message string `json:"Message"`
	}
	if err := json.Unmarshal([]byte(sqsEvent), &req); err != nil {
		fmt.Printf("error unmarshalling; error=%s\n", err)
		return
	}

	// 拆分请求的头部和体部,HTTP规范中用\r\n\r\n作为分界
	requestParts := strings.SplitN(req.Message, "\r\n\r\n", 2)
	if len(requestParts) != 2 {
		fmt.Println("invalid request format: missing body separator")
		return
	}

	// 先解析请求头部部分(保留分界符让ReadRequest正常处理)
	httpReq, err := http.ReadRequest(bufio.NewReader(strings.NewReader(requestParts[0] + "\r\n\r\n")))
	if err != nil {
		fmt.Printf("failed to parse request headers, error=%s\n", err)
		return
	}

	// 手动设置请求体和Content-Length信息
	bodyContent := requestParts[1]
	httpReq.Body = ioutil.NopCloser(strings.NewReader(bodyContent))
	httpReq.ContentLength = int64(len(bodyContent))
	httpReq.Header.Set("Content-Length", fmt.Sprintf("%d", len(bodyContent)))

	// 验证结果
	fmt.Printf("X-Amzn-Trace-Id=%s\n", httpReq.Header.Get("X-Amzn-Trace-Id"))
	body, err := ioutil.ReadAll(httpReq.Body)
	if err != nil {
		fmt.Printf("failed to read body, error=%s\n", err)
		return
	}
	fmt.Printf("Body = %s\n", string(body))
}

关键修改点说明

  • 用strings.SplitN把SQS消息里的HTTP请求拆成头部和体部两部分,分界符是HTTP标准的\r\n\r\n
  • 解析请求头部时,只传头部内容加分界符,让http.ReadRequest正常解析头部信息,不会因为缺少Content-Length而报错
  • 手动把体部内容赋值给httpReq.Body,同时设置ContentLength字段和对应的请求头,确保后续读取体部的逻辑能正常工作

现在运行修改后的代码,就能正确输出请求体内容了。

内容的提问来源于stack exchange,提问作者Arvind

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 18:37:27