包装器(代理)在 golang



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

情况有点复杂,当你不想松散一个接口时,当包装的对象有它时。

在示例示例中,我编写了如何选择性地实现 http。冲洗器。但是如何解决文章中的情况,当 w 可以实现 3 个接口中的一些(http。冲洗器,http。劫机者,http。推杆(。这是更好的解决方案,编写 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)
    })
}

你嵌入ResponseWriter,并在不添加任何行为的情况下Flush遮蔽;删除 shadow 方法。您仍然需要执行某些类型整理,但除非您尝试添加或更改行为(可能只是基于问题的WriteHeader(,否则不需要执行任何方法实现。

因为你只是想在这里公开嵌入类型的方法,你甚至不需要定义所有的结构,你可以使用匿名结构(这里的游乐场示例(:

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

最新更新