Golang中的服务器头中间件

I'm trying to set Server Header on every Response. I'm trying to achieve this, using a Middleware for Gin. However, this does not set the Header, for some reason. I've tried to debug this so far, and I could not understand why this should not work. Probably I'm missing something here.

Here is the code

package main

import "fmt"
import "github.com/gin-gonic/gin"

const SERVER_INFO = "Some-Play-Server"

type ServerHeader struct {
    gin.ResponseWriter
    ServerInfo string
}

func (w *ServerHeader) Write(data []byte) (int, error) {
    if w.Header().Get("Server") == "" {
        w.Header().Add("Server", w.ServerInfo)
    }

    return w.ResponseWriter.Write(data)
}

func InitServerHeader() gin.HandlerFunc {
    return func(c *gin.Context) {
        writer := &ServerHeader{c.Writer, SERVER_INFO}
        c.Writer = writer
        c.Next()
    }
}

func main() {
    mux := gin.Default()
    mux.Use(InitServerHeader())
    mux.GET("/", func(c *gin.Context) {
        c.String(200, "OK")
    })

    fmt.Println("Server Listening on 0.0.0.0:8080")
    mux.Run(":8080")
}

And, here is the Test Output

❯ curl -v http://localhost:8080/
* About to connect() to localhost port 8080 (#0)
* Connected to localhost (::1) port 8080 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:8080
> Accept: */*
> 
< HTTP/1.1 200 OK
< Content-Type: text/plain
< Date: Wed, 13 Aug 2014 16:54:21 GMT
< Content-Length: 2
< 
OK

You did not use the right method for that ..

package main

import "fmt"
import "github.com/gin-gonic/gin"

const SERVER_INFO = "Some-Play-Server"

type ServerHeader struct {
    gin.ResponseWriter
    ServerInfo string
}

func (w *ServerHeader) WriteHeader(code int) {
    if w.Header().Get("Server") == "" {
        w.Header().Add("Server", w.ServerInfo)
    }

    w.ResponseWriter.WriteHeader(code)
}

func InitServerHeader() gin.HandlerFunc {
    return func(c *gin.Context) {
        writer := &ServerHeader{c.Writer, SERVER_INFO}
        c.Writer = writer
        c.Next()
    }
}

func main() {
    mux := gin.Default()
    mux.Use(InitServerHeader())
    mux.GET("/", func(c *gin.Context) {
        c.String(200, "OK")
    })

    fmt.Println("Server Listening on 0.0.0.0:8080")
    mux.Run(":8080")
}

Here is the Output

$ curl -v 'http://localhost:8080/'
* About to connect() to localhost port 8080 (#0)
* Connected to localhost (::1) port 8080 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:8080
> Accept: */*
> 
< HTTP/1.1 200 OK
< Content-Type: text/plain
< Server: Some-Play-Server
< Date: Thu, 14 Aug 2014 00:41:27 GMT
< Content-Length: 2
< 
OK

I'm not familiar with gin, however using the built in http server it's rather trivial to do that:

const SERVER_INFO = "Some-Play-Server"
var extra = map[string]string{
    "Server": SERVER_INFO,
}

func HeaderSetter(fn func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
    return func(rw http.ResponseWriter, req *http.Request) {
        for k, v := range extra {
            rw.Header().Set(k, v)
        }
        fn(rw, req)
    }
}

func main() {
    fn := func(rw http.ResponseWriter, req *http.Request) {
        io.WriteString(rw, "Hello: "+req.URL.String()+"
")
    }
    http.HandleFunc("/", HeaderSetter(fn))
    log.Panic(http.ListenAndServe(":9020", nil))
}

A different approach was mentioned by @elithrar in the comments is to return http.Handler instead:

func HeaderSetter(fn http.Handler) http.Handler {
    return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
        for k, v := range extra {
            rw.Header().Set(k, v)
        }
        fn(rw, req)
    })
}

playground

you do not have to do that. You just have to do this:

func main() {
    mux := gin.Default()
    mux.Use(func(c *gin.Context) {
        c.Writer.Header().Set("Server", "Some-Play-Server")
    })

    mux.GET("/", func(c *gin.Context) {
        c.String(200, "OK")
    })    
    mux.Run(":8080")
}

Also, check out the last version (develop branch), we improved it a lot. Now gin automatically defers the call to WriteHeader() so you do not have to worry.

here Gin's creator: DO NOT override the c.Writer, that's too much overhead and uneeded complexity. The writer should be overrided if you want to support transparent gzip compression or caching (see gzip middleware). If you want to add a header just do this:

func main() {
    mux := gin.Default()
    mux.Use(serverHeader)
    mux.GET("/", func(c *gin.Context) {
        c.String(200, "OK")
    })    
    mux.Run(":8080")
}

func serverHeader(c *gin.Context) {
    // shortcut for c.Writer.Header().Set("Server", "Some-Play-Server")
    c.Header("Server", "Some-Play-Server")
}

Done! since it is attached as a global middleware, all the responses will include the Server header, even when serving static files!