调用 http.ResponseWriter.Write() 时检查错误
Posted
技术标签:
【中文标题】调用 http.ResponseWriter.Write() 时检查错误【英文标题】:Check errors when calling http.ResponseWriter.Write() 【发布时间】:2017-10-14 00:58:42 【问题描述】:假设我有这个 http 处理程序:
func SomeHandler(w http.ResponseWriter, r *http.Request)
data := GetSomeData()
_, err := w.Write(data)
我应该检查w.Write
返回的错误吗?我见过的例子只是忽略它,什么也不做。此外,http.Error()
之类的函数不会返回要处理的错误。
【问题讨论】:
“我应该检查返回的错误...” 答案取决于您的要求。如果发生错误,您打算如何反应?如果您检查错误并且在错误时您所做的只是“不知道现在该做什么......让我们登录到 /dev/null 并将其称为已处理。”那么您可以很好地忽略错误。可以就一般问题给出一般建议。你的不一般。 【参考方案1】:这取决于你。我的建议是,除非某些方法/函数的文档明确声明它永远不会返回非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 = LogWriterw
w.Write([]byte("hi"))
使用LogWriter
作为http.ResponseWriter
的包装器,如果写入原始http.ResponseWriter
失败,则会自动记录。
这还有一个很大的好处,就是不期望调用记录器函数,因此您可以将LogWriter
的值“向下”传递给链,并且尝试写入它的每个人都将被监视和记录,他们不必担心,甚至不必知道这一点。
但是在将LogWriter
传递到链中时必须小心,因为这也有一个缺点:LogWriter
的值不会实现原始http.ResponseWriter
也可能执行的其他接口,例如http.Hijacker
或 http.Pusher
。
这是Go Playground 上的一个示例,它显示了这一点,也表明LogWriter
不会实现其他接口;并且还展示了一种方法(使用 2 个“嵌套”类型断言)如何仍然从 LogWriter
(示例中为 http.Pusher
)中获取我们想要的内容。
【讨论】:
根据我在一些帖子中看到的内容,可以放弃“自动记录错误提示”选项。可以在这里找到解释:blog.merovius.de/2017/07/30/… 虽然它已经在答案中提到了“但是通过时必须小心......”我只是想把链接放在这里给那些想知道究竟是什么问题的人,所以答案是 +1。【参考方案2】:我想添加到@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
【讨论】:
以上是关于调用 http.ResponseWriter.Write() 时检查错误的主要内容,如果未能解决你的问题,请参考以下文章