Gin 如果在中间件中绑定了`request body`, c.Request.Body 变为0
Gin If `request body` bound in middleware, c.Request.Body become 0
我的 API 服务器有中间件从请求 header 获取令牌。
如果访问正确,则进入下一个功能。
但是request去了中间件,去了下一个函数,c.Request.Body
变成了0
。
中间件
func getUserIdFromBody(c *gin.Context) (int) {
var jsonBody User
length, _ := strconv.Atoi(c.Request.Header.Get("Content-Length"))
body := make([]byte, length)
length, _ = c.Request.Body.Read(body)
json.Unmarshal(body[:length], &jsonBody)
return jsonBody.Id
}
func CheckToken() (gin.HandlerFunc) {
return func(c *gin.Context) {
var userId int
config := model.NewConfig()
reqToken := c.Request.Header.Get("token")
_, resBool := c.GetQuery("user_id")
if resBool == false {
userId = getUserIdFromBody(c)
} else {
userIdStr := c.Query("user_id")
userId, _ = strconv.Atoi(userIdStr)
}
...
if ok {
c.Nex()
return
}
}
下一个函数
func bindOneDay(c *gin.Context) (model.Oneday, error) {
var oneday model.Oneday
if err := c.BindJSON(&oneday); err != nil {
return oneday, err
}
return oneday, nil
}
bindOneDay
return EOF
错误。因为也许 c.Request.Body
是 0
.
我想在中间件中从请求 body 中获取 user_id
。
如何做到 c.Request.Body
变成 0
没有问题
您只能从客户端读取一次正文。数据是从用户那里流出来的,他们不会再发送它。如果您想多次阅读它,您将不得不 将整个内容缓冲 到内存中,如下所示:
bodyCopy := new(bytes.Buffer)
// Read the whole body
_, err := io.Copy(bodyCopy, req.Body)
if err != nil {
return err
}
bodyData := bodyCopy.Bytes()
// Replace the body with a reader that reads from the buffer
req.Body = ioutil.NopCloser(bytes.NewReader(bodyData))
// Now you can do something with the contents of bodyData,
// like passing it to json.Unmarshal
请注意,将整个请求缓冲到内存中意味着用户可能会导致您分配无限内存——您可能应该在前端代理中阻止它或使用 io.LimitedReader
来限制数据量你会缓冲。
在 Unmarshal 开始工作之前,您还必须阅读整个正文——这可能没什么大不了的,但如果您愿意,使用 io.TeeReader
和 json.NewDecoder
可以做得更好倾斜。
当然,更好的方法是想出一种方法来重构您的代码,这样就不需要缓冲正文并将其解码两次。
我的 API 服务器有中间件从请求 header 获取令牌。 如果访问正确,则进入下一个功能。
但是request去了中间件,去了下一个函数,c.Request.Body
变成了0
。
中间件
func getUserIdFromBody(c *gin.Context) (int) {
var jsonBody User
length, _ := strconv.Atoi(c.Request.Header.Get("Content-Length"))
body := make([]byte, length)
length, _ = c.Request.Body.Read(body)
json.Unmarshal(body[:length], &jsonBody)
return jsonBody.Id
}
func CheckToken() (gin.HandlerFunc) {
return func(c *gin.Context) {
var userId int
config := model.NewConfig()
reqToken := c.Request.Header.Get("token")
_, resBool := c.GetQuery("user_id")
if resBool == false {
userId = getUserIdFromBody(c)
} else {
userIdStr := c.Query("user_id")
userId, _ = strconv.Atoi(userIdStr)
}
...
if ok {
c.Nex()
return
}
}
下一个函数
func bindOneDay(c *gin.Context) (model.Oneday, error) {
var oneday model.Oneday
if err := c.BindJSON(&oneday); err != nil {
return oneday, err
}
return oneday, nil
}
bindOneDay
return EOF
错误。因为也许 c.Request.Body
是 0
.
我想在中间件中从请求 body 中获取 user_id
。
如何做到 c.Request.Body
变成 0
您只能从客户端读取一次正文。数据是从用户那里流出来的,他们不会再发送它。如果您想多次阅读它,您将不得不 将整个内容缓冲 到内存中,如下所示:
bodyCopy := new(bytes.Buffer)
// Read the whole body
_, err := io.Copy(bodyCopy, req.Body)
if err != nil {
return err
}
bodyData := bodyCopy.Bytes()
// Replace the body with a reader that reads from the buffer
req.Body = ioutil.NopCloser(bytes.NewReader(bodyData))
// Now you can do something with the contents of bodyData,
// like passing it to json.Unmarshal
请注意,将整个请求缓冲到内存中意味着用户可能会导致您分配无限内存——您可能应该在前端代理中阻止它或使用 io.LimitedReader
来限制数据量你会缓冲。
在 Unmarshal 开始工作之前,您还必须阅读整个正文——这可能没什么大不了的,但如果您愿意,使用 io.TeeReader
和 json.NewDecoder
可以做得更好倾斜。
当然,更好的方法是想出一种方法来重构您的代码,这样就不需要缓冲正文并将其解码两次。