从外部Go中间件控制HTTP标头 [英] Control HTTP headers from outer Go middleware

查看:68
本文介绍了从外部Go中间件控制HTTP标头的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我在Go中有一个中间件,我想用我自己的值覆盖任何现有的Server标头.

Let's say I have a middleware in Go where I want to override any existing Server headers with my own value.

// Server attaches a Server header to the response.
func Server(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)
    })
}

然后我将其添加到这样的响应链中

Then I add it to a response chain like this

http.Handle("/", authHandler)
http.ListenAndServe(":8000", Server(http.DefaultServeMux, "myapp"))

不幸的是,如果authHandler或由DefaultServeMux处理的任何事物调用w.Header().Add("Server", "foo")(例如

Unfortunately, if the authHandler or anything handled by DefaultServeMux calls w.Header().Add("Server", "foo") (like, say, httputil.ReverseProxy.ServeHTTP), I end up with two Server headers in the response.

$ http localhost:5962/v1/hello_world
HTTP/1.1 200 OK
Content-Length: 11
Content-Type: text/plain
Date: Tue, 12 Jul 2016 04:54:04 GMT
Server: inner-middleware
Server: myapp

我真正想要的是这样的东西:

What I really want is something like this:

// Server attaches a Server header to the response.
func Server(h http.Handler, serverName string) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        h.ServeHTTP(w, r)
        w.Header().Set("Server", serverName)
    })
}

但是,这是 ServeHTTP的语义所不允许的:

ServeHTTP应该将回复标头和数据写入ResponseWriter,然后返回.返回信号表明请求已完成;在ServeHTTP调用完成之后或与之同时使用ResponseWriter或从Request.Body中读取是无效的.

ServeHTTP should write reply headers and data to the ResponseWriter and then return. Returning signals that the request is finished; it is not valid to use the ResponseWriter or read from the Request.Body after or concurrently with the completion of the ServeHTTP call.

我已经看到一些与此相关的丑陋骇客,例如

I have seen some fairly ugly hacks around this, for example the code in httputil.ReverseProxy.ServeHTTP, which copies headers and rewrites the response code, or using httptest.ResponseRecorder, which reads the entire body into a byte buffer.

我还可以颠倒传统的中间件顺序,并以某种方式将我的Server中间件放在最后,或者使其成为最内部的中间件.

I could also reverse the traditional middleware order and put my Server middleware last, somehow, or make it the inner-most middleware.

我缺少任何简单的方法吗?

Is there any simple method I'm missing?

推荐答案

您可以定义一个自定义类型,该类型将包装ResponseWriter并在写入所有标头之前插入Server标头,但要付出额外的间接层费用.这是一个示例:

You can define a custom type which wraps a ResponseWriter and inserts the Server header just before all headers are written, at the cost of an additional layer of indirection. Here is an example:

type serverWriter struct {
    w           http.ResponseWriter
    name        string
    wroteHeader bool
}

func (s serverWriter) WriteHeader(code int) {
    if s.wroteHeader == false {
        s.w.Header().Set("Server", s.name)
        s.wroteHeader = true
    }
    s.w.WriteHeader(code)
}

func (s serverWriter) Write(b []byte) (int, error) {
    return s.w.Write(b)
}

func (s serverWriter) Header() http.Header {
    return s.w.Header()
}

// Server attaches a Server header to the response.
func Server(h http.Handler, serverName string) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        sw := serverWriter{
            w:           w,
            name:        serverName,
            wroteHeader: false,
        }
        h.ServeHTTP(sw, r)
    })
}

我在这里写了更多有关此的内容. https://kev.inburke.com/kevin/how-to -write-go-middleware/

I wrote more about this here. https://kev.inburke.com/kevin/how-to-write-go-middleware/

这篇关于从外部Go中间件控制HTTP标头的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