以下均摘自 Go 源码 go/src/net/http/server.go
接口型函数
- 以下代码中,定义了
HandlerFunc
函数类型,该类型的参数和返回值与 Handler
接口中的 ServeHTTP
是一致的,并绑定了 ServeHTTP
方法,在该方法中调用自己,从而实现了 Handler
接口。像这样实现了接口的函数类型,简称为接口型函数
1
2
3
4
5
6
7
8
9
10
11
12
13
| type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
// 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)
}
|
接口型函数作为参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| // 使用 http.HandleFunc()
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %s!", r.URL.Path)
}
func main() {
// 映射根路径 "/" 到 handler 函数
http.HandleFunc("/", handler)
// 映射 "/hello" 到 handler 函数
http.HandleFunc("/hello", handler)
// 监听 8080 端口
http.ListenAndServe(":8080", nil)
}
// 使用 http.Handle()
type customHandler struct{}
func (h *customHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello from custom handler!")
}
func myHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello from myHandler!")
}
func main() {
// 使用自定义的 handler
http.Handle("/custom", &customHandler{})
// 使用 http.HandlerFunc 适配器
http.Handle("/mypath", http.HandlerFunc(myHandler))
http.ListenAndServe(":8080", nil)
}
|
在上面的使用 http.Handle()
的例子中,第二个参数不但可以是实现了 http.Handler
接口的结构体,还可以像 http.HandleFunc()
那样是普通函数,只需要在转换成 http.HandlerFunc
类型即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| // Handle registers the handler for the given pattern in [DefaultServeMux].
// The documentation for [ServeMux] explains how patterns are matched.
func Handle(pattern string, handler Handler) {
if use121 {
DefaultServeMux.mux121.handle(pattern, handler)
} else {
DefaultServeMux.register(pattern, handler)
}
}
// HandleFunc registers the handler function for the given pattern in [DefaultServeMux].
// The documentation for [ServeMux] explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if use121 {
DefaultServeMux.mux121.handleFunc(pattern, handler)
} else {
DefaultServeMux.register(pattern, HandlerFunc(handler))
}
}
|
对比两个方法的内部实现,发现内部逻辑并没有区别,只是 http.HandleFunc
会将传入的 func(ResponseWriter, *Request)
类型的参数转换成 http.Handler
类型。
我们再看 http.ListenAndServe
函数的实现,摘自 http package - net/http - Go Packages
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
func (srv *Server) ListenAndServe() error {
if srv.shuttingDown() {
return ErrServerClosed
}
addr := srv.Addr
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return srv.Serve(ln)
}
|
可以看到第二个参数也是 http.Handler
类型,例子中 http.ListenAndServe(":8080", nil)
传入的是 nil
,代表着使用标准库 net/http
内置的路由,如果传入的是一个实现了 Handler
接口的结构体就可以完全托管所有的 HTTP 请求。
结语
这样,既能够将普通的函数类型(需类型转换)作为参数,也可以将结构体作为参数,使用更为灵活,可读性也更好,这就是接口型函数的价值。
参考文章