How can I tell if net/http's ResponseWriter.Write() has been called?

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

How can I tell if net/http's ResponseWriter.Write() has been called?

问题

假设我有一系列的net/http处理程序,并且其中一个处理程序以HTTP错误(例如http.StatusInternalServerError)进行响应。我如何在后续的处理程序中检测到这个错误,并避免向客户端发送额外的数据?

或者,这完全是错误的解决方法吗?

英文:

Suppose I have a chain of net/http Handlers, and an early one responds with an HTTP error (http.StatusInternalServerError, for instance). How can I detect this in the following handlers, and avoid sending additional data to the client?

Or is this entirely the wrong approach to the problem?

答案1

得分: 6

http.ResponseWriter是一个接口。所以只需组合一个新的实例:

type MyResponseWriter struct {
    http.ResponseWriter
    WroteHeader bool
}

func (w *MyResponseWriter) Write(b []byte) (int, error) {
    w.WroteHeader = true
    return w.ResponseWriter.Write(b)
}

func (w *MyResponseWriter) WriteHeader(code int) {
    w.WroteHeader = true
    w.ResponseWriter.WriteHeader(code)
}

在你的处理程序中:

//...
if w, ok := w.(*MyResponseWriter); ok && w.WroteHeader {
    log.Println("已经写入,跳过")
    return
}

编辑: 还有一件事要考虑。大多数情况下,如果你有一个“链”式处理程序,这意味着一个处理程序在另一个处理程序内部被调用。所以如果你有像下面这样的东西:

type Handler1 struct { http.Handler }
type Handler2 struct { http.Handler }
type Handler3 struct { http.Handler }
var MyHandler http.Handler = Handler1{Handler2{Handler3{h}}}

只要每个处理程序在使用wr之后作为它们与内部处理程序的最后一件事情调用内部处理程序,你应该没问题,因为wr甚至不会到达内部处理程序。例如:

func (h Handler2) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if somethingBadHappened() {
        w.WriteHeader(http.StatusInternalServerError)
        return
    }
    h.ServeHTTP(w, r) // 如果somethingBadHappened()没有被调用,则不会执行此行。
}

希望对你有所帮助!

英文:

http.ResponseWriter is an interface. So just compose a new instance of it:

type MyResponseWriter struct {
    http.ResponseWriter
    WroteHeader bool
}

func (w *MyResponseWriter) Write(b []byte) (int, error) {
    w.WroteHeader = true
    return w.ResponseWriter.Write(b)
}

func (w *MyResponseWriter) WriteHeader(code int) {
    w.WroteHeader = true
    w.ResponseWriter.WriteHeader(code)
}

And in your handlers:

//...
if w, ok := w.(*MyResponseWriter); ok && w.WroteHeader {
    log.Println("Already wrote, skipping")
    return
}

EDIT: Another thing to consider. Most of the time if you have a "chain" of handlers that means that a handler is called inside a handler. So if you have something like

type Handler1 struct { http.Handler }
type Handler2 struct { http.Handler }
type Handler3 struct { http.Handler }
var MyHandler http.Handler = Handler1{Handler2{Handler3{h}}}

as long as each of those call the inner handler as the last thing they do with w and r, you should be fine because then w and r won't even reach the inner handler. E.g.

func (h Handler2) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if somethingBadHappened() {
        w.WriteHeader(http.StatusInternalServerError)
        return
    }
    h.ServeHTTP(w, r) // Not called if somethingBadHappened().
}

答案2

得分: 0

首先,可能存在一种更轻量级的解决方案。

但是,如果你找不到这样的解决方案,可以考虑使用x/net/context来实现超时、截止日期以及中间件链的提前终止。

英文:

First: a lighter-weight solution may exist.

However, if you cannot find one, consider using x/net/context to allow you to implement timeouts, deadlines, and of course, early termination of middleware chains.

huangapple
  • 本文由 发表于 2015年8月9日 02:53:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/31897411.html
匿名

发表评论

匿名网友

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

确定