Golang gin框架 获取响应体

203 阅读2分钟

场景

假设我们有一个GET接口/data,它返回一大堆数据,它运行的非常好

不知道为什么,项目经理突然发癫,让你把这个接口的响应数据输出到某个地方

(我才不会说是因为我想不出场景)

如果只修改这一个接口还好,在原来的接口加一个日志输出即可

就当你哼着小曲准备改代码的时候,项目经理再次发癫,要你把所有的响应数据都输出到日志

你无语了,如果还按照上面的方法,那就是每个接口都要加一个日志输出

如果有这么一个方法,可以在中间件里处理响应,那该多好啊

于是便有了下面的内容


方案

在使用gin框架开发web的时候,有时会需要对接口返回的数据进行处理,但是gin框架并没有提供相关的方法

我们可以手动拓展,来实现这一功能


代码

整体的逻辑大概如下

r.Use(func(c *gin.Context) {
        // 一些中间件操作
        lulululalala()

        // 让处理函数执行
        c.Next()

        // 处理响应
        log.Println(c.Writer.body)
})

我们需要定义一个新的Writer,来取代原先的Writer

type MyResponseWriter struct {
	gin.ResponseWriter
	body *bytes.Buffer
}

这里的gin.ResponseWriter就是gin.Context原先的Writer,通过组合它,可以让它在gin框架下随意驰骋

body是一个字节池,可以用于存放字节数据

光有结构还不够,我们还需要实现一个方法

func (rw *MyResponseWriter) Write(data []byte) (int, error) {
	rw.body.Write(data)
	return rw.ResponseWriter.Write(data)
}

通过改写Write方法,让我们可以把响应写到我们自定义的body中

然后,我们在中间件里,把gin.Context改造一下

    r.Use(func(c *gin.Context) {
            myWriter := &MyResponseWriter{
                    ResponseWriter: c.Writer,
                    body:           bytes.NewBufferString(""),
            }
            c.Writer = myWriter // 替换默认的 ResponseWriter

            c.Next()
    })

如此一来,这个api在调用Writer时,用的就是我们定义的Writer了


完整示例

package main

import (
	"bytes"
	"log"
	"net/http"

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

type MyResponseWriter struct {
	gin.ResponseWriter
	body *bytes.Buffer
}

func (rw *MyResponseWriter) Write(data []byte) (int, error) {
	rw.body.Write(data)
	return rw.ResponseWriter.Write(data)
}

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

	r.Use(func(c *gin.Context) {
		myWriter := &MyResponseWriter{
			ResponseWriter: c.Writer,
			body:           bytes.NewBufferString(""),
		}
		c.Writer = myWriter

		c.Next()

		log.Println(myWriter.body.String())
	})

	r.GET("/ping", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"code": 200,
			"data": nil,
			"msg":  "pong",
		})
	})

	r.Run(":8080")
}

运行后,请求/ping接口

krean@Kreans-Air test % curl localhost:8080/ping
{"code":200,"data":null,"msg":"pong"}%  

在终端应该能看到响应输出

2024/11/27 21:48:29 {"code":200,"data":null,"msg":"pong"}
[GIN] 2024/11/27 - 21:48:29 | 200 |     259.542µs |             ::1 | GET      "/ping"

结语

实际上,这类方法用的是不多的,也是为什么gin框架(以及大部分框架不封装这部分功能),总有更好的方法来处理这类问题

所以,这算是作为应对特别情况的紧急方案吧

Author:KreanXie