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即可。