net/http
辅助方法
CanonicalHeaderKey
与textproto.CanonicalMIMEHeaderKey效果相同。
Handler 和 HandlerFunc
Handler是一个interface:
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}
HandlerFunc是一类函数,它的参数列表与Handler的ServeHTTP方法的参数列表相同:
type HandlerFunc func(ResponseWriter, *Request)
并且它实现了ServeHTTP方法:
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}
因此,HandlerFunc的主要作用就是将一个普通函数包装成一个Handler:
var f = func(w http.ResponseWriter, r *http.Request) {}
var g interface{} = http.HandlerFunc(f)
_, ok := g.(http.Handler)
fmt.Println(ok) // true
Cookie 和 CookieJar
// This implementation is done according to RFC 6265:
//
//    http://tools.ietf.org/html/rfc6265
// A Cookie represents an HTTP cookie as sent in the Set-Cookie header of an
// HTTP response or the Cookie header of an HTTP request.
type Cookie struct {
    Name       string
    Value      string
    Path       string
    Domain     string
    Expires    time.Time
    RawExpires string
    // MaxAge=0 means no 'Max-Age' attribute specified.
    // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
    // MaxAge>0 means Max-Age attribute present and given in seconds
    MaxAge   int
    Secure   bool
    HttpOnly bool
    Raw      string
    Unparsed []string // Raw text of unparsed attribute-value pairs
}
例如:
c := http.Cookie{
    Name:   "token",
    Value:  "abcd1234",
    Path:   "/",
    MaxAge: 3600,
}
fmt.Println(c.String()) // "token=abcd1234; Path=/; Max-Age=3600"
CookieJar是一个接口:
// A CookieJar manages storage and use of cookies in HTTP requests.
//
// Implementations of CookieJar must be safe for concurrent use by multiple
// goroutines.
//
// The net/http/cookiejar package provides a CookieJar implementation.
type CookieJar interface {
    // SetCookies handles the receipt of the cookies in a reply for the
    // given URL.  It may or may not choose to save the cookies, depending
    // on the jar's policy and implementation.
    SetCookies(u *url.URL, cookies []*Cookie)
    // Cookies returns the cookies to send in a request for the given URL.
    // It is up to the implementation to honor the standard cookie use
    // restrictions such as in RFC 6265.
    Cookies(u *url.URL) []*Cookie
}
net/http/cookiejar提供了一个具体的实现。
Header
与textproto.MIMEHeader类似,本质上也是一个map[string][]string。同样拥有Add,Set,Get,Del方法,此外还有Write和WriteSubset方法。
h := http.Header{}
h.Set("Content-Type", "application/json")
h.Add("Accept-Encoding", "gzip")
h.Add("Accept-Encoding", "deflate")
h.Set("Cache-Control", "no-cache")
h.Write(os.Stdout)
// Accept-Encoding: gzip
// Accept-Encoding: deflate
// Cache-Control: no-cache
// Content-Type: application/json
h.WriteSubset(os.Stdout, map[string]bool{
    "Accept-Encoding": true,
})
// Cache-Control: no-cache
// Content-Type: application/json
Request
Request是对HTTP的封装,包括作为服务器接收到的请求以及作为客户端发出的请求。相关的字段可以参看http.Request。
作为客户端发出的请求时,有以下方法:
- AddCookie(c *Cookie)
- SetBasicAuth(username, password string)
作为服务器接收的请求时,有以下方法:
- BasicAuth() (username, password string, ok bool)
- Cookie(name string) (*Cookie, error)
- Cookies() []*Cookie
- FormFile(key string) (multipart.File, *multipart.FileHeader, error)
- FormValue(key string) string
- PostFormValue(key string) string
- Referer() string
- UserAgent() string
为了获取表单的参数值,需要先调用ParseForm或者ParseMultipartForm,例如:
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    fmt.Println("UserAgent:", r.UserAgent())
    fmt.Println(r.Form)
    fmt.Println(r.PostForm)
    w.Write([]byte("hello world"))
})
http.ListenAndServe(":8080", nil)
执行:
curl -X POST -d "hello=world" "localhost:8080/?a=b&c=d"
输出为:
UserAgent: curl/7.51.0
map[hello:[world] a:[b] c:[d]]
map[hello:[world]]
Response
当作为客户端发请求时,服务器的返回就是一个Response。具体字段可以参看http.Response。
以下几个HTTP方法都可以得到一个Response:
- Get(url string) (resp *Response, err error)
- Head(url string) (resp *Response, err error)
- Post(url string, contentType string, body io.Reader) (resp *Response, err error)
- PostForm(url string, data url.Values) (resp *Response, err error)
例如:
resp, err := http.Get("https://httpbin.org/user-agent")
if err != nil {
    fmt.Println(err)
    return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
// { "user-agent": "Go-http-client/1.1" }
Client
Client作为客户端发请求。有如下方法:
- func (c *Client) Do(req *Request) (*Response, error)
- func (c *Client) Get(url string) (resp *Response, err error)
- func (c *Client) Head(url string) (resp *Response, err error)
- func (c *Client) Post(url string, contentType string, body io.Reader) (resp *Response, err error)
- func (c *Client) PostForm(url string, data url.Values) (resp *Response, err error)
事实上,Get,Head,Post,PostForm都是构建好Request对象后,调用Do方法。
有一个全局的DefaultClient对象,全局的Get,Head,Post,PostForm其实是调用了DefaultClient的相应方法。
如果只是发送简单的请求,直接用http.Get等这些方法就可以了,如果需要定制header等行为,则需要显式使用client.Do(req),例如:
client := &http.Client{
    CheckRedirect: redirectPolicyFunc,
}
resp, err := client.Get("http://example.com")
// ...
req, err := http.NewRequest("GET", "http://example.com", nil)
// ...
req.Header.Add("If-None-Match", `W/"wyzzy"`)
resp, err := client.Do(req)
// ...
Transport
Transport是比Client更底层的东西,如果需要控制proxy等更具体的行为,需要使用Transport,例如:
tr := &http.Transport{
    MaxIdleConns:       10,
    IdleConnTimeout:    30 * time.Second,
    DisableCompression: true,
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://example.com")
ResponseWriter
当作为服务器时,接收到请求后会进行处理,然后发送回复。ResponseWriter是一个接口,就是对服务器回复的抽象。
type ResponseWriter interface {
    Header() Header
    Write([]byte) (int, error)
    WriteHeader(int)
}
ServeMux
可以认为ServeMux是一个简单的路由器,当接收到请求时,它会将请求分发给相应的Handler来处理。
通过NewServeMux()可以创建一个新的ServeMux。有一个全局的默认ServeMux:DefaultServeMux。
有如下方法:
- Handle(pattern string, handler Handler):注册Handler
- HandleFunc(pattern string, handler func(ResponseWriter, *Request)):注册处理函数
- Handler(r *Request) (h Handler, pattern string):返回响应request对象应该使用的Handler
- ServeHTTP(w ResponseWriter, r *Request):实现了该方法,因此ServeMux本身也是Handler,该方法先调用Handler(r *Request)得到应当使用的Handler,然后调用该Handler的ServeHTTP方法
全局的Handle和HandleFunc方法分别调用了DefaultServeMux的相应方法。