我正在尝试通读以下内容: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不能。
我感到困惑的地方是viewRecordwhich 类型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与函数类型方法相同的事情)。
| 归档时间: |
|
| 查看次数: |
247 次 |
| 最近记录: |