Go Web 服务器 http.Handler 接口
Go做web服务器,只需要寥寥几行代码即可实现。前边已经写过web服务器几个基本步骤,可以参考webserver工作原理。
socket()->bind()->listen()->accept()
在http包种,go提供了一个函数
// The handler is typically nil, in which case the DefaultServeMux is used
func ListenAndServe(addr string, handler Handler) error
其中Handler
是一个接口类型,须提供ServeHTTP
方法
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
举例
假定一个map充当一个数据库,实现一个查询球员所在球队的功能。
package main
import (
"fmt"
"net/http"
)
func main() {
p := player{
"James": "Lakers",
"Wade": "Heats",
"Durant": "Nets",
"Curry": "Warriors",
}
_ = http.ListenAndServe(":8711", p)
}
type player map[string]string
func (p player) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
switch req.URL.Path {
case "/list":
for name, team := range p {
fmt.Fprintf(resp, "name = %s, team = %s\n", name, team)
}
case "/query":
name := req.URL.Query().Get("name")
team, ok := p[name]
if !ok {
resp.WriteHeader(http.StatusNotFound)
fmt.Fprintf(resp, "%s not found", name)
return
}
fmt.Fprintf(resp, "player = %s, team = %s", name, team)
default:
resp.WriteHeader(http.StatusNotFound)
fmt.Fprintf(resp, "path %s not support", req.URL.Path)
}
return
}
player类型实现了ServeHTTP
方法,ListenAndServe
第二个参数传入了这个player变量。
把/list和/query各个处理放到单独的函数中去,更合适一些。net/http包也为此提供了请求多工转发器ServeMux
,用来简化URL和程序处理之间的关联。
优化如下:
func main() {
p := player{
"James": "Lakers",
"Wade": "Heats",
"Durant": "Nets",
"Curry": "Warriors",
}
mux := http.NewServeMux()
mux.Handle("/list", http.HandlerFunc(p.list))
mux.Handle("/query", http.HandlerFunc(p.query))
_ = http.ListenAndServe(":8711", mux)
}
type player map[string]string
func (p player) list(resp http.ResponseWriter, req *http.Request) {
for name, team := range p {
fmt.Fprintf(resp, "name = %s, team = %s\n", name, team)
}
}
func (p player) query(resp http.ResponseWriter, req *http.Request) {
name := req.URL.Query().Get("name")
team, ok := p[name]
if !ok {
resp.WriteHeader(http.StatusNotFound)
fmt.Fprintf(resp, "%s not found", name)
return
}
fmt.Fprintf(resp, "player = %s, team = %s", name, team)
}
http.NewServeMux()
实现了ServeHTTP
方法,需要注意的是http.HandlerFunc(p.list)
,是类型强转,并不是函数调用。
// HandlerFunc类型是个适配器,它实现了ServeHTTP方法,并直接调用本身
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
为了简化http.NewServeMux()
这种写法,net/http提供了包级别变量DefaultServeMux
,包内部实现和这样实现是一样的。
包net/http DefaultServeMux内部实现:
// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
// 包内函数调用
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
// *ServeMux 方法
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
mux.Handle(pattern, HandlerFunc(handler))
}
简化后实现
http.HandleFunc("/list", p.list)
http.HandleFunc("/query", p.query)
_ = http.ListenAndServe(":8711", nil)
ListenAndServe()
第二个参数nil即可。