http.Handle和http.HandleFunc之间的区别?

Ral*_*alf 52 go

Go文档对http包有以下示例:

http.Handle("/foo", fooHandler)
http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})
Run Code Online (Sandbox Code Playgroud)

我有点难以理解Handle和HandleFunc之间的区别以及为什么需要两个.有人可以用清楚的语言向新的Gopher解释吗?

Dav*_*rth 38

基本上,HTTP服务器的"mux"有一个路径映射 - >处理程序接口

我假设这里使用接口,以允许您实现具有状态的复杂路径处理程序.

例如,标准包中的文件服务器是包含文件服务的根目录的结构,并实现处理程序接口.

这说,对于简单的东西,功能更容易,更清晰.所以他们添加了一个特殊的生成器,这样你就可以轻松传入一个函数.

看看:server.go

从线:1216(截至今天)

  1216  type HandlerFunc func(ResponseWriter, *Request)
  1217  
  1218  // ServeHTTP calls f(w, r).
  1219  func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
  1220      f(w, r)
  1221  }
Run Code Online (Sandbox Code Playgroud)

他们正在做的是在自定义类型上实现接口(恰好与接口的api匹配).

  • 只是添加一点,它本质上是一个重载(Go不允许).一个方法接受一个func,另一个接受一个实现Handler接口的实例.正如David指出的那样,func参数只是被包装起来以使它实现Handler接口,所以它很方便. (4认同)

Mar*_*ris 19

非常正确的答案,所以我不会说太多,而是简单地解释一下:

问题:我想创建一个响应 HTTP 请求的对象(类型)。

解决方案:使用http.Handle和您创建的对象应该实现ServeHTTP来自 HTTP 包的接口。

问题:我想要一个函数来响应我的 HTTP 请求。

解决方案:使用http.HandleFunc该注册和使用HTTP服务器的功能。


Sam*_*ape 15

处理程序函数只是创建处理程序的便捷方法。

\n

虽然它们都可以用来创建处理程序,但因为使用处理程序函数更干净,而且它的工作效果也很好,为什么还要使用处理程序呢?这一切都归结为设计。如果您有一个现有的接口,或者如果您想要一个也可以用作处理程序的类型,只需将 ServeHTTP 方法添加到该接口,您\xe2\x80\x99 将获得一个可以分配给 URL 的处理程序。它还可以让您\n构建更加模块化的 Web 应用程序。

\n

使用手柄

\n
package main\n\nimport (\n    "fmt"\n    "net/http"\n)\n\ntype HelloHandler struct{}\n\nfunc (h *HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n    fmt.Fprintf(w, "Hello!")\n}\n\ntype WorldHandler struct{}\n\nfunc (h *WorldHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n    fmt.Fprintf(w, "World!")\n}\nfunc main() {\n    hello := HelloHandler{}\n    world := WorldHandler{}\n    http.Handle("/hello", &hello)\n    http.Handle("/world", &world)\n    http.ListenAndServe(":8080", nil)\n}\n
Run Code Online (Sandbox Code Playgroud)\n

使用 HandleFunc

\n
package main\n\nimport (\n    "fmt"\n    "net/http"\n)\n\nfunc hello(w http.ResponseWriter, r *http.Request) {\n    fmt.Fprintf(w, "Hello!")\n}\nfunc world(w http.ResponseWriter, r *http.Request) {\n    fmt.Fprintf(w, "World!")\n}\nfunc main() {\n    http.HandleFunc("/hello", hello)\n    http.HandleFunc("/world", world)\n    http.ListenAndServe(":8080", nil)\n}\n
Run Code Online (Sandbox Code Playgroud)\n

附加信息:

\n

http.Handler 是一个带有 ServeHTTP() 方法的接口

\n
// net/http/server.go\n\ntype Handler interface {\n     ServeHTTP(ResponseWriter, *Request)\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这是一个 ServeHTTP 信息,

\n
// net/http/server.go\n\nServeHTTP(w http.ResponseWriter, r *http.Request)\n\n// where,\n// http.ResponseWriter is a writer interface, and,\n// http.Request is a structure with request details.\n
Run Code Online (Sandbox Code Playgroud)\n

现在让我们看看 HandlerFunc,

\n
// net/http/server.go\n// The HandlerFunc type is an adapter to allow the use of\n// ordinary functions as HTTP handlers. If f is a function\n// with the appropriate signature, HandlerFunc(f) is a\n// Handler that calls f.\n\ntype HandlerFunc func(ResponseWriter, *Request)\n    \n// ServeHTTP calls f(w, r).\nfunc (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request){\n     f(w, r)\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这意味着,http.HandlerFunc 是一个实现了 ServeHTTP 方法的类型。

\n
http.HandlerFunc(someFunc) \n\n// where,\n\n// 1. someFunc() must have a signature, \nfunc someFunc(w http.ResponseWriter, r *http.Request)\n\n// 2. That means, http.HandlerFunc(someFunc) is just a type casting of type http.HandlerFunc on a someFunc() and not a function call.\n
Run Code Online (Sandbox Code Playgroud)\n

现在让我们转到 http.Handle(),

\n
// net/http/server.go\n// Handle registers the handler for the given pattern\n// in the DefaultServeMux.\n// The documentation for ServeMux explains how patterns are matched.\n\nfunc Handle(pattern string, handler Handler) { \n     DefaultServeMux.Handle(pattern, handler) \n}\n
Run Code Online (Sandbox Code Playgroud)\n

通过查看上面的代码片段,您可能已经注意到,\n第二个参数接受 Handler 接口,这意味着您可以创建任何类型并为其实现 ServeHTTP() 方法来满足此要求。请参考下面的例子来证明。

\n
type MyHandler struct{}\n\nfunc (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { \n    fmt.Fprintf(w, "Hello World!")\n}\n\nfunc main() {\n    handler := MyHandler{}\n    http.Handle("/hello", &handler)\n    http.ListenAndServe()\n}\n
Run Code Online (Sandbox Code Playgroud)\n


ala*_*min 5

不,不一样。让我们检查一下

func Handle(pattern string, handler Handler) {
    DefaultServeMux.Handle(pattern, handler) 
}
Run Code Online (Sandbox Code Playgroud)

handle希望我们通过HandlerHandler是一个接口

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}
Run Code Online (Sandbox Code Playgroud)

例如,如果有type实现ServeHTTP(ResponseWriter, *Request)的话: myCustomHandler那么我们可以像那样传递它Handle(pattern string, myCustomHandler)

在第二种情况下:

HandleFunc(pattern string, func(w ResponseWriter, r *Request) {
    // do some stuff
}
Run Code Online (Sandbox Code Playgroud)

HandleFunc期望HandleHandler接口的功能。

因此,如果您只想传递函数,则可以使用http.HandleFunc(..)。就像@David一样,它在后台Handler通过调用来实现接口ServeHTTP

type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}
Run Code Online (Sandbox Code Playgroud)

  • 我相信问题实际上在于,当有两种方法可以做同一件事时,为什么人们会选择 X 而不是 Y。通过查看两种方法就可以很清楚地看出方法签名是不同的(这也是您所解释的),但是这个答案没有为初学者提供关于何时选择函数而不是结构的任何建议。 (2认同)