Life of an HTTP Request

resp, err := http.Get("<http://example.com/>")
...
resp, err := http.Post("<http://example.com/upload>", "image/jpeg", &buf)
...
resp, err := http.PostForm("<http://example.com/form>",
	url.Values{"key": {"Value"}, "id": {"123"}})

if err != nil {
	// handle error
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
// ...

For control over HTTP client headers, redirect policy ... etc, create a Client:

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)
// ...

http.Request

type Request struct {
	...
	URL *url.URL
	...
	Header Header
	
	// Body must allow Read to be called concurrently with Close.
	// In particular, calling Close should unblock a Read waiting
	// for input.
	Body io.ReadCloser
	...
}

大多数 http 框架都是这样实现的,Body 这样的 流 Stream 数据只读一次,是因为持有的缓冲区的指针都是往前读的,如果一直持有缓冲区而不释放会出问题,可以想象一下,假如可以多次重复读,那么用户连接所产生的的内存占用的缓冲区有多大呢?什么时候释放呢?

在实际开发中,响应主体持有的资源可能会很大,所以并不会将其直接保存在内存中,只是持有数据流连接。当我们需要时,才会从服务器获取数据并返回。同时,考虑到应用重复读取数据的可能性很小,所以将其设计为一次性流(one-shot) 即“读取后立即关闭并释放资源”。

URL

type URL struct {
	Scheme      string
	Opaque      string    // encoded opaque data
	User        *Userinfo // username and password information
	Host        string    // host or host:port
	Path        string    // path (relative paths may omit leading slash)
	RawPath     string    // encoded path hint (see EscapedPath method)
	ForceQuery  bool      // append a query ('?') even if RawQuery is empty
	RawQuery    string    // encoded query values, without '?'
	Fragment    string    // fragment for references, without '#'
	RawFragment string    // encoded fragment hint (see EscapedFragment method)
}

type Values map[string][]string

func (u *URL) Query() Values

func main() {
	u, err := url.Parse("<http://bing.com/search?q=dotnet>")
	if err != nil {
		log.Fatal(err)
	}
	u.Scheme = "https"
	u.Host = "google.com"
	q := u.Query()
	q.Set("q", "golang")
	u.RawQuery = q.Encode()
	fmt.Println(u) // <https://google.com/search?q=golang>
}

[scheme:][//[userinfo@]host][/]path[?query][#fragment]

http.Response

What's in an HTTP response?

  1. an HTTP status code
  2. HTTP response headers
  3. optional HTTP body

Golang 中客户端请求信息都封装到 Request 结构体,但发送给客户端的是 ResponseWriter 接口,用来处理 HTTP 响应

type ResponseWriter interface {
	Header() Header
	Write([]byte)(int, error)
	WriteHeader(statusCode int)
}

func Home(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "Welcome to my blog site")
}