如何使用Context.Request.Body并保留它?

huangapple go评论85阅读模式
英文:

How to use Context.Request.Body and retain it?

问题

我正在尝试编写一个中间件,在其中对请求体进行 JSON Schema 验证。验证完成后,我需要再次使用请求体。但是我无法弄清楚如何实现这一点。我参考了这篇帖子,找到了一种访问请求体的方法。但是一旦请求体被使用,我需要在下一个函数中仍然可以访问它。

以下是示例代码:

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/gin-gonic/gin"
	// "github.com/xeipuuv/gojsonschema"
)

func middleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 在这里进行 JSON Schema 验证

		body := c.Request.Body
		x, _ := ioutil.ReadAll(body)

		fmt.Printf("%s \n", string(x))

		fmt.Println("我是用于 JSON Schema 验证的中间件")

		c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(x)) // 将请求体重新放回上下文中

		c.Next()
		return
	}
}

type E struct {
	Email    string
	Password string
}

func test(c *gin.Context) {
	// data := &E{}
	// c.Bind(data)
	// fmt.Println(data)   // 打印为空,因为 JSON 请求体已经被使用过了

	body := c.Request.Body
	x, _ := ioutil.ReadAll(body)

	fmt.Printf("请求体为: %s \n", string(x))
	c.JSON(http.StatusOK, c)
}

func main() {
	router := gin.Default()

	router.Use(middleware())

	router.POST("/test", test)

	// 监听并服务
	router.Run("127.0.0.1:8080")
}

当前输出:

{
    "email": "test@test.com",
    "password": "123"
} 

我是用于 JSON Schema 验证的中间件
请求体为: 

期望输出:

{
    "email": "test@test.com",
    "password": "123"
} 
我是用于 JSON Schema 验证的中间件
请求体为: {
    "email": "test@test.com",
    "password": "123"
}

希望这可以帮助到你!

英文:

I am trying to write a middleware where I will be doing a json schema validation against the request body. After the validation, I need to use the request body again. But I am not able to figure out how this can be done. I referred this post
and found a way to access the body. But once the request body is used, I need that available to my next function.

Here is the sample code:

package main
import (
        "fmt"
    	"io/ioutil"
    	"net/http"
    	"github.com/gin-gonic/gin"
        //"github.com/xeipuuv/gojsonschema"
)

func middleware() gin.HandlerFunc {
 return func(c *gin.Context) {
	//Will be doing json schema validation here

	body := c.Request.Body
	x, _ := ioutil.ReadAll(body)

	fmt.Printf("%s \n", string(x))

	fmt.Println("I am a middleware for json schema validation")

	c.Next()
	return
 }
}    

type E struct {
 Email    string
 Password string
}

func test(c *gin.Context) {
 //data := &E{}
 //c.Bind(data)
 //fmt.Println(data)   //prints empty as json body is already used
 
 body := c.Request.Body
 x, _ := ioutil.ReadAll(body)

 fmt.Printf("body is: %s \n", string(x))
 c.JSON(http.StatusOK, c)
}

func main() {
 router := gin.Default()

 router.Use(middleware())

 router.POST("/test", test)

 //Listen and serve
 router.Run("127.0.0.1:8080")
}

Current output:

{
    "email": "test@test.com",
    "password": "123"
} 

I am a middleware for json schema validation
body is: 

Expected output:

{
    "email": "test@test.com",
    "password": "123"
} 
I am a middleware for json schema validation
body is: {
    "email": "test@test.com",
    "password": "123"
}

答案1

得分: 7

Thellimist所说的,但用更多的话来说。

你需要“捕获和恢复”Body。
Body是一个缓冲区,这意味着一旦你读取它,它就会消失。所以,如果你捕获它并“放回去”,你就可以再次访问它。

查看这个答案,我认为这就是你要找的:
https://stackoverflow.com/a/47295689/3521313

英文:

What Thellimist said, but in more words.

You need to "catch and restore" the Body.
The Body is a buffer, that means that once you read it, its gone. So, if you catch it and "put it back", you can access it again.

Check this answer, I think this is what you are looking for:
https://stackoverflow.com/a/47295689/3521313

答案2

得分: 0

你可以在中间件中复制req.Body。可以使用io.TeeReader和bytes.Buffer来实现。根据我所知,你不能直接复制io.Reader,所以你必须在读取时进行复制,然后将复制后的内容赋值给c.Request.Body,以便在c.Bind中使用。

我不确定,但也许这个链接可以让事情变得更容易。

英文:

You could copy req.Body inside your middleware. Check out io.TeeReader + bytes.Buffer.

As far as I know you can't directly copy an io.Reader so you must copy it while you read it then assign the copied one back to c.Request.Body to be able to use it for c.Bind

I'm not sure but maybe this can make things easier.

答案3

得分: 0

如果你想多次使用请求体内容,并且你也在使用gin-gonic,我认为ShouldBindBodyWith函数是你要找的。

ShouldBindBodyWithShouldBindWith类似,但它将请求体存储到上下文中,并在再次调用时重用。

注意:该方法在绑定之前读取请求体。因此,如果你只需要调用一次,建议使用ShouldBindWith以获得更好的性能。

参考资料:

英文:

If you want to use the body content multiple times and you're also using gin-gonic, I think the ShouldBindBodyWith function is what you're looking for.

> ShouldBindBodyWith is similar with ShouldBindWith, but it stores the request body into the context, and reuse when it is called again.
>
>NOTE: This method reads the body before binding. So you should use ShouldBindWith for better performance if you need to call only once.

References:

huangapple
  • 本文由 发表于 2015年8月14日 19:11:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/32008680.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定