Add response header to every handler without repeating the same line

别等时光非礼了梦想. 提交于 2019-12-05 01:06:49

问题


I am writing a small website and for every page, I am putting a server name to its header:

func httpSignUp(rw http.ResponseWriter, req *http.Request) {
    rw.Header().Set("Server", SERVER_NAME)
}

I am wondering if there's a way that I can set http.ResponseWriter's default server name, so I don't have to use the same line over and over?


回答1:


Create a wrapper to set the header:

func wrap(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) (
         w.Header().Set("Server", SERVER_NAME)
         h.ServeHTTP(w, r)
    })
}

Wrap individual handlers

http.Handle("/path", wrap(aHandler)(
http.Handle("/another/path", wrap(anotherHandler))

or the root handler passed to ListenAndServe:

log.Fatal(http.ListenAndServe(addr, wrap(rootHandler))



回答2:


The http.ResponseWriter is an interface, not a struct. So, You cannot extend it directly. You need to extend the internal struct. But it is not idiomatic way to solve this problem.

One approach you can take is to use middleware. middleware is just a piece of code which will be executed before your main request handler, and can be used perform some common tasks.

For example, to write the same thing using middleware approach:

func injectServerHeader(handler http.Handler, serverName string) http.Handler {
    ourFunc := func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Server", serverName)
        handler.ServeHTTP(w, r)
    }
    return http.HandlerFunc(ourFunc)
}

This way, you wrap your actual http handler with middleware. So, you won't have to write same code over and over again.

For example:

http.Handle("/some-path", injectServerHeader(aHandler))



回答3:


"Prefer composition to inheritance" - Gang of 4

Inheritance simply wasn't designed into Golang in the first place. ;)

If you are looking for detailed explanations on the why part, I believed this has been answered on SO, hence I would just point you to it: Embedding instead of inheritance in Go.

Well, you can actually achieve the same result with adapter design pattern, which enables you to extend functionality from a built-in library, and to me, its way more flexible than inheritance.

func adapter(h http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Server", SERVER_NAME)
    h.ServeHTTP(w, r)
  })
}

Or pass in serverName as parameter:

func adapter(h http.Handler, serverName string) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Server", serverName)
    h.ServeHTTP(w, r)
  })
}

Finally, you have the flexibility to choose which handler(s) to be 'inherited':

http.Handle("/path", adapter(your_handler))

Or if its meant for every handlers, just 'inherit' to the root-handler:

http.ListenAndServe(port, adapter(root_Handler))


来源:https://stackoverflow.com/questions/36490162/add-response-header-to-every-handler-without-repeating-the-same-line

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!