初学者(比如说我🙃)尝试使用net/http
包去实现客户端和服务端的时候回发现,我们可以通过多种方式来满足我们的需求,但是往往会混淆Handler
、HandlerFunc
、handler
几个相近的类型或者名称,接下来结合《Go程序设计语言》,来讲一讲http.Handler接口,将知识点和注意点都梳理和串通一下。
Http.Handler
// A Handler responds to an HTTP request.
//
// ServeHTTP should write reply headers and data to the ResponseWriter
// and then return. Returning signals that the request is finished; it
// is not valid to use the ResponseWriter or read from the
// Request.Body after or concurrently with the completion of the
// ServeHTTP call.
//
// Depending on the HTTP client software, HTTP protocol version, and
// any intermediaries between the client and the Go server, it may not
// be possible to read from the Request.Body after writing to the
// ResponseWriter. Cautious handlers should read the Request.Body
// first, and then reply.
//
// Except for reading the body, handlers should not modify the
// provided Request.
//
// If ServeHTTP panics, the server (the caller of ServeHTTP) assumes
// that the effect of the panic was isolated to the active request.
// It recovers the panic, logs a stack trace to the server error log,
// and either closes the network connection or sends an HTTP/2
// RST_STREAM, depending on the HTTP protocol. To abort a handler so
// the client sees an interrupted response but the server doesn't log
// an error, panic with the value ErrAbortHandler.
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
接口就一个方法,我们只需要实现ServerHTTP即可
http.HandleFunc
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
handler
HandleFunc的参数
handler func(ResponseWriter, *Request)
首先我们先来看看常见Go语言搭建简易web服务器的几种方式:
第一种:直接使用http.HandleFunc()
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", indexHandler)
http.HandleFunc("/hello", helloHandler)
log.Fatal(http.ListenAndServe(":8848", nil))
}
func helloHandler(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer, "Header的情况如下\n")
for key, value := range request.Header {
fmt.Fprintf(writer, "Header[%q] = %q\n", key, value)
}
}
func indexHandler(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer, "URL.Path = %q\n", request.URL.Path)
}
我们可以注意到log.Fatal(http.ListenAndServe(":8848", nil))
中的参数hander为nil,这个我们可以对比一下下面这种方式。
第二种:实现Http.Handler
package main
import (
"fmt"
"log"
"net/http"
)
type Engine struct{}
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
switch req.URL.Path {
case "/":
fmt.Fprintf(w, "URL.Path = %q\n", req.URL.Path)
case "/hello":
fmt.Fprintf(w, "Header的情况如下\n")
for key, value := range req.Header {
fmt.Fprintf(w, "Header[%q] = %q\n", key, value)
}
default:
fmt.Fprintf(w, "404 NOT FOUND: %s\n", req.URL)
}
}
func main() {
engine := new(Engine)
log.Fatal(http.ListenAndServe(":8848", engine))
}
我们可以看到,log.Fatal(http.ListenAndServe(":8848", engine))
中的参数为engine
,因为engine
实现ServeHTTP
方法,请求会先来到用户自定义的ServeHTTP
中进行处理。
看到这里或许你会疑问:方法一的http.ListenAndServe(":8848", nil)
其参数为nil,那请求是怎么和http.HandleFunc()
建立关系的?
我们点进这个方法
// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
方法上面的注释其实就大致告诉我们的原因:HandleFunc在DefaultServeMux中注册给定匹配路径的处理程序函数。ServeMux的文档解释了如何匹配模式。
我们会发现,这DefaultServeMux
其实就是ServeMux
,在往下看就会发现一个熟悉的接口实现:
我们回到http.ListenAndServe(":8848", nil)
,我们点进ListenAndServe
// ListenAndServe listens on the TCP network address addr and then calls
// Serve with handler to handle requests on incoming connections.
// Accepted connections are configured to enable TCP keep-alives.
//
// The handler is typically nil, in which case the DefaultServeMux is used.
//
// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
显然`Server`是关键结构体,再来到`Server`看看
// A Server defines parameters for running an HTTP server.
// The zero value for Server is a valid configuration.
type Server struct {
// Addr optionally specifies the TCP address for the server to listen on,
// in the form "host:port". If empty, ":http" (port 80) is used.
// The service names are defined in RFC 6335 and assigned by IANA.
// See net.Dial for details of the address format.
Addr string
Handler Handler // handler to invoke, http.DefaultServeMux if nil
...
}
注释解释`http.DefaultServeMux`生效的条件就是我们在`http.ListenAndServe(addr string, handler Handler)`handler为空