在处理请求前后读取request.body和response.writer

前言

有时需要在处理请求前后,分别读取request.bodyresponse.writer,在比如记录日志或其他场景使用,这里记录一下在使用gin时,如何获取且不影响正常请求

示例程序

这里是使用ginweb框架构建一个项目。拥有一个POST方法,在DoPost业务方法前有一个middleware方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

func main() {
	r := gin.Default()
	r.POST("/test", MiddleFunc, DoPost, After)
	r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}

func MiddleFunc(c *gin.Context) {
	c.Next()
}

func DoPost(c *gin.Context) {
	body, err := ioutil.ReadAll(c.Request.Body)
	if err != nil {
		log.Fatalf("read body failed at Before,err:%s", err.Error())
	}
	var req Body
	if err = json.Unmarshal(body, &req); err != nil {
		log.Fatalf("json unmarshal failed, body:%s, err:%s", string(body), err.Error())
	}
	c.JSON(http.StatusOK, fmt.Sprintf("this is response, get body id:%s", req.Id))
}


type Body struct {
	Id string `json:"id"`
}

通过curl请求会得到一个正常的返回

1
2
3
4
5
6
7
curl --location --request POST 'localhost:8080/test' \
--header 'Content-Type: application/json' \
--data-raw '{
    "id":"333"
}'
# 返回
"this is response, get body id:333"

获取request.body

这里正常读取body就可以,需要注意的是需要对request.body重新进行赋值,否则DoPost里会读取不到body的内容

1
2
3
4
5
6
7
body, err := ioutil.ReadAll(c.Request.Body)
if err != nil {
  log.Fatalf("read body failed at Before,err:%s", err.Error())
}
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body))
log.Println("read body: ", string(body))
c.Next()

获取response.writer

这里主要是通过go的interface特性,通过实现http.ResponseWriter对象的方法来替换原类型。 如下代码中,通过Write方法,把写入body中的内容多写一份到buf字段,读取的时候使用r.buf.String()即可

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
type LogResponseWriter struct {
	buf    *bytes.Buffer
	writer http.ResponseWriter
}

func NewLogResponseWriter(r gin.ResponseWriter) *LogResponseWriter {
	return &LogResponseWriter{
		buf:    new(bytes.Buffer),
		writer: r,
	}
}

func (r *LogResponseWriter) Header() http.Header {
	return r.writer.Header()
}

func (r *LogResponseWriter) Write(b []byte) (int, error) {
	r.buf = bytes.NewBuffer(b)
	return r.writer.Write(b)
}

func (r *LogResponseWriter) WriteHeader(statusCode int) {
	r.writer.WriteHeader(statusCode)
}

由于本文中因为使用的是gin框架,所以除了要实现上述对象的方法外,还需要实现gin.ResponseWriter接口。LogResponseWriter结构更改为如下方式

1
2
3
4
type LogResponseWriter struct {
	buf    *bytes.Buffer
	writer gin.ResponseWriter
}

MiddleFunc方法添加如下内容

1
2
3
4
5
6
7
8
9
// 初始化对象
r := NewLogResponseWriter(c.Writer)

// 替换原来的`*gin.Context`对象中`writer`对象
c.Writer = r
// 继续进行业务处理
c.Next()
// 打印response的body信息
log.Println("response body:", r.buf.String())

完整代码

完整代码