Golang嵌入具有其他隐藏方法的接口吗?

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?

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).

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) <-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 := &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() <-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.