Golang如何嵌入一个带有额外隐藏方法的接口?

huangapple go评论71阅读模式
英文:

Golang embed an interface with additional hidden methods?

问题

我想在一个结构体中嵌入一个http.ResponseWriter。这样做是可以的,它确保我的结构体也实现了http.ResponseWriter接口:

type MyWriter struct {
    BytesWritten int
    http.ResponseWriter
}

然而,尽管通常嵌入的http.ResponseWriter实现了http.Hijackerhttp.CloseNotifierhttp.Flusher接口,但它不再实现这些接口。

有没有办法解决这个问题呢?

英文:

I want to embed a http.ResponseWriter in a struct. That's fine, it makes sure my struct also implements http.ResponseWriter:

type MyWriter struct {
    BytesWritten int
    http.ResponseWriter
}

However, it no longer implements http.Hijacker, http.CloseNotifier or http.Flusher even though the embedded http.ResponseWriter usually does.

Is there some way to do this?

答案1

得分: 2

如果你知道响应写入器满足问题中列出的所有接口,那么你可以这样做:

type allResponseWriterInterfaces struct {
    http.ResponseWriter
    http.Hijacker
    http.CloseNotifier
    http.Flusher
}

type MyWriter struct {
    BytesWritten int
    allResponseWriterInterfaces
}

...

aw, ok := w.(allResponseWriterInterfaces)
if !ok {
    // 哎呀,响应写入器没有实现这些接口
    // 处理错误
}
mw := MyWriter{0, aw}

如果响应写入器不满足所有接口,情况就会变得混乱。参考Gorilla logger中处理响应写入器满足(http.ResponseWriter, http.CloseNotifier)或者(http.ResponseWriter, http.CloseNotifier, http.Hijacker)的示例。

英文:

If you know that the response writer satisfies all of the interfaces listed in the question, then you can do this:

type allResponseWriterInterfaces {
    http.ResponseWriter
    http.Hijacker
    http.CloseNotifier
    http.Flusher
}

type MyWriter struct {
    BytesWritten int
    allResponseWriterInterfaces
}

...

aw, ok := w.(allResponseWriterInterfaces)
if !ok {
    // oops, response writer does not implement the interfaces
    // handle the error
}
mw := MyWriter{0, aw}

It get's messy if the response writer does not satisfy all of the interfaces. See the Gorilla logger for an example of handling the case where the response writer satisfies (http.ResponseWriter, http.CloseNotifier) or (http.ResponseWriter, http.CloseNotifier, http.Hijacker).

答案2

得分: 0

一种解决方案是暴力解法,实际上实现所有的排列。我尝试过这种方法,发现它非常痛苦。总共有18种排列!

所以这里有一个通用的包装器。优点是你可以多次重复使用这个包装器。

关键是定义一个接口,如下所示:

// ResponseWriterTo可以将请求代理到底层的http.ResponseWriter。
// 它与CustomResponseWriter一起使用,用于包装具有自定义行为的http.ResponseWriter。
type ResponseWriterTo interface {
HeaderTo(w http.ResponseWriter) http.Header
WriteHeaderTo(w http.ResponseWriter, s int)
WriteTo(w http.ResponseWriter, b []byte) (n int, err error)

// http.ResponseWriter有时会实现其他方法。
CloseNotifyTo(w http.CloseNotifier) <-chan bool
FlushTo(w http.Flusher)
HijackTo(w http.Hijacker) (net.Conn, *bufio.ReadWriter, error)

// ReaderFrom用于优化从TCP连接或文件中读取的http包。
ReadFromTo(w io.ReaderFrom, r io.Reader) (n int64, err error)

}

这样我们就可以定义一个自定义的包装器函数(这是冗长的部分):

