package publicmessage
import (
"bytes"
"XXXXXX/global" //本地项目封装的全局量,需要使用,换成自己封装的日志库即可
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"github.com/gin-gonic/gin"
)
// 透传http请求
func ProxyHandler(ctx *gin.Context) {
var err error
req := &http.Request{}
defer func() {
if err != nil {
global.GIN_LOG.ErrorWithContext(ctx, err)
}
}()
r := ctx.Request
bodyBytes, _ := io.ReadAll(ctx.Request.Body)
global.GIN_LOG.InfoWithContext(ctx, fmt.Sprintf("ProxyHandler method:%+v,url:%+v,body:%+v", r.Method, r.RequestURI, string(bodyBytes)))
// 创建新的reader,使用bytes.NewReader
bodyReader := bytes.NewReader(bodyBytes)
// 恢复r.Body,以便可以多次读取
r.Body = io.NopCloser(bodyReader)
// 创建一个新的请求
req, err = http.NewRequest(r.Method, r.RequestURI, r.Body)
if err != nil {
if ctx.Writer != nil {
http.Error(ctx.Writer, err.Error(), http.StatusInternalServerError)
}
return
}
// 复制原始请求的Header
for name, values := range r.Header {
for _, value := range values {
req.Header.Add(name, value)
}
}
global.GIN_LOG.InfoWithContext(ctx, fmt.Sprintf("ProxyHandler request header:%+v", req.Header))
// 发送请求到服务public_message
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
http.Error(ctx.Writer, err.Error(), http.StatusInternalServerError)
return
}
defer resp.Body.Close()
// 将服务public_message的响应头部写回到原始响应中
for name, values := range resp.Header {
for _, value := range values {
ctx.Writer.Header().Add(name, value)
}
}
// 将服务public_message的响应写回到原始响应中
body, err := io.ReadAll(resp.Body)
if err != nil {
http.Error(ctx.Writer, err.Error(), http.StatusInternalServerError)
return
}
global.GIN_LOG.InfoWithContext(ctx, fmt.Sprintf("ProxyHandler response body:%+v", string(body)))
ctx.Writer.Write(body)
}
这段代码我只是想做一下透传处理,把App端的http请求通过我的服务透传给另外一个服务,为了方便出了问题方便查询定位,我就把透传进来的http请求信息打印了一下,就是因为这个操作埋下了一个坑。在Go语言的HTTP处理中,io.ReadAll(ctx.Request.Body)
读取了请求体的全部内容,并将它存储在一个字节切片中。这个操作会消耗掉请求体的内容,因此,如果你尝试再次读取ctx.Request.Body
,你会得到一个空的读取器(reader),因为数据已经被读取过了。