HTTP Handler接口
使用http.ListenAndServe
函数启动一个基本HTTP服务器,其参数包含监听的地址和http.Handler
1 2 3 4
| func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe() }
|
http.Handler
是一个接口类型,只要实现了ServeHTTP
即可作为一个Handler
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 29 30 31 32 33 34 35
| type Handler interface { ServeHTTP(ResponseWriter, *Request) }
type ResponseWriter interface { Header() Header Write([]byte) (int, error) WriteHeader(statusCode int) }
type Request struct { Method string URL *url.URL Proto string ProtoMajor int ProtoMinor int Header Header Body io.ReadCloser GetBody func() (io.ReadCloser, error) ContentLength int64 TransferEncoding []string Close bool Host string Form url.Values PostForm url.Values MultipartForm *multipart.Form Trailer Header RemoteAddr string RequestURI string TLS *tls.ConnectionState Cancel <-chan struct{} Response *Response ctx context.Context }
|
先上个例子
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 29 30 31
| package main
import ( "fmt" "log" "net/http" )
type dollars float32
func (d dollars) String() string { return fmt.Sprintf("$%.2f", d) }
type database map[string]dollars
func (db database) ServeHTTP(w http.ResponseWriter, req *http.Request) { for item, price := range db { fmt.Fprintf(w, "%s: %s\n", item, price) } }
func main() { db := database{"shoes": 20, "socks": 2} httpAddr := "localhost:8000" fmt.Printf("starting http server, listen on %s ...\n", httpAddr) log.Fatal(http.ListenAndServe(httpAddr, db)) }
|
当然,一个完整的http服务器URL肯定不止一个,我们可以继续添加一些URL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| func (db database) ServeHTTP(w http.ResponseWriter, req *http.Request) {
switch req.URL.Path { case "/list": for item, price := range db { fmt.Fprintf(w, "%s: %s\n", item, price) } case "/get": item := req.URL.Query().Get("item") price, ok := db[item] if !ok { w.WriteHeader(http.StatusNotFound) fmt.Fprintf(w, "item %s not found", item) return } fmt.Fprintf(w, "price of item %s is: %s", item, price) default: w.WriteHeader(http.StatusNotFound) fmt.Fprintf(w, "page %s not found\n", req.URL) } }
|
随着业务逻辑的增加,我们不可能在一个函数里面处理所有业务逻辑,肯定会拆分到不同的函数。http包提供了一个ServeMux,用于将不同的Handler组成一个Handler,然后作为ListenAndServe的参数。
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 29 30 31 32 33 34 35 36 37 38 39 40
| package main
import ( "fmt" "log" "net/http" )
type dollars float32
func (d dollars) String() string { return fmt.Sprintf("$%.2f", d) }
type database map[string]dollars
func (db database) list(w http.ResponseWriter, req *http.Request) { for item, price := range db { fmt.Fprintf(w, "%s: %s\n", item, price) }
} func (db database) get(w http.ResponseWriter, req *http.Request) { item := req.URL.Query().Get("item") price, ok := db[item] if !ok { w.WriteHeader(http.StatusNotFound) fmt.Fprintf(w, "item %s not found", item) return } fmt.Fprintf(w, "price of item %s is: %s", item, price) }
func main() { db := database{"shoes": 20, "socks": 2} httpAddr := "localhost:8000" mux := http.NewServeMux() mux.Handle("/list", http.HandlerFunc(db.list)) mux.Handle("/get", http.HandlerFunc(db.get)) fmt.Printf("starting http server, listen on %s ...\n", httpAddr) log.Fatal(http.ListenAndServe(httpAddr, mux)) }
|
这段代码中,我们关注以下内容:
1
| func (db database) list(w http.ResponseWriter, req *http.Request) {}
|
这仅仅是一个方法,并没有实现http.Handler接口(因为没有实现ServeHTTP接口),所以不能直接赋给mux.Handler
1
| mux.Handle("/list", http.HandlerFunc(db.list))
|
这里是做了一个类型转换,将db.list
转换成HandlerFunc
类型,因为参数和返回值都一致,所以可以转换
而HandlerFunc
类型,实现了ServeHTTP
方法,因此也就满足http.Handler接口,从而使得db.list
间接满足了http.Handler接口`
1 2 3 4 5 6 7 8
|
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) }
|
我们可以发现,HandlerFunc是一个函数类型,HandlerFunc.ServeHTTP方法调用了自身,这样就可以让某个函数成为某个接口类型。
通过这样一个间接的小技巧,就可以让database类型list、get等不同函数签名都实现http.Handler接口。
当然,mux也提供了HandleFunc方法,简化了第二个参数
1 2 3
| mux := http.NewServeMux() mux.HandleFunc("/list", db.list) mux.HandleFunc("/get", db.get)
|