I want to add a uniform response header to all the processing functions, but it does not work

As shown in the figure below, I want to add a custom header "X-TIME" after the processing is completed, but it does not work.

package main

import "github.com/gin-gonic/gin"
func main() {
    r:=gin.Default()
    g:=r.Group("/res", func(context *gin.Context) {
        context.Next()

        //I want to add a unified response header here,
        //but it is invalid.
        context.Header("X-TIME","time")
    })
    g.GET("/", func(context *gin.Context) {
        //it is valid.
        context.Header("X-KEY","key")
        context.JSON(200,gin.H{
            "key":"value",
        })
    })
    r.Run(":18080")
}

Comment From: binbin0325

gin: v1.4.0 golang: 1.14.2 @QueryHeaven I tested it and it seemed to work well.

 func SetupRouter() *gin.Engine {
    router := gin.Default()
    v1 := router.Group("/v1", func(c *gin.Context) {
        c.Next()
        c.Header("X-TIME", "time")
    })
    {
        v1.Any("/log", services.Get)
    }
    return router
 }

Gin  How to add a unified response header after the processing function

Comment From: QueryHeaven

go 1.12 gin v1.6.3 @sanxun0325 I don't know how it is written in your services.Get, I tested again and found the following: gin

Comment From: joaohaas

Kind of necroing, but since this is still open, the reason headers don't get written is due to the way Go internals handles response buffering. As soon as a status header (aka status code) is written, the response starts getting written to a buffer, and extra headers cannot be written. In most cases, you can just add the header before the c.Next() call and it will work. If you need to write the header after the status code is written, you'll need to hijack the writer and override the WriteHeader function:

type afterMiddlewareWriter struct {
    gin.ResponseWriter
}

func (w *afterMiddlewareWriter) WriteHeader(statusCode int) {
    w.Header().Add("X-TIME", "time")
    w.ResponseWriter.WriteHeader(statusCode)
}

func AfterMiddleware(c *gin.Context) {
    c.Writer = &afterMiddlewareWriter{c.Writer}
    c.Next()
}

func main() {
    r := gin.Default()
    r.Use(AfterMiddleware)
    r.GET("/", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "key": "value",
        })
    })
}

For a real X-Time implementation, you can store the initial time on your struct and calculate how many seconds have passed in the hijacked WriteHeader.

Comment From: coanor

For my case, it's simple to add customize header in response:

router := gin.New()
router.Use(func(c *gin.Context) {
  c.Header("X-Your-Header-Name", "your-header-value")
})

// some other middleware...

// add router
router.GET("/some/api", apiHandler)
... // more

Comment From: maxant

here is a more complete version of @joaohaas 's suggestion:

type timingMiddlewareWriter struct {
    gin.ResponseWriter
    start time.Time
}

func (w *timingMiddlewareWriter) WriteHeader(statusCode int) {
    elapsed := time.Since(w.start)
    log.Debug().Msg("timer ended after writing statusCode: " + elapsed.String())
    w.Header().Add("x-time", elapsed.String())
    w.ResponseWriter.WriteHeader(statusCode)
}

func timingMiddleware(c *gin.Context) {
    log.Debug().Msg("timer starting...")

    c.Writer = &timingMiddlewareWriter{ c.Writer, time.Now()}
    c.Next()
    log.Debug().Msg("timer ended after next()")
}