Giter VIP home page Giter VIP logo

Comments (19)

dragonly avatar dragonly commented on March 29, 2024 1

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?

from gin.

jasonrhansen avatar jasonrhansen commented on March 29, 2024

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

from gin.

jasonrhansen avatar jasonrhansen commented on March 29, 2024

I opened a pull request to add this.
#89

from gin.

AlexanderChen1989 avatar AlexanderChen1989 commented on March 29, 2024

That's what i wanted!

from gin.

bluele avatar bluele commented on March 29, 2024

👍

from gin.

manucorporat avatar manucorporat commented on March 29, 2024

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.

from gin.

manucorporat avatar manucorporat commented on March 29, 2024

Check this out: dcafad3

from gin.

jasonrhansen avatar jasonrhansen commented on March 29, 2024

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

from gin.

manucorporat avatar manucorporat commented on March 29, 2024

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?

from gin.

dfilipovic39 avatar dfilipovic39 commented on March 29, 2024

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

from gin.

themartorana avatar themartorana commented on March 29, 2024

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.

from gin.

manucorporat avatar manucorporat commented on March 29, 2024

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

from gin.

themartorana avatar themartorana commented on March 29, 2024

Fantastic! Many thanks.

from gin.

montanaflynn avatar montanaflynn commented on March 29, 2024

@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()
    }
}

from gin.

javierprovecho avatar javierprovecho commented on March 29, 2024

@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")
}

from gin.

montanaflynn avatar montanaflynn commented on March 29, 2024

@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")
}

from gin.

javierprovecho avatar javierprovecho commented on March 29, 2024

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.

from gin.

montanaflynn avatar montanaflynn commented on March 29, 2024

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

from gin.

luza avatar luza commented on March 29, 2024

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

Hope it will help.

from gin.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.