Server
package main
import (
"fmt"
"log"
"net/http"
)
// 模拟实现 Handler 接口
type Bar struct {}
func (b Bar) ServeHTTP(w http.ResponseWriter, req *http.Request) {
tgt := req.URL.Query().Get("name")
fmt.Fprintf(w, "Hi %v", tgt)
}
func main() {
// 创建路由, 方式 1: Handle(), 参数是 Handle 接口
http.Handle("/bar", Bar{})
// 创建路由, 方式 2: HandleFunc(), 参数是满足要求的 func()
http.HandleFunc("/foo", func(w http.ResponseWriter, req *http.Request) {
tgt := req.URL.Query().Get("name")
fmt.Fprintf(w, "Hello %v", tgt)
})
// 启动服务, log.Fatal() 输出错误信息并退出
log.Fatal(http.ListenAndServe(":9999", nil))
}
Client
发起请求
GET
直接请求
// 发起请求
rep, err := http.Get("http://127.0.0.1:9998/sayhello")
// 这里做一个处理 err 的示例, 文档中其他位置都忽略错误处理
if err != nil {
log.fatel(err)
}
// 这个很重要, 回收资源
defer rep.Body.Close()
自定义请求
// client
c := http.DefaultClient // 实际实现 c := &http.Client{}
// request
req, _ := http.NewRequest("GET", "http://127.0.0.1:9998/x", nil)
// 加header
req.Header.Add("token", "xxx") // Add, 如果变量不存在则设置, 如果存在则不设置
req.Header.Set("Content-Type", "application/x-www-form-urlencoded") // Set, 如果变量存在则设置, 不存在则不设置
// 加参数
req.URL.RawQuery = url.Values{
"key": {"v"},
}.Encode()
// client 发送 request, 并且拿到返回
rep := c.Do(req)
defer rep.Body.Close() // 回收资源
补充
两种 header 的设置方法
// 1. req.Header.Add(k, v)
req.Header.Add("token", "xxx")
// 2. 因为 Header 类型为 map[string][]string, 可以定义一个同类型的 Header 数据, 然后赋值
req.Header = map[string][]string{
"token": {"xxx"},
"num": {"1", "2", "3"},
}
两种参数的设置方法
// 1. req.URL.RawQuery = url.Values{}, 虽然 url.Values 类型也是 map[string][]string, 但是直接使用 map 则没有 Encode 方法, 不自找麻烦了
req.URL.RawQuery = url.Values{
"key": {"v"},
}.Encode()
// 2. 使用 Sprinf 将参数拼接到 url 中
newURL := fmt.Sprintf("https://%v/$v?%v=$v", url, uri, k, v)
POST
直接请求
// http.Post 只能设置 url, contentType 和 body, 如果需要设置 header, 需要 http.DefaultClient
rep, err := http.Post("http://127.0.0.1:9998/y", "application/json", strings.NewReader("name=Tim"))
if err != nil {
log.fatal(err)
}
defer rep.Body.Close()
自定义请求
// client
c := http.DefaultClient // 实际实现 c := &http.Client{}
// 模拟一个数据类型, 先进行序列化, 再将 []byte 转为 io.Reader
data := []int{1, 2, 3}
marshal, _ := json.Marshal(data)
body := bytes.NewReader(marshal)
// request
req, _ := http.NewRequest("POST", "http://127.0.0.1:9998/x", body)
// 加header
req.Header.Add("token", "xxx") // 如果变量不存在则设置, 如果存在则不设置
// client 发送 request
rep := c.Do(req)
defer rep.Body.Close()
补充
两种 io.Reader 的获取方法(造 body)
// 1. []byte -> io.Reader, 需要先将 go 的数据类型序列化为 []byte
data := []int{1, 2, 3}
marshal, _ := json.Marshal(data)
body := bytes.NewReader(marshal)
// 2. string -> io.Reader
strings.NewReader("name=Tim"))
读取 response 数据
读所有
all, _ := ioutil.ReadAll(rep.Body)
根据字节分片读
r := make([]byte, 0, 1024) // 结果
buf := make([]byte, 0, 128) // 缓存
// 一直读, 读到出现 EOF 为止
for {
// n 是读取的字节个数
n, err := rep.Body.Read(buf)
// 如果是 EOF 错误, n 为末尾的字节位置, 将 buf 切片并追加到 r 中, 返回
if err == io.EOF {
r = append(r, buf[:n]...)
break
}
// 如果有报错且报错不是 EOF, 则退出
if err != nil && err != io.EOF {
log.Println(err)
break
}
// 为出现 EOF, 则将 buf 内所有内容全部追加到 r
r = append(r, buf[:]...)
}
// 打印结果
fmt.Println(string(r))
取得结果
- 强转
// []byte --> string
string(body)
-
反序列化:
json --> struct or map
-
第三方包解析:
从 json 直接取值