// CustomResponseWriter创建一个http.ResponseWriter,它实现了尽可能多的基本http.ResponseWriter的隐藏接口。
func CustomResponseWriter(base http.ResponseWriter, custom ResponseWriterTo) http.ResponseWriter {
rw := &customResponseWriter{base: base, custom: custom}

// 基本的http.ResponseWriter可以实现许多隐藏接口,所以检查所有的排列

type HCFR interface {
    http.ResponseWriter
    http.Hijacker
    http.CloseNotifier
    http.Flusher
    io.ReaderFrom
}
if _, ok := base.(HCFR); ok {
    return struct {
        HCFR
    }{rw}
}

type HCF interface {
    http.ResponseWriter
    http.Hijacker
    http.CloseNotifier
    http.Flusher
}
if _, ok := base.(HCF); ok {
    return struct {
        HCF
    }{rw}
}

// 其他排列...

return struct {
    http.ResponseWriter
}{rw}

}

// customResponseWriter允许我们将ResponseWriterTo适配到ResponseWriter。
type customResponseWriter struct {
base http.ResponseWriter
custom ResponseWriterTo
}

func (w *customResponseWriter) Header() http.Header { return w.custom.HeaderTo(w.base) }
func (w *customResponseWriter) Write(b []byte) (int, error) { return w.custom.WriteTo(w.base, b) }
func (w *customResponseWriter) WriteHeader(s int) { w.custom.WriteHeaderTo(w.base, s) }
func (w *customResponseWriter) CloseNotify() <-chan bool {
return w.custom.CloseNotifyTo(w.base.(http.CloseNotifier))
}
func (w *customResponseWriter) Flush() { w.custom.FlushTo(w.base.(http.Flusher)) }
func (w *customResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return w.custom.HijackTo(w.base.(http.Hijacker))
}
func (w *customResponseWriter) ReadFrom(r io.Reader) (n int64, err error) {
return w.custom.ReadFromTo(w.base.(io.ReaderFrom), r)
}

这个想法是将正确的接口嵌入到一个结构体中。然后只有"正确"的方法被暴露出来。

英文:

One solution is the brute-force solution. Actually implement all permutations. I tried this and found out how painful it is. There are 18 permutations!

So here is a general-purpose wrapper. The advantage is that you can re-use this wrapper as many times as you want.

The key is to define an interface like so:

// ResponseWriterTo can proxy requests to an underlying http.ResponseWriter.
// It is used with CustomResponseWriter to wrap an http.ResponseWriter with
// custom behavior.
type ResponseWriterTo interface {
    HeaderTo(w http.ResponseWriter) http.Header
    WriteHeaderTo(w http.ResponseWriter, s int)
    WriteTo(w http.ResponseWriter, b []byte) (n int, err error)

    // Additional methods that http.ResponseWriter sometimes implements.
    CloseNotifyTo(w http.CloseNotifier) &lt;-chan bool
    FlushTo(w http.Flusher)
    HijackTo(w http.Hijacker) (net.Conn, *bufio.ReadWriter, error)

    // ReaderFrom is used by the http package to optimize reads from TCP
    // connections or from files.
    ReadFromTo(w io.ReaderFrom, r io.Reader) (n int64, err error)
}

so that we can define a custom wrapper function (this is the verbose part):

