我正在尝试通读以下内容:https : //blog.golang.org/error-handling-and-go特别是标题为Simplifying repetitive error handling
.
他们这样称呼http.Handle
:
func init() {
http.Handle("/view", appHandler(viewRecord))
}
Run Code Online (Sandbox Code Playgroud)
http.Handle
的第二个参数需要一个类型Handler
(https://golang.org/pkg/net/http/#Handler),它需要一个方法serveHttp
。
serveHttp
这里的功能:
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err := fn(w, r); err != nil {
http.Error(w, err.Error(), 500)
}
}
Run Code Online (Sandbox Code Playgroud)
所以,他们的类型appHandler
现在实现了 Handler 接口,因为它实现了ServeHTTP
,我明白了。所以它可以在Handle
函数中使用而viewRecord
不能。
我感到困惑的地方是viewRecord
which 类型appHandler
和ServeHTTP
. 哪个调用哪个?他们对“函数如何也可以成为接收器”进行了附加评论,我认为这就是我被绊倒的地方。
在这里,fn appHandler
作为接收者,我希望有类似的东西viewRecord.serveHTTP()
,但这没有意义,而且viewRecord
是一个函数。我认为正在发生的是Handle
函数调用serveHTTP
,但如何serveHTTP
调用viewRecord
?
还appHandler(viewRecord)
做演员吗?
基本上,我正在寻找关于函数作为接收器意味着什么的一些清晰度。我是新手,我想我不小心落在了这里的一个非平凡的地雷上。
任何类型都可以是接收器。例如:
type X int
Run Code Online (Sandbox Code Playgroud)
这X
是一个新类型,您可以为它创建方法:
func (x X) method() {
// Do something with x
}
Run Code Online (Sandbox Code Playgroud)
在 Go 中,函数就像任何其他类型一样。所以如果你有一个函数类型:
type F func()
Run Code Online (Sandbox Code Playgroud)
这F
是一个新类型,因此您可以为其定义方法:
func (x F) method() {
x()
}
Run Code Online (Sandbox Code Playgroud)
有了上面的声明,现在你可以调用value.method()
ifvalue
类型了F
。
a:=F(func() {fmt.Println("hey")})
a.method()
Run Code Online (Sandbox Code Playgroud)
这里,a
是一个类型的变量F
。F
有一个方法叫做method
,所以你可以调用a.method
. 当你调用它时,a.method
调用a
,这是一个函数。
回到你的例子,appHandler
似乎是一个函数类型:
type appHandler func(http.ResponseWriter, *http.Request)
Run Code Online (Sandbox Code Playgroud)
因此,可以使用具有该签名的任何函数来代替appHandler
. 假设您编写了这样一个函数:
func myHandler(http.ResponseWriter, *http.Request) {
// Handle request
}
Run Code Online (Sandbox Code Playgroud)
您可以在任何需要 an 的地方传递此函数appHandler
。但是,如果Handler
不编写这样的结构,就不能将 if 传递到需要 a 的地方:
type myHandlerStruct struct{}
func (myHandlerStruct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
myHandler(w,r)
}
Run Code Online (Sandbox Code Playgroud)
您可以为 type 定义一个方法,而不是定义一个新的结构appHandler
:
func (a appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
a(w,r)
}
Run Code Online (Sandbox Code Playgroud)
现在您可以传递appHandler
到appHandler
需要的地方以及Handler
需要的地方。如果它作为 a 调用Handler
,则该ServeHTTP
方法将简单地将调用转发到底层函数。
好吧,有很多问题,但我会尽力回答所有问题。
ServeHTTP
这定义了该类型的方法appHandler
,这意味着该方法appHandler
现在是有效的net/http.Handler
。该类型appHandler
恰好是函数类型,所以是的 - 这里有一个函数值,其中包含可以调用它的方法,与调用函数本身分开。http.HandleFunc
顺便说一句,这已经是标准库中的工作原理- 检查其源代码也可能有助于理解其工作原理。
注册处理程序后,net/http
调用其ServeHTTP
方法来处理传入请求。我们类型的方法appHandler
是:
func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if err := fn(w, r); err != nil {
http.Error(w, err.Error(), 500)
}
}
Run Code Online (Sandbox Code Playgroud)
因此,在函数的第一行,ServeHTTP
调用appHandler
函数 -fn appHandler
是我们的接收者,创建fn
一个函数值,我们可以调用它:
fn(w, r)
Run Code Online (Sandbox Code Playgroud)
这是通过将我们的处理函数转换为以下appHandler
类型来利用的:
http.Handle("/view", appHandler(viewRecord))
Run Code Online (Sandbox Code Playgroud)
这实际上与更常见的中间件模式没有什么不同:
func middleware(fn func(w http.ResponseWriter, r *http.Request) error) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if err := fn(w, r); err != nil {
http.Error(w, err.Error(), 500)
}
}
}
Run Code Online (Sandbox Code Playgroud)
这做了完全相同的事情,但是使用闭包和函数参数而不是方法和函数接收器。这是通过调用我们的包装函数来利用的:
http.Handle("/view", http.HandlerFunc(middleware(viewRecord)))
Run Code Online (Sandbox Code Playgroud)
这使用我们的middleware
函数来包装viewRecord
并将其转换为 a http.HandlerFunc
(正如前面提到的,它实际上会做appHandler
与函数类型方法相同的事情)。