It would be very nice to have this. Lets say i want to have sessions and i need the header to be written before the response. Currently there i no way to do that. Martni framework has it solved with Before function on custom ResponseWriter https://github.com/go-martini/martini/blob/master/response_writer.go#L13

Comment From: jasonrhansen

I would like this same feature. It should be simple to implement.

Comment From: jasonrhansen

I opened a pull request to add this. https://github.com/gin-gonic/gin/pull/89

Comment From: AlexanderChen1989

That's what i wanted!

Comment From: bluele

:+1:

Comment From: manucorporat

hi guys! I am back. we may need to fix this issue in a different way. I meant, Before() is a way to had around how the net library works, I do not think the API should be designed in that way. The APIs should not be implementation dependent.

The developer should not know that they have to use Before()... I propose to defer the call to write.WriteHeader() just before we write to the socket.

So for example: c.WriteHeader(404) --> caches 404 c.WriteHeader(200) --> caches 200

c.Write(stuff) ---> write headers & write stuff c.Write(moreStuff) --> write more stuff

If c.Write() is never called in the "user-land", internally Gin would write the headers manually.

Comment From: manucorporat

Check this out: https://github.com/gin-gonic/gin/commit/dcafad3ced1d87333a7abdaecafc984813df5392

Comment From: jasonrhansen

I agree that this is a better solution. I was just doing what martini does without exploring other approaches.

Comment From: manucorporat

I have been testing this so far and in fact it fixes several bugs in our backend, sounds like a good fix. Is everybody ok? are there any use case where Before() could be needed?

Comment From: dfilipovic39

Agree that it should be transparent and this sounds as a good solution so far

Comment From: themartorana

Would we also be able to defer the write in Abort? I ran into a case where I was calling abort, but in a clean-up middleware was capturing and transforming the error and calling c.JSON(200,...) - that ends up calling WriteHeader twice - first time in Abort. If it also deferred until all code had run, I could transform it in middleware.

Comment From: manucorporat

Yes, the current version also defers Abort() so, if you call, c.Abort(400), and then you can c.JSON(200, info). The final response would be 200.

And WriteHeader() is called once! does it work for you?

I will add a new function to Context, so you can know if Abort() was already called. Context.Aborted() bool

Comment From: themartorana

Fantastic! Many thanks.

Comment From: montanaflynn

@manucorporat Can you share an example? After reading the thread I still don't understand how to implement the logic for middleware that would add a header as soon as it gets the request and also right before sending the response (for logging performance from the client). Is this possible?

Here's what I have:

func latencyHeaders() gin.HandlerFunc {
    return func(c *gin.Context) {
        now := time.Now()
        timestamp := strconv.FormatInt(now.UnixNano() / 1000000, 10)
        c.Writer.Header().Set("x-request-received", timestamp)
        c.Next()
    }
}

Comment From: javierprovecho

@montanaflynn here you have it:

package main

import (
    "net/http"
    "time"
    "strconv"
    "github.com/gin-gonic/gin"
)

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

    latencyHandler := func(c *gin.Context) {
            now := time.Now()
            timestamp := strconv.FormatInt(now.UnixNano() / 1000000, 10)
            c.Writer.Header().Set("x-request-received", timestamp)
            c.Next()
        }

    // Logging middleware
    g.Use(gin.Logger())

    // Enable the custom latency middleware, must come before recovery
    g.Use(latencyHandler)

    // Recovery middleware
    g.Use(gin.Recovery())


    g.GET("/", func(c *gin.Context) {
        c.String(http.StatusOK, "Hello World!")
    })

    // Serve
    g.Run(":3000")
}

Comment From: montanaflynn

@javierprovecho thanks for taking the time to help me out although I don't think I was clear enough in my request. What I would like is to have my middleware add the x-request-received header as soon as it gets the request but also add a second header x-response-sent right before sending the response.

Here's an example that works but I'd like to add the x-response-sent header in the middleware.

package main

import (
    "net/http"
    "time"
    "strconv"
    "github.com/gin-gonic/gin"
)

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

    latencyHandler := func(c *gin.Context) {
        now := time.Now()
        timestamp := strconv.FormatInt(now.UnixNano() / 1000000, 10)
        c.Writer.Header().Set("x-request-received", timestamp)
        c.Next()
    }

    // Enable the custom latency middleware, must come before recovery
    g.Use(latencyHandler)

    // Logging middleware
    g.Use(gin.Logger())

    // Recovery middleware
    g.Use(gin.Recovery())

    g.GET("/", func(c *gin.Context) {

        // Simulate processing time
        time.Sleep(5 * time.Second)

        now := time.Now()
        timestamp := strconv.FormatInt(now.UnixNano() / 1000000, 10)
        c.Writer.Header().Set("x-response-sent", timestamp)
        c.String(http.StatusOK, "Hello World!")
    })

    // Serve
    g.Run(":3000")
}

Comment From: javierprovecho

Well, due to the nature of Gin and its buffer-less response the only way to do it is to call a second function passing the context as argument and doing the same as the first function, latencyHandler.

    g.GET("/", func(c *gin.Context) {
        // Simulate processing time
        time.Sleep(5 * time.Second)
        latencyHandler2(c)
        c.String(http.StatusOK, "Hello World!")
    })

    latencyHandler2 := func(c *gin.Context) {
        now := time.Now()
        timestamp := strconv.FormatInt(now.UnixNano() / 1000000, 10)
        c.Writer.Header().Set("x-response-sent", timestamp)
    }

As said you can't create a middleware that attach a new header after sending body.

Comment From: montanaflynn

Bummer, I think this hits on when @manucorporat asked if there was any need for a Before() method.

In other languages these are often referred to as "hooks". So you could have your middleware hook into the final Write for instance and only execute the given function at that time. I have no idea how this would look in Go or if suitable for Gin.

@javierprovecho thanks for your help

Comment From: dragonly

Hi guys, after all those years, I bump into the same problem here! any cleaner solutions with middleware to write the x-response-sent header?

Comment From: luza

Hey everyone. Here is how I eventually solved the problem: https://gist.github.com/luza/dca5936637f525848d67e49e21820f93

Hope it will help.