// CustomResponseWriter creates a http.ResponseWriter that implements as many
// hidden interfaces from the base http.ResponseWriter as are available.
func CustomResponseWriter(base http.ResponseWriter, custom ResponseWriterTo) http.ResponseWriter {
    rw := &amp;customResponseWriter{base: base, custom: custom}

    // the base http.ResponseWriter can implement many hidden interfaces,
    // so check all permutations

    type HCFR interface {
        http.ResponseWriter
        http.Hijacker
        http.CloseNotifier
        http.Flusher
        io.ReaderFrom
    }
    if _, ok := base.(HCFR); ok {
        return struct {
            HCFR
        }{rw}
    }

    type HCF interface {
        http.ResponseWriter
        http.Hijacker
        http.CloseNotifier
        http.Flusher
    }
    if _, ok := base.(HCF); ok {
        return struct {
            HCF
        }{rw}
    }

    type HCR interface {
        http.ResponseWriter
        http.Hijacker
        http.CloseNotifier
        io.ReaderFrom
    }
    if _, ok := base.(HCR); ok {
        return struct {
            HCR
        }{rw}
    }

    type HFR interface {
        http.ResponseWriter
        http.Hijacker
        http.Flusher
        io.ReaderFrom
    }
    if _, ok := base.(HFR); ok {
        return struct {
            HFR
        }{rw}
    }

    type CFR interface {
        http.ResponseWriter
        http.CloseNotifier
        http.Flusher
        io.ReaderFrom
    }
    if _, ok := base.(CFR); ok {
        return struct {
            CFR
        }{rw}
    }

    type HC interface {
        http.ResponseWriter
        http.Hijacker
        http.CloseNotifier
    }
    if _, ok := base.(HC); ok {
        return struct {
            HC
        }{rw}
    }

    type HF interface {
        http.ResponseWriter
        http.Hijacker
        http.Flusher
    }
    if _, ok := base.(HF); ok {
        return struct {
            HF
        }{rw}
    }

    type CF interface {
        http.ResponseWriter
        http.CloseNotifier
        http.Flusher
    }
    if _, ok := base.(CF); ok {
        return struct {
            CF
        }{rw}
    }

    type HR interface {
        http.ResponseWriter
        http.Hijacker
        io.ReaderFrom
    }
    if _, ok := base.(HR); ok {
        return struct {
            HR
        }{rw}
    }

    type CR interface {
        http.ResponseWriter
        http.CloseNotifier
        io.ReaderFrom
    }
    if _, ok := base.(CR); ok {
        return struct {
            CR
        }{rw}
    }

    type FR interface {
        http.ResponseWriter
        http.Flusher
        io.ReaderFrom
    }
    if _, ok := base.(FR); ok {
        return struct {
            FR
        }{rw}
    }

    type H interface {
        http.ResponseWriter
        http.Hijacker
    }
    if _, ok := base.(H); ok {
        return struct {
            H
        }{rw}
    }

    type C interface {
        http.ResponseWriter
        http.CloseNotifier
    }
    if _, ok := base.(C); ok {
        return struct {
            C
        }{rw}
    }

    type F interface {
        http.ResponseWriter
        http.Flusher
    }
    if _, ok := base.(F); ok {
        return struct {
            F
        }{rw}
    }

    type R interface {
        http.ResponseWriter
        io.ReaderFrom
    }
    if _, ok := base.(R); ok {
        return struct {
            R
        }{rw}
    }

    return struct {
        http.ResponseWriter
    }{rw}
}

// customResponseWriter allows us to adapt a ResponseWriterTo to a ResponseWriter.
type customResponseWriter struct {
    base   http.ResponseWriter
    custom ResponseWriterTo
}

func (w *customResponseWriter) Header() http.Header         { return w.custom.HeaderTo(w.base) }
func (w *customResponseWriter) Write(b []byte) (int, error) { return w.custom.WriteTo(w.base, b) }
func (w *customResponseWriter) WriteHeader(s int)           { w.custom.WriteHeaderTo(w.base, s) }
func (w *customResponseWriter) CloseNotify() &lt;-chan bool {
    return w.custom.CloseNotifyTo(w.base.(http.CloseNotifier))
}
func (w *customResponseWriter) Flush() { w.custom.FlushTo(w.base.(http.Flusher)) }
func (w *customResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
    return w.custom.HijackTo(w.base.(http.Hijacker))
}
func (w *customResponseWriter) ReadFrom(r io.Reader) (n int64, err error) {
    return w.custom.ReadFromTo(w.base.(io.ReaderFrom), r)
}

The idea is to embed the correct interface into a struct. Then only the "right" methods are exposed.

huangapple
  • 本文由 发表于 2015年12月22日 13:08:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/34408808.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定