当 web 服务器发生一个恐慌( panic )时,我们的 web 服务器就会终止。这样非常的糟糕:一个 web 服务必须是一个健壮的程序,能够处理可能会出现的问题。
一个方法是可以在每一个处理函数( handler )中去使用 defer/recover
,但是这样会导致出现很多重复的代码。更加优雅的解决方法是使用 13.5 章节 中的闭包的方法处理错误。我们将这种机制应用到上一节中的 simple webserver 中,当然,它也可以很容易的应用于任何 web 服务器的程序中。
为了使代码更具可读性,我们为处理函数(HandleFunc)创建一个 type :
type HandleFnc func(http.ResponseWriter,*http.Request)
我们模仿 13.5 章节 的 errorHandler 函数,创建一个 logPanics 函数:
func logPanics(function HandleFnc) HandleFnc {
return func(writer http.ResponseWriter, request *http.Request) {
defer func() {
if x := recover(); x != nil {
log.Printf("[%v] caught panic: %v", request.RemoteAddr, x)
}
}()
function(writer, request)
}
}
然后我将处理函数作为回调包装进 logPanics:
http.HandleFunc("/test1", logPanics(SimpleServer))
http.HandleFunc("/test2", logPanics(FormServer))
处理函数中应该包含一个 panic 的调用,或者像 [1.3 章节] 中的用来检查错误的 check (error) 函数;下面是完整的代码示例:
译者注:可以在 SimpleServer 中直接写一个 panic (errors.New (“test error”)) 来模拟出现 panic
Listing 15.11—robust_webserver.go:
package main
import (
"net/http"
"io"
"log"
)
type HandleFnc func(http.ResponseWriter,*http.Request)
// 将上一章节示例 15.10 中的代码复制过来,除了 main() 函数都复制过来
func main() {
http.HandleFunc("/test1", logPanics(SimpleServer))
http.HandleFunc("/test2", logPanics(FormServer))
if err := http.ListenAndServe(":8088", nil); err != nil {
panic(err)
}
}
func logPanics(function HandleFnc) HandleFnc {
return func(writer http.ResponseWriter, request *http.Request) {
defer func() {
if x := recover(); x != nil {
log.Printf("[%v] caught panic: %v", request.RemoteAddr, x)
// 下面一行代码是译者添加,默认出现 panic 只会记录日志,页面就是一个无任何输出的白页面,
// 可以给页面一个错误信息,如下面的示例返回了一个 500
http.Error(writer, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
}()
function(writer, request)
}
}