概述
net/http
包可以处理 HTTP 协议,包括 HTTP 服务器和 HTTP 客户端。http
包主要由五个部分组成:
Request
:HTTP 请求对象Response
:HTTP 响应对象Client
:HTTP 客户端Server
:HTTP 服务器Handler
:HTTP 请求处理程序
最简单的使用
http
包提供了对应于每个 HTTP 动词的函数来发送 HTTP 请求,当不需要对请求进行详细定制时,可以直接使用它们。例如:
resp, err := http.Get("http://example.com/") // GET 请求
resp, err := http.Post("http://example.com/", "application/json", body) // POST 请求
resp, err := http.PostForm("http://example.com/", url.Values{"foo": "bar"}) // 提交表单
HTTP 请求和响应
HTTP 作为通信协议,通过报文传递信息。报文分为请求报文和响应报文,在 http
包中,分别用 Request
和 Response
对象进行抽象。
Request
可以通过 NewRequest
创建一个 Request
对象,方法声明如下,需要传入 HTTP 方法、URL 以及报文体进行初始化:
func NewRequest(method, url string, body io.Reader) (*Request, error)
Request
对象主要用于存储请求数据,其结构如下:
type Request struct {
Method string // HTTP 方法
URL *url.URL // URL
Header Header // 报文头
Body io.ReadCloser // 报文体
Host string // 主机名
// 其他字段...
}
可以看到 Request
对象可以对请求报文的各个方面进行设置,此外,Request
也提供了一些方法对这些属性进行访问和修改,详细文档可见 Request。
Response
与 Request
对象类似,Response
也是一个数据对象,拥有多个字段来描述 HTTP 响应。需要注意的是 Response
对象引用了当前的 Request
对象。Response
的声明如下:
type Response struct {
Status string // HTTP 状态 "200 OK"
StatusCode int // 状态码 200
Header Header // 响应报文头
Body io.ReadCloser // 响应报文体
ContentLength int64 // 报文长度
Request *Request // 对应的请求对象
// 其他字段...
}
Client
在第一节中的 GET
、POST
等函数是通过绑定到默认 Client
实现的。你也可以创建自己的 Client
对象。要通过 Client
发出 HTTP 请求,可以先初始化一个 Client
对象,然后发出请求。例如,下面的程序访问 Google:
package main
import (
"net/http"
"fmt"
)
func main() {
client := http.Client{}
res, err := client.Get("http://www.google.com")
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Response Status:", res.Status)
}
对于常用 HTTP 动词,Client
对象提供了对应的函数处理:
func (c *Client) Get(url string) (resp *Response, err error)
func (c *Client) Post(url, contentType string, body io.Reader) (resp *Response, err error)
func (c *Client) PostForm(url string, data url.Values) (resp *Response, err error)
但是在很多情况下,需要对报文头、Cookies 等进行定制,上述方法不能满足需求。因此,Client
对象提供了一个 Do
方法,通过传入一个 Request
对象实现请求的定制化。方法声明如下:
func (c *Client) Do(req *Request) (resp *Response, err error)
下面是一个简单的配置实例:
package main
import (
"net/http"
"fmt"
"io/ioutil"
)
func main() {
req, err := http.NewRequest(http.MethodGet, "http://www.baidu.com", nil)
if err != nil {
fmt.Println(err.Error())
return
}
req.Header.Set("Cookie", "name=foo")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := http.Client{}
res, err := client.Do(req)
if err != nil {
fmt.Println(err.Error())
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err.Error())
return
}
fmt.Println(string(body))
}
Server
http
包不仅可以发送 HTTP 请求,还可以创建 HTTP 服务器,对外提供访问服务。可以通过 ListenAndServe
方法创建一个 HTTP 服务:
package main
import (
"net/http"
"io"
"log"
)
func EchoServer(w http.ResponseWriter, req *http.Request) {
body, err := io.ReadAll(req.Body)
if err != nil {
http.Error(w, "Unable to read body", http.StatusBadRequest)
return
}
w.Write(body)
}
func main() {
http.HandleFunc("/echo/", EchoServer)
log.Fatal(http.ListenAndServe(":8080", nil))
}
在 Server 模块中,有两个重要的概念:URL
和 Handler
。前者是访问的路径,后者是对应的处理函数。Server 需要完成从 URL
到 Handler
的映射。在 http
包中的默认实现是 DefaultServeMux
,每个 Handler
需要通过 HandleFunc
进行注册。
HTTP 方法和状态码
除了上述内容外,http
包还定义了一系列常量用于表示 HTTP 动词和返回状态码,详情可见 constants。
这个版本对原内容进行了结构优化,并删除了不必要的细节,使其更加简洁易读。