2.2.1goweb内置的 HTTP 处理程序
net/http 使用源码分析
在 Go 语言的 HTTP 服务器里,HTTP handler 是实现了http.Handler
接口的对象。该接口定义如下:
type Handler interface {ServeHTTP(ResponseWriter, *Request)
}
ServeHTTP
方法接收两个参数:
http.ResponseWriter
:用于向客户端发送 HTTP 响应。*http.Request
:代表客户端的 HTTP 请求。
示例代码
package mainimport ("fmt""net/http"
)// 自定义的HTTP处理程序
type HelloHandler struct{}// 实现http.Handler接口的ServeHTTP方法
func (h HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello, World!")
}func main() {// 创建自定义handler的实例hello := HelloHandler{}// 注册handler到默认的ServeMuxhttp.Handle("/", hello)// 启动HTTP服务器,监听8080端口fmt.Println("Starting server on :8080")http.ListenAndServe(":8080", nil)
}
在上述代码中,HelloHandler
结构体实现了http.Handler
接口,其ServeHTTP
方法会向客户端发送 "Hello, World!" 消息。
源码分析
http.Handler
接口定义在net/http/server.go
文件中:
// Handler is an interface implemented by an HTTP handler.
type Handler interface {ServeHTTP(ResponseWriter, *Request)
}
该接口仅有一个方法ServeHTTP
,任何实现了这个方法的类型都可作为 HTTP handler 使用。
http.ServeMux
http.ServeMux
是一个 HTTP 请求多路复用器,它会将不同的请求路径映射到对应的 handler。其定义如下:
// ServeMux is an HTTP request multiplexer.
// It matches the URL of each incoming request against a list of registered
// patterns and calls the handler for the pattern that most closely matches the
// URL.
type ServeMux struct {mu sync.RWMutexm map[string]muxEntryes []muxEntry // slice of entries sorted from longest to shortest.hosts bool // whether any patterns contain hostnames
}type muxEntry struct {h Handlerpattern string
}
ServeMux
结构体包含一个映射m
,用于存储路径模式和对应的 handler。http.Handle
函数会向ServeMux
注册 handler:
// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
func (mux *ServeMux) Handle(pattern string, handler Handler) {mux.mu.Lock()defer mux.mu.Unlock()if pattern == "" {panic("http: invalid pattern")}if handler == nil {panic("http: nil handler")}if _, exist := mux.m[pattern]; exist {panic("http: multiple registrations for " + pattern)}if mux.m == nil {mux.m = make(map[string]muxEntry)}e := muxEntry{h: handler, pattern: pattern}mux.m[pattern] = eif pattern[len(pattern)-1] == '/' {mux.es = appendSorted(mux.es, e)}if pattern[0] != '/' {mux.hosts = true}
}
ServeMux
的ServeHTTP
方法会根据请求的路径查找对应的 handler 并调用其ServeHTTP
方法:
// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {if r.RequestURI == "*" {if r.ProtoAtLeast(1, 1) {w.Header().Set("Connection", "close")}w.WriteHeader(StatusBadRequest)return}h, _ := mux.Handler(r)h.ServeHTTP(w, r)
}
http.ListenAndServe
http.ListenAndServe
函数用于启动一个 HTTP 服务器,它会监听指定的地址和端口,并处理 HTTP 请求:
// 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()
}
http.ListenAndServe
函数创建了一个http.Server
实例,并调用其ListenAndServe
方法启动服务器。
内置的 HTTP 处理程序
http.NotFoundHandler
功能概述
http.NotFoundHandler
是 net/http
包提供的一个内置处理程序,当客户端请求的资源不存在时,使用这个处理程序可以返回一个标准的 404 响应。它通常用于处理那些没有匹配到任何路由规则的请求。
使用示例
package mainimport ("net/http"
)func main() {// 注册一个空的路由,这里没有具体的处理逻辑http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {// 处理其他特定逻辑})// 对于所有未匹配的路由,使用 NotFoundHandler 返回 404 响应http.HandleFunc("/notfound", http.NotFoundHandler().ServeHTTP)// 启动服务器,监听 8080 端口http.ListenAndServe(":8080", nil)
}
在这个示例中,当客户端请求 /notfound
路径时,会触发 http.NotFoundHandler
,返回 404 状态码和相应的提示信息。
源码分析
http.NotFoundHandler
的定义和实现位于 net/http/server.go
文件中,下面是其源码:
// NotFoundHandler returns a simple request handler
// that replies to each request with a ``404 page not found'' reply.
func NotFoundHandler() Handler { return NotFoundHandlerFunc }// NotFoundHandlerFunc is the default handler for http.NotFoundHandler.
func NotFoundHandlerFunc(w ResponseWriter, r *Request) {http.Error(w, "404 page not found", http.StatusNotFound)
}
NotFoundHandler
是一个函数,它返回一个实现了http.Handler
接口的处理程序。在这个例子中,返回的是NotFoundHandlerFunc
。NotFoundHandlerFunc
是一个具体的函数,它实现了http.Handler
接口的ServeHTTP
方法所需的签名。当调用这个函数时,它会使用http.Error
函数向客户端发送一个 404 状态码和相应的错误信息。
http.Error
函数
http.Error
函数用于向客户端发送一个带有指定错误信息和状态码的响应,其源码如下:
// Error replies to the request with the specified error message and HTTP code.
// It does not otherwise end the request; the caller should ensure no further
// writes are done to w.
// The error message should be plain text.
func Error(w ResponseWriter, error string, code int) {w.Header().Set("Content-Type", "text/plain; charset=utf-8")w.Header().Set("X-Content-Type-Options", "nosniff")w.WriteHeader(code)io.WriteString(w, error)
}
http.Error
函数首先设置响应头,指定内容类型为纯文本(text/plain
)并设置字符集为utf-8
,同时设置X-Content-Type-Options
为nosniff
以防止浏览器进行内容类型嗅探。- 然后,使用
w.WriteHeader(code)
设置 HTTP 状态码。 - 最后,使用
io.WriteString
函数将错误信息写入响应体。
RedirectHandler
http.RedirectHandler
的定义和实现位于net/http/server.go
文件中,下面是其源码:
// RedirectHandler returns a request handler that redirects
// each request it receives to the given url using the given
// status code.
//
// The provided code should be in the 3xx range and is usually
// StatusMovedPermanently, StatusFound or StatusSeeOther.
func RedirectHandler(url string, code int) Handler {return redirectHandler{url, code}
}type redirectHandler struct {url stringcode int
}func (rh redirectHandler) ServeHTTP(w ResponseWriter, r *Request) {Redirect(w, r, rh.url, rh.code)
}
// Redirect replies to the request with a redirect to url,
// which may be a path relative to the request path.
//
// The provided code should be in the 3xx range and is usually
// StatusMovedPermanently, StatusFound or StatusSeeOther.
func Redirect(w ResponseWriter, r *Request, url string, code int) {if u, err := urlparse.Parse(url); err == nil {// If url was relative, make absolute by// combining with request path.// The browser would probably do this for us,// but doing it ourselves is more reliable.//// RFC 2616 says that the Location header must be an absolute URI,// but we obey the spirit of the RFC and allow relative URIs so// long as they can be made absolute relative to the request URI.if u.Scheme == "" && u.Host == "" {oldpath := r.URL.Pathif oldpath == "" { // should not happen, but avoid a crash if it doesoldpath = "/"}// no leading http://serverif url == "" || url[0] != '/' {// make relative path absolutelast := len(oldpath) - 1if last >= 0 && oldpath[last] != '/' {oldpath = oldpath + "/"}url = oldpath + url}var query stringif i := strings.Index(url, "?"); i != -1 {url, query = url[:i], url[i:]}// clean up but preserve trailing slashtrailing := strings.HasSuffix(url, "/")url = path.Clean(url)if trailing && !strings.HasSuffix(url, "/") {url = url + "/"}url = url + query}}w.Header().Set("Location", hexEscapeNonASCII(url))w.WriteHeader(code)// RFC 2616 recommends that a short note "SHOULD" be included in the// response because older user agents may not understand 301/307.// Shouldn't send the response for POST or HEAD; that leaves GET.if r.Method == "GET" {body := "<a href=\"" + html.EscapeString(url) + "\">" + http.StatusText(code) + "</a>.\n"io.WriteString(w, body)}
}
http.Redirect
函数首先会处理相对 URL,将其转换为绝对 URL。- 然后,设置响应头的
Location
字段为目标 URL,并使用w.WriteHeader(code)
设置重定向状态码。 - 最后,如果请求方法是
GET
,会在响应体中返回一个简单的 HTML 链接,提示用户重定向信息