golang中的包装(代理)

时间:2017-06-14 13:53:11

标签: go wrapper proxy-classes

当我阅读文章https://upgear.io/blog/golang-tip-wrapping-http-response-writer-for-middleware/?utm_source=golangweekly&utm_medium=email时,我意识到很容易制作一个包装器(设计模式代理),它包装了一些方法。

情况有点复杂,当你不想要松开界面时,包裹的对象有它。

在示例中,我已经编写了如何选择性地实现http.Flusher的方法。但是如何解决文章中的情况,当w可以实现3个接口中的一些时(http.Flusher,http.Hijacker,http.Pusher)。这是更好的解决方案,写出8种不同的类型,其中每种都实现了之前的组合?

// add type, that do what statusRecorder, but keeps ability to be Flusher
type statusRecordFlusher statusRecorder

func (w *statusRecordFlusher) Flush() {
    w.ResponseWriter.(http.Flusher).Flush()
}

// and decision, how to wrap
func logware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Initialize the status to 200 in case WriteHeader is not called
        var rec http.ResponseWriter
        if _, ok := w.(http.Flusher); ok {
            rec = &statusRecordFlusher{w, 200}
        } else {
            rec = &statusRecorder{w, 200}
        }

        next.ServeHTTP(rec, r)
    })
}

1 个答案:

答案 0 :(得分:0)

您正在嵌入ResponseWriter,并且在不添加任何行为的情况下隐藏Flush;删除阴影方法。您仍然需要进行某种类型的争论,但除非您尝试添加或更改行为(大概只是WriteHeader,否则您不需要执行任何方法实施)。

因为您只是尝试在此处公开嵌入类型的方法,所以您甚至不需要定义所有结构,您可以使用匿名结构(playground example here):

type statusRecorder struct {
    // Not sure what all is in here but let's assume at least:
    http.ResponseWriter
    status int
}

func logware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Per the docs:
        // The default ResponseWriter for HTTP/1.x connections supports Hijacker, but HTTP/2 connections intentionally do not.
        // Pusher is the interface implemented by ResponseWriters that support HTTP/2 server push.
        // Therefor w will only ever be a Hijacker or a Pusher, never both.

        sr := &statusRecorder{w, 200}

        if h, ok := w.(http.Hijacker); ok {
            if f, ok := w.(http.Flusher); ok {
                w = &struct {
                    http.ResponseWriter
                    http.Hijacker
                    http.Flusher
                }{sr, h, f}
            } else {
                w = &struct {
                    http.ResponseWriter
                    http.Hijacker
                }{sr, h}
            }
        } else if p, ok := w.(http.Pusher); ok {
            if f, ok := w.(http.Flusher); ok {
                w = &struct {
                    http.ResponseWriter
                    http.Pusher
                    http.Flusher
                }{sr, p, f}
            } else {
                w = &struct {
                    *statusRecorder
                    http.Pusher
                }{sr, p}
            }
        } else {
            w = sr
        }

        next.ServeHTTP(w, r)
    })
}