Golang - net/http 笔记

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

取得结果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值