ChatGPT解决这个技术问题 Extra ChatGPT

http.Handle 和 http.HandleFunc 的区别?

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))
})

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

http.Handlerhttp.Handle 接受的)是一个接口; http.HandlerFuncHandleFunc 接受的)是满足 http.Handler 的简单类型。例如,我有 appHandler type func (w http.ResponseWriter, r *http.Request) appError,它有一个满足 http.Handler 的 ServeHTTP 方法。在我的路由器中,我可以根据需要混合使用 Handle/HandleFunc,因为某些路由可能只需要基本的 HandlerFunc,而其他路由可能只需要我的自定义 appHandler
我想答案是“不”。

T
Telemachus

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

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

例如,标准包中的 file server 是一个结构,它包含文件服务的根目录并实现处理程序接口。

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

看看: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  }

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


补充一点,它本质上是一个重载(Go 不允许)。一种方法采用 func,另一种采用实现 Handler 接口的实例。正如大卫指出的那样,func 参数只是被包装以使其实现 Handler 接口,因此很方便。
m
merlindru

简单来说:

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

解决方案:为此使用 http.Handle。它接受 http.Handler 作为第二个参数。 http.Handler 是一个接口,应该从 http 包中实现 ServeHTTP

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

解决方案:为此使用 http.HandleFunc。它接受 http.HandlerFunc 作为第二个参数。

http.HandlerFunc 是一个函数类型,应该从 http 包中实现 ServeHTTP


m
merlindru

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

func Handle(pattern string, handler Handler) {
    DefaultServeMux.Handle(pattern, handler) 
}

Handle 期望我们通过 HandlerHandler 是一个接口

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

如果任何 type 实现了 ServeHTTP(ResponseWriter, *Request),例如:myCustomHandler,那么我们可以像 Handle(pattern string, myCustomHandler) 一样传递它。

在第二种情况下:

HandleFunc(pattern string, func(w ResponseWriter, r *Request) {
    // do some stuff
}

HandleFunc 需要一个函数,而 Handle 需要一个 Handler 接口。

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

type HandlerFunc func(ResponseWriter, *Request)
    
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

我相信问题实际上是围绕当有两种方法做同一件事时,为什么要选择 X 而不是 Y。只需查看两种方法签名不同的方法就很清楚了(这也是您正在解释的内容),但是这个答案并没有为初学者提供何时选择函数而不是结构的任何建议。
S
Samir Kape

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

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

使用句柄

package main

import (
    "fmt"
    "net/http"
)

type HelloHandler struct{}

func (h *HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello!")
}

type WorldHandler struct{}

func (h *WorldHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "World!")
}
func main() {
    hello := HelloHandler{}
    world := WorldHandler{}
    http.Handle("/hello", &hello)
    http.Handle("/world", &world)
    http.ListenAndServe(":8080", nil)
}

使用 HandleFunc

package main

import (
    "fmt"
    "net/http"
)

func hello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello!")
}
func world(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "World!")
}
func main() {
    http.HandleFunc("/hello", hello)
    http.HandleFunc("/world", world)
    http.ListenAndServe(":8080", nil)
}

附加信息:

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

// net/http/server.go

type Handler interface {
     ServeHTTP(ResponseWriter, *Request)
}

这是一个ServeHTTP信息,

// net/http/server.go

ServeHTTP(w http.ResponseWriter, r *http.Request)

// where,
// http.ResponseWriter is a writer interface, and,
// http.Request is a structure with request details.

现在让我们看看 HandlerFunc,

// net/http/server.go
// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.

type HandlerFunc func(ResponseWriter, *Request)
    
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request){
     f(w, r)
}

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

http.HandlerFunc(someFunc) 

// where,

// 1. someFunc() must have a signature, 
func someFunc(w http.ResponseWriter, r *http.Request)

// 2. That means, http.HandlerFunc(someFunc) is just a type casting of type http.HandlerFunc on a someFunc() and not a function call.

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

// net/http/server.go
// Handle registers the handler for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.

func Handle(pattern string, handler Handler) { 
     DefaultServeMux.Handle(pattern, handler) 
}

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

type MyHandler struct{}

func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 
    fmt.Fprintf(w, "Hello World!")
}

func main() {
    handler := MyHandler{}
    http.Handle("/hello", &handler)
    http.ListenAndServe()
}

如何将其他函数包装在 HandleFunc 上?我想要一个功能来首先授权请求
您可能想编写一个身份验证中间件,该中间件将在您的实际请求处理程序之前调用。有关详细信息,请参阅此内容:alexedwards.net/blog/making-and-using-middleware
Jwt 身份验证中间件:hackernoon.com/…

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