英文:
Getting EOF for a valid json when doing a gin.Context.ShouldBind()
问题
我遇到了一个之前没有出现过的奇怪错误,可能是由于我没有彻底测试的JWT
实现引起的。所以我有以下的代码:
ginGine.POST("/edge_device_info", func(ctx *gin.Context) {
reqMap := helpers.GetJSONFromReqBody(ctx.Request.Body, ctx)
accessToken := fmt.Sprintf("%v", reqMap["access_token"])
tokenValid, jwtErr := srvHelpers.ValidateJWTToken(accessToken)
if jwtErr != nil || !tokenValid {
fmt.Println("invalid token")
ctx.JSON(http.StatusBadRequest, gin.H{
"error0": jwtErr.Error(),
})
return
}
// reqBody, err := json.Marshal(reqMap)
// if err != nil {
// ctx.JSON(http.StatusBadRequest, gin.H{
// "error10": err.Error(),
// })
// return
// }
var edge_device_info models.EdgeDeviceInfo
// err = json.Unmarshal(reqBody, &edge_device_info)
err := ctx.ShouldBind(&edge_device_info)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error1": err.Error(),
})
return
}
// more code
})
这个API端点接收到类似的json
数据:
{
"name": "一些文本",
"description": "另一些文本",
"access_token": "SOME_JWT_TOKEN"
}
现在,这个json
看起来没问题,对吧?但是ShouldBind()
不喜欢它,会返回一个EOF
错误。如果你检查代码的顶部部分,我将接收到的JSON
体转换为一个map,然后使用access_token
(一个JWT
令牌)来验证用户的访问权限。我不太确定这个过程是否会导致这个问题,这就是我想知道的。这就是我提出这个问题的原因。我已经找到了解决方法,就是上面被注释掉的代码。这很奇怪,因为首先创建map意味着请求体的JSON
是有效的。
英文:
I'm getting a weird error which previously didn't appear for my code, which might be caused by my JWT
implementation that I didn't test thoroughly tbh. So I have this current code:
ginGine.POST("/edge_device_info", func(ctx *gin.Context) {
reqMap := helpers.GetJSONFromReqBody(ctx.Request.Body, ctx)
accessToken := fmt.Sprintf("%v", reqMap["access_token"])
tokenValid, jwtErr := srvHelpers.ValidateJWTToken(accessToken)
if jwtErr != nil || !tokenValid {
fmt.Println("invalid token")
ctx.JSON(http.StatusBadRequest, gin.H{
"error0": jwtErr.Error(),
})
return
}
// reqBody, err := json.Marshal(reqMap)
// if err != nil {
// ctx.JSON(http.StatusBadRequest, gin.H{
// "error10": err.Error(),
// })
// return
// }
var edge_device_info models.EdgeDeviceInfo
// err = json.Unmarshal(reqBody, &edge_device_info)
err := ctx.ShouldBind(&edge_device_info)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"error1": err.Error(),
})
return
}
// more code
})
This API endpoint gets sent this similar looking json
:
{
"name": "Some text",
"description": "Another bunch of text",
"access_token": "SOME_JWT_TOKEN"
}
Now, this json looks fine, right? But ShouldBind()
doesn't like that and would return an EOF
. If you check the top part of my code, I'm converting the received JSON
body to a map then use the access_token
, which is a JWT
token, to validate the user's access. I'm not quite sure if that process somehow made that issue, and that's what I wanted to know. That's the reason I made this question. I already found the workaround, which is the commented code above. Which is quite weird as creating the map in the first place means that the request body's JSON
was valid.
答案1
得分: 1
正如@Zeke Lu所指出的那样,你应该在gin.Context
结构体上使用ShouldBindBodyWith
方法。我为你编写了一个小例子供你使用。
package main
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
)
type Todo struct {
Description string `json:"description" binding:"required"`
}
func Todos(c *gin.Context) {
// 第一次读取请求体
var todo Todo
if err := c.ShouldBindBodyWith(&todo, binding.JSON); err != nil {
c.JSON(http.StatusBadRequest, err)
return
}
fmt.Println("第一次:", todo)
// 再次读取请求体
var todoSecondTime Todo
if err := c.ShouldBindBodyWith(&todoSecondTime, binding.JSON); err != nil {
c.JSON(http.StatusBadRequest, err)
return
}
fmt.Println("第二次:", todoSecondTime)
}
func main() {
gin.SetMode(gin.DebugMode)
r := gin.Default()
r.POST("/todos", Todos)
r.Run(":8080")
}
这段代码非常简单,但它的目的只是提供一个有价值的示例。如果你需要多次读取请求体,就像你的场景一样,你应该依赖这个方法,因为它会将请求的负载复制到上下文中。由于这个原因,后续的调用将再次使用该请求体流进行绑定。
请注意,如果你只需要读取一次请求体,出于性能原因,你应该依赖
ShouldBindWith
方法。
如果这个例子解决了你的疑问,请告诉我,谢谢!
英文:
As correctly pointed out by @Zeke Lu, you should use the method ShouldBindBodyWith
on the gin.Context
struct. I've written a small example for you to use.
package main
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
)
type Todo struct {
Description string `json:"description" binding:"required"`
}
func Todos(c *gin.Context) {
// read the body the first time
var todo Todo
if err := c.ShouldBindBodyWith(&todo, binding.JSON); err != nil {
c.JSON(http.StatusBadRequest, err)
return
}
fmt.Println("first time:", todo)
// read the body again
var todoSecondTime Todo
if err := c.ShouldBindBodyWith(&todoSecondTime, binding.JSON); err != nil {
c.JSON(http.StatusBadRequest, err)
return
}
fmt.Println("second time:", todoSecondTime)
}
func main() {
gin.SetMode(gin.DebugMode)
r := gin.Default()
r.POST("/todos", Todos)
r.Run(":8080")
}
The code is pretty simple but it's purpose is to just provide a valuable example. If you're going to read the body more than once, like in your scenario, you should rely on this method as it does a copy of the request payload into the context. Thanks to this, the subsequent calls will use that body stream for binding it again.
> Please note that, if you're going to read the body only once, you should rely on the ShouldBindWith
method due to performance reasons.
Let me know if this example clarifies your doubts, thanks!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论