HTTP基础
- HTTP是一种无状态协议,服务器不会维护没个请求的状态,而是通过多种方式跟踪其状态,这些方式可能包括会话标识符,cookie,HTTP标头等。客户端和服务器有责任正确协商和验证状态。
- 客户端和服务器之间的通信可以同步或异步进行,但它们需要以请求或相应的方式循环运行。可以在请求中添加几个标头,以影响服务器的行为并创建可用的Web应用程序。最常见的是服务器托管Web浏览器的渲染的文件,以生成数据的图形化、组织化和时尚化的表示形式。API通常使用XML、JSON或MSGRPC进行通信,某些情况下,检索到的数据可能是二进制形式,表示要下载的任意文件类型。
- Go中包含许多便捷函数,使你可以快速轻松的构建HTTP请求并将其发宋到服务器,然后检索和处理相应。
调用HTTP API
GO的net/http标准包包含多个便捷函数,可以便捷地发送POST、GET和HEAD请求。
GET(url string) (resp *Response, err errror)
Head(url string) (resp *Response, err error)
Post(url string, bodyType string, body io.Reader) (resp *Response, err error)
相较于GET和HEAD请求,POST请求需要两个额外的参数,一个是bodyType,用来接收请求正文中的Content-Type HTTP标头(通常为application/x-www-form-urlencoded),而io.Reader我们在之前已经说过。
package main
import (
"net/http"
"net/url"
"strings"
)
func main() {
r1, _ := http.Get("https://www.haochen1204.com")
// 读取相应正文,未显示
defer r1.Body.Close()
r2, _ := http.Head("https://www.haochen1204.com")
// 读取相应正文,未显示
defer r2.Body.Close()
form := url.Values{}
form.Add("foo", "bar")
r3, _ := http.Post(
"https://www.haochen1204.com",
"application/x-www-form-urlencoded",
strings.NewReader(form.Encode()),
)
// 读取相应正文,未显示
defer r3.Body.Close()
}
Go中还有一个可以快速发送POST请求的函数,PostForm()。我们如果使用该函数,那么就无法设置这些值和手动编码每个请求。
func PostForm(url string, data url.Values) (resp *Response, err error)
使用PostForm函数进行POST请求:
package main
import (
"net/http"
"net/url"
)
func main() {
form := url.Values{}
form.Add("foo", "bar")
r3, _ := http.PostForm("https://www.haochen1204.com", form)
defer r3.Body.Close()
}
生成一个请求
除去上面的方法,我们还可以使用NewRequest()函数来创建一个请求的包,然后将其发送给我们的服务器。
func NewRequest(method, url string, body io.Reader) (req *Request, err error)
其需要3个参数,首先是我们要请求的方法,其次是我们要请求的目标,最后通过io.Reader作为最后一个参数来提供请求正文。但是这样仅仅是创建了一个结构体,我们如何将其发送给我们的服务器呢,我们这里就需要另一个东西,http.Client,他存在一个方法Do()可以将我们刚刚生成好的HTTP请求发送给我们的服务器。
package main
import (
"net/http"
"net/url"
"strings"
)
func main() {
form := url.Values{}
form.Add("foo", "bar")
var client http.Client
req, _ := http.NewRequest(
"POST",
"https://www.haochen1204.com",
strings.NewReader(form.Encode()),
)
resp, _ := client.Do(req)
defer resp.Body.Close()
}
使用结构化解析响应
在Go中,我们可以使用ioutil.ReadAll()函数从响应正文读取数据,进行一些错误检查,并将HTTP状态代码和响应正文打印到stdout中。
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
)
func main() {
resp, err := http.Get("https://www.haochen1204.com")
if err != nil {
log.Panicln(err)
}
// 打印HTTP状态码
fmt.Println(resp.Status)
// 读取并显示响应正文
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Panicln(err)
}
fmt.Println(string(body))
resp.Body.Close()
}
如上代码,我们在该断代码中首先发送了一个get请求,然后将响应包赋值给参数resp,之后通过Status参数来获取响应码,通过ioutil.ReadAll对Body参数进行处理,进而得倒了响应包。
而我们需要额外知道的是,我们通过Status获取的并不仅仅是一个响应码的整数,而是包括了其后面的OK,我们如果想仅仅获取响应码,应该使用的是StatusCode参数。而后我们的Body参数,其实际上是一个io,.ReadCloser类型,用来充当io.,Reader以及io.Close的接口,或者是实现Close()函数以关闭reader并执行任何清理的接口。从io.ReadCloser读取数据后,需要在响应正文上调用Close()函数来进行关闭。
而对于POST请求,我们经常需要使用其来获取一些json的响应,所以我们如何处理json的响应呢?假设我们现在json响应如下
{"Message":"All is good with the world","Status":"Success"}
首先,如图,我们定义了一个名为Status的结构体,其中包含服务器响应中json的元素。然后我们首先使用http.Post的方法来发送我们的Post请求,然后使用json.NewDecoder函数来解析我们的响应体中的json数据,然后通过我们上述定义的结构体中的Message和Status来获取返回中json的值。
package main
import (
"encoding/json"
"log"
"net/http"
)
type Status struct {
Message string
Status string
}
func main() {
res, err := http.Post(
"http://ip:port/ping",
"application/json",
nil,
)
if err != nil {
log.Fatalln(err)
}
var status Status
if err := json.NewDecoder(res.Body).Decode(&status); err != nil {
log.Fatalln(err)
}
defer res.Body.Close()
log.Panicf("%s -> %s\n", status.Status, status.Message)
}
其实总体来说,他和python中解析json的响应体步骤是基本一致的,首先通过请求来获取响应包,然后将响应包交给对应的json处理函数来进行处理并切得到结果,只不过在python中会将json数据直接转换为字典,而在go中需要以结构体的形式来获取其内容。当然,不仅仅是json,其他编码格式比如XML或二进制的表示形式也适用于这种方法。