I want to rewrite response body in middleware, but I found it is difficult, Beacuse I must rewrite all of the method of the interface "ResponseWrite", have any simple method to rewrite response body?

Comment From: 0x2d3c

are you want to resize response data? if that, this code may help you


type copyWriter struct {
    gin.ResponseWriter
    buf *bytes.Buffer
}

func (cw copyWriter) Write(b []byte) (int, error) {
    return cw.buf.Write(b)
}

func main() {
    engine := gin.New()

    engine.Use(func(ctx *gin.Context) {
        cw := &copyWriter{buf: &bytes.Buffer{}, ResponseWriter: ctx.Writer}
        ctx.Writer = cw

        ctx.Next()

        // read data in buf
        // do something for data
        // write to ResponseWriter
    })

    engine.GET("hi", func(ctx *gin.Context) {
        ctx.JSON(http.StatusOK, gin.H{"msg": "hello"})
    })

    engine.Run()
}

Comment From: seifchen

Yes, Thanks

Comment From: nishyt

@seifchen @0x2d3c can you please explain how to write the modified response back to the client (browser / curl) For example : How would I update the JSON from{"msg": "hello"} to {"msg": "nice"} and get the new response when I run the command : curl -vk http://localhost:8080/hi

Comment From: seifchen

@nishyt there is the test code type BodyWriter struct { gin.ResponseWriter body *bytes.Buffer }

in middleware to rewrite the body: `

fmt.Printf("PostHandler login_name:%s\n", c.Request.Header.Get("staffname"))
    var wb *BodyWriter // define a new writer
    if w, ok := c.Writer.(gin.ResponseWriter); ok {
        fmt.Println("ok")
        wb = NewBodyWriter(w)
        c.Writer = wb
        c.Next()
    } else {
        c.Next()
        return
    }
    status := wb.Status()
            // read the response
    data := wb.body.String()
            // rewrite the response
    wb.ResponseWriter.WriteString(`{"seifchen":"success"}`)
    wb.body.Reset()
    data2 := wb.body.String()
    fmt.Printf("PostHandler stats:%v, data:%s,data2:%s", status, data, data2)

`

Comment From: nishyt

@seifchen . Thanks a lot for your help . I tried implementing the above code but to make it work I had to rewrite the all methods of interface "ResponseWriter". Is there any better way to implement the same. Can you point me to complete code (if any) for this

Comment From: seifchen

@nishyt define the struct ,had not to rewrite all methods of interface "ResponseWriter"

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

Comment From: nishyt

@seifchen . Here's the program I wrote as per you recommendation . Here I want to modify the JSON {"name": "OLD_NAME", "work": "OLD_WORK"} to {"name": "NEW_NAME", "work": "NEW_WORK"}.

package main

import (
    "bytes"
    "encoding/json"
    "net/http"

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

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

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

    r.Use(TestMiddleware())

    r.GET("/test", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "name": "OLD_NAME",
            "work": "OLD_WORK",
        })
    })

    r.Run(":8080")
}

func TestMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {

        wb := &BodyWriter{
            body:           &bytes.Buffer{},
            ResponseWriter: c.Writer,
        }
        c.Writer = wb

        c.Next()

        data := wb.body.String()

        obj := make(map[string]interface{})

        json.Unmarshal([]byte(data), &obj)

        obj["name"] = "NEW_NAME"
        obj["work"] = "NEW_WORK"

        updatedBody, _ := json.Marshal(obj)

        wb.ResponseWriter.WriteString(string(updatedBody))
        wb.body.Reset()
    }
}

But when I run the program (go run main.go) and in the other terminal execute the command :curl -vk http://localhost:8080/test , I get the following response body {"name":"OLD_NAME","work":"OLD_WORK"}{"name":"NEW_NAME","work":"NEW_WORK"}. It's appending the updated response on to the previous response but I only want the updated response

Comment From: seifchen

@0x2d3c you should rewrite Write method like this

func (w BodyWriter) Write(b []byte) (int, error) {
    return w.body.Write(b)
}

Comment From: nishyt

@seifchen Thanks a lot for your help. It worked :)

Comment From: kuustudio

@seifchen According to your proposal, when I want to response []byte type content, the result cannot be output. I tried c.Data(http.StatusOK, "text/plain", []byte("Test")) and wb.Write([]byte("Test")).

Comment From: seifchen

@kuustudio you should use wb.ResponseWriter.Write([]byte("Test"))

Comment From: kuustudio

@seifchen

Thanks a lot for your help. It worked :)

Comment From: manuelarte

I am trying with the following code and I can't make it to rewrite the response:


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

func(c *gin.Context) {
    wb := &bodyWriter{
        body:           &bytes.Buffer{},
        ResponseWriter: c.Writer,
    }
    c.Writer = wb
    c.Next()
    data := wb.body.String()
    wb.ResponseWriter.WriteString(`{"seifchen":"success"}`)
    wb.body.Reset()
    data2 := wb.body.String()

    fmt.Printf("PostHandler data:%s,data2:%s", data, data2)
}

I get an empty string in data2, any idea what am I doing wrong?