Джин Если `запрос тела` связан в промежуточном программном обеспечении, c.Request.Body становится 0
Мой API-сервер имеет промежуточное ПО, которое получает токен из заголовка запроса. Если это правильный доступ, его функция перейти к следующей.
Но запрос перешел к промежуточному программному обеспечению и перешел к следующей функции, 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
вернуть ошибку с EOF
, потому что возможно c.Request.Body
является 0
,
я хочу получить user_id
из тела запроса в средней посуде. Как это сделать без проблем, что c.Request.Body
стать 0
2 ответа
Вы можете прочитать тело только один раз. Данные передаются от пользователя, и они не собираются отправлять их снова. Если вы хотите прочитать это более одного раза, вам придется буферизовать все это в памяти, например так:
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
если вы так склонны.
Конечно, лучше было бы найти способ реструктурировать ваш код так, чтобы ненужная буферизация тела и его расшифровка.
Gin предоставляет собственное решение, позволяющее многократно получать данные из файлов . Решение заключается в использованииc.ShouldBindBodyWith
. Согласно документации джина
ShouldBindBodyWith... сохраняет тело запроса в контексте и повторно использует его при повторном вызове.
Для вашего конкретного примера это будет реализовано в вашем промежуточном программном обеспечении следующим образом:
func getUserIdFromBody(c *gin.Context) (int) {
var jsonBody User
if err := c.ShouldBindBodyWith(&jsonBody, binding.JSON); err != nil {
//return error
}
return jsonBody.Id
}
После промежуточного программного обеспечения, если вы хотите снова выполнить привязку к телу, просто используйтеctx.ShouldBindBodyWith
снова. Для вашего конкретного примера это будет реализовано так
func bindOneDay(c *gin.Context) (model.Oneday, error) {
var oneday model.Oneday
if err := c.ShouldBindBodyWith(&oneday); err != nil {
return error
}
return oneday, nil
}
Проблема, с которой мы боремся, заключается в том, чтоgin
настроен какio.ReadCloser
объект -- это означает, что он предназначен для чтения только один раз. Таким образом, если вы вообще получите доступ к своему коду, байты будут прочитаны (потреблены) и после этого будут пустыми. ИспользуяShouldBindBodyWith
для доступа к байтам gin сохраняет байты в другом механизме хранения в контексте, чтобы его можно было использовать снова и снова.
В качестве примечания, если вы использовали и позже хотите получить доступc.Request.Body
, вы можете сделать это, подключившись к механизму хранения джина черезctx.Get(gin.BodyBytesKey)
. Вот пример того, как вы можете получить тело запроса, хранящееся в gin, как[]byte
а затем преобразовать его в строку,
var body string
if cb, ok := ctx.Get(gin.BodyBytesKey); ok {
if cbb, ok := cb.([]byte); ok {
body = string(cbb)
}
}