Go语言基础:简单梳理Http.Handler接口 | 豆包MarsCode AI 刷题

17 阅读3分钟

初学者(比如说我🙃)尝试使用net/http包去实现客户端和服务端的时候回发现,我们可以通过多种方式来满足我们的需求,但是往往会混淆HandlerHandlerFunchandler几个相近的类型或者名称,接下来结合《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为空