调用http.ResponseWriter.Write()

时间:2017-05-15 09:41:46

标签: http go error-handling server

说我有这个http处理程序:

func SomeHandler(w http.ResponseWriter, r *http.Request) {
    data := GetSomeData()
    _, err := w.Write(data)
}

我应该检查w.Write返回的错误吗?我见过的例子只是忽略它而什么都不做。此外,http.Error()等函数不会返回要处理的错误。

2 个答案:

答案 0 :(得分:15)

这取决于你。我的建议是,除非某些方法/函数的文档明确声明它永远不会返回非nil错误(例如bytes.Buffer.Write()),否则请始终检查错误,并且您可以做的最少是记录它,所以如果发生错误,它会留下一些标记,如果以后出现问题你可以调查。

写入http.ResponseWriter时也是如此。

如果发送数据失败(例如连接已关闭),您可能认为ResponseWriter.Write()可能只返回错误,但事实并非如此。实现http.ResponseWriter的具体类型是未导出的http.response类型,如果您检查未导出的response.write()方法,您会看到它可能会返回非nil错误其他一些原因。

ResponseWriter.Write()可能会返回非nil错误的原因

  • 如果连接被劫持(请参阅http.Hijacker):http.ErrHijacked
  • 如果指定了内容长度,并且您尝试编写的内容超过:http.ErrContentLength
  • 如果HTTP方法和/或HTTP状态根本不允许响应主体,并且您尝试写入超过0个字节:http.ErrBodyNotAllowed
  • 如果将数据写入实际连接失败。

即使您无法对错误做任何事情,记录它可能对以后调试错误有很大帮助。例如。你(或处理程序链中的其他人)劫持了连接,你试图稍后写信给它;你得到一个错误(http.ErrHijacked),记录它会立即显示原因。

提示“简单”记录错误

如果您不能对偶然的错误做任何事情并且它不是“showstopper”,您可以创建并使用一个执行检查和记录的简单函数,如下所示:

func logerr(n int, err error) {
    if err != nil {
        log.Printf("Write failed: %v", err)
    }
}

使用它:

logerr(w.Write(data))

“自动记录”错误的提示

如果您甚至不想一直使用logerr()函数,可以为http.ResponseWriter创建一个“自动”执行此操作的包装器:

type LogWriter struct {
    http.ResponseWriter
}

func (w LogWriter) Write(p []byte) (n int, err error) {
    n, err = w.ResponseWriter.Write(p)
    if err != nil {
        log.Printf("Write failed: %v", err)
    }
    return
}

使用它:

func SomeHandler(w http.ResponseWriter, r *http.Request) {
    w = LogWriter{w}
    w.Write([]byte("hi"))
}

使用LogWriter作为http.ResponseWriter的包装,如果写入原始http.ResponseWriter失败,则会自动记录。

这样做的好处还在于不会期望调用记录器功能,因此您可以将LogWriter“向下”传递给链,并且每个尝试写入记录器的人都将受到监控,记录下来,他们不必担心甚至不知道这一点。

但是在将LogWriter传递给链时必须小心,因为这也有一个缺点:LogWriter的值不会实现原始http.ResponseWriter可能也会执行的其他接口,例如http.Hijackerhttp.Pusher

以下是Go Playground中显示此操作的示例,并且还显示LogWriter不会实现其他接口;并且还显示了一种方法(使用2个“嵌套”类型断言)如何从LogWriter(示例中的http.Pusher)中得到我们想要的内容。

答案 1 :(得分:-1)

我想添加到@icza 解决方案中。你不需要创建日志结构,你可以使用简单的函数:

func logWrite(write func([]byte) (int, error), body []byte) {
    _, err := write(body)
    if err != nil {
        log.Printf("Write failed: %v", err)
    }
}

看看基于@icza 代码的方法:https://play.golang.org/p/PAetVixCgv4