golang 使用net/http的client请求数据


通常我们直接通过 client 向 http server 发送请求时,需要注意几点:

  • 1.请求方法,GET 还是 POST 还是有不同的,POST 需要携带 请求体数据,另外两者共性的是,在请求首部处需要指定对应字段
  • 2.如果是复杂点的请求,建议还是通过 http.Client 执行,而不通过 http.Get()/http.Post() 发送请求
  • 3.请求的 server 需要注意是否是 明文、加密 的

1.简单请求

1.1 Get请求

对于这种简单请求,通常我们不用关心 content-type 传输格式是明文还是加密,直接通过 http.Get() 发送请求就可以了,下面是代码示例。

func TestSimpleGetClient(t *testing.T) {
	resp, _ := http.Get("http://172.22.22.22:8000/api/v1/task/info")
	body := resp.Body
	bodyBytes, _  := io.ReadAll(body)
	// print resp body
	fmt.Printf("%s", bodyBytes)
	// unmarshal to app
	var m map[string]interface{}
	_ = json.Unmarshal(bodyBytes, &m)
	fmt.Printf("m: %v", m)
}

http.Get()

func Get(url string) (resp *Response, err error) {
	return DefaultClient.Get(url)
}

var DefaultClient = &Client{}

可以看出,该方法实际也是基于一个预先设定的默认 client 发送 Get 请求, 传入一个 url 就可以了。

1.2 POST请求

参照 Get 简单请求,此处主要需注意按照传入参数要求给定,下面是示例。

func TestSimplePostClient(t *testing.T) {
	body := map[string]string{
		"key": "value",
	}
	bodyBytes, _ := json.Marshal(body)
	reqBody := bytes.NewReader(bodyBytes)
	
	resp, _ := http.Post("http://172.22.22.22:8000/api/v1/task/info", "application/json", reqBody)
	// read resp body
	respBody, _ := io.ReadAll(resp.Body)
	// 通常响应都是json格式,所以需要转回结构体或map
	var m map[string]interface{}
	_ = json.Unmarshal(respBody, &m)
	// 使用map
	fmt.Printf("m: %v", m)
}

http.Post()

func Post(url, contentType string, body io.Reader) (resp *Response, err error) {
	return DefaultClient.Post(url, contentType, body)
}

// DefaultClient is the default Client and is used by Get, Head, and Post.
var DefaultClient = &Client{}

request body 的转换
只要实现了 io.Reader 接口 的都可以

// io包
// Implementations must not retain p.
type Reader interface {
	Read(p []byte) (n int, err error)
}

所以我们的 request body 应该通过实现了 Reader 接口的传入,我们常用的有以下:

  • bytes
  • buffers
  • strings
  • jsonReader

比如我们有这样一个构建的 map 结构的请求体:

body := map[string]string{
		"key": "value",
}

上述的这些实现 Reader 接口的有如下方法:

jsonBody, _ := json.Marshal(body)
// 1.bytes
bytesReader := bytes.NewReader(jsonBody)

// 2.strings
stringsReader := strings.NewReader(string(jsonBody))

// 3.buffers
bufferIns := bytes.NewBuffer(make([]byte, 1024))
bufferIns.Read(jsonBody)

2.复杂请求-用 http.Client

上面的主要针对一些简单的 http 请求,如果是更复杂的请求,如我们需要指定更多的请求首部等信息,这里就需要用到自己创建的 Client 实例,下面看看示例。

2.1 Get请求

func TestGetClient(t *testing.T) {
	// 1.create client instance
	myClient := http.Client{}
	// 2.build request
	// 2.1 Get req
	getReq, _ := http.NewRequest(http.MethodGet, "http://172.22.22.22:8000/api/v1/task/info", nil)
	// build headers
	headers := map[string]string{
		"Authorization": fmt.Sprintf("Bearer %s", "12345678"),
		"Content-Type": "application/json",
	}
	// add to req
	for k, v := range headers {
		getReq.Header.Set(k, v)
	}
	// 3.client exec req
	resp, _ := myClient.Do(getReq)
	// read resp body
	respBody, _ := io.ReadAll(resp.Body)
	// 通常响应都是json格式,所以需要转回结构体或map
	var m map[string]interface{}
	_ = json.Unmarshal(respBody, &m)
	// 使用map
	fmt.Printf("m: %v", m)
}

2.2 Post请求

func TestPostClient(t *testing.T) {
	// 1.create client instance
	myClient := http.Client{}
	// 2.build request
	// 2.2 Post req
    // build body
	body := map[string]string{
		"key": "value",
	}
	bodyBytes, _ := json.Marshal(body)
	reqBody := bytes.NewReader(bodyBytes)
	postReq, _ := http.NewRequest(http.MethodPost, "http://172.22.22.22:8000/api/v1/task/info", reqBody)
	// build headers
	headers := map[string]string{
		"Authorization": fmt.Sprintf("Bearer %s", "12345678"),
		"Content-Type": "application/json",
	}
	// add to req
	for k, v := range headers {
		postReq.Header.Set(k, v)
	}
	// 3.client exec req
	resp, _ := myClient.Do(postReq)
	// read resp body
	respBody, _ := io.ReadAll(resp.Body)
	// 通常响应都是json格式,所以需要转回结构体或map
	var m map[string]interface{}
	_ = json.Unmarshal(respBody, &m)
	// 使用map
	fmt.Printf("m: %v", m)
}

3.加密请求

如果发送 https,显然上面已经不满足需求了,这时需要注意在 client 实例中指定 transport。

func TestHttpsClient(t *testing.T) {
	// 1.create client
	myCLient := http.Client{
		Transport:     &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}},
		CheckRedirect: nil,
		Jar:           nil,
		Timeout:       time.Second * 10,
	}
	// 2.这里仅发送简单请求, 如复杂请求请先构建 request
	resp, _ := myCLient.Get("http://172.22.22.22:8000/api/v1/task/info")
	body := resp.Body
	bodyBytes, _  := io.ReadAll(body)
	// print resp body
	fmt.Printf("%s", bodyBytes)
	// unmarshal to app
	var m map[string]interface{}
	_ = json.Unmarshal(bodyBytes, &m)
	fmt.Printf("m: %v", m)
}

4.multiPart示例 & postForm示例

参考:

multiPart

func newfileUploadRequest(uri string, params map[string]string, paramName, path string) (*http.Request, error) {
    file, err := os.Open(path)
    if err != nil {
        return nil, err
    }
    defer file.Close()

    body := &bytes.Buffer{}
    writer := multipart.NewWriter(body)
    part, err := writer.CreateFormFile(paramName, path)
    if err != nil {
        return nil, err
    }
    _, err = io.Copy(part, file)

    for key, val := range params {
        _ = writer.WriteField(key, val)
    }
    err = writer.Close()
    if err != nil {
        return nil, err
    }
    request, err := http.NewRequest("POST", uri, body)
    request.Header.Set("Content-Type", writer.FormDataContentType())
    return request, err
}

http.PostForm

func httpPostForm() {
    resp, err := http.PostForm("http://www.01happy.com/demo/accept.php",
        url.Values{"key": {"Value"}, "id": {"123"}})
 
    if err != nil {
        // handle error
    }
 
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        // handle error
    }
 
    fmt.Println(string(body))
 
}

更多示例:

client 简单 POST 请求

func main() {
    reqBody, _ := json.Marshal(map[string]string{"key1": "val1", "key2": "val2"})
    
    resp, _ := http.Post(":8091", "application/json", bytes.NewReader(reqBody))
    defer resp.Body.Close()
    
    respBody, _ := io.ReadAll(resp.Body)
    fmt.Printf("resp: %s", respBody)
}

代码:

package main

import (
	"bytes"
	"crypto/sha256"
	"crypto/tls"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"sync"
	"time"
)

func main()  {
	sessionID, _ := checkIn()
	fmt.Println("sessionID:", sessionID)
	if sessionID != "" {
		getArsModels(sessionID)
	}
}

var (
	username = "admin"
	deviceID = "22241dc1-a7fc-d69e-5467-xxxxxxxxxxxx"
	baseURL = "https://xxx:xxx"
	v1URL = "/xxx/v1/"
	sessionURL = baseURL + v1URL + "sessions"
	arsModelsURL = baseURL + v1URL  + fmt.Sprintf("devices/%s/xxx/xrs", deviceID)
)

var (
	once sync.Once
	client *http.Client
)

func init()  {
	getHttpClient()
}

func getHttpClient() *http.Client {
	once.Do(func() {
		client =  &http.Client{
			// 跳过证书验证
			Transport: &http.Transport{
				TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
			},
		}
	})

	return client
}

// Get
func getArsModels(sessionID string)  {
	headers := map[string]string{
		"Content-Type": "application/json",
		"Accept-Type":  "application/json",
		"User-Agent":   "QKAct-External-Client",
		"Authorization": fmt.Sprintf("basic %s", (base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("client:%s", sessionID))))),
	}

	req, err := http.NewRequest(http.MethodGet, arsModelsURL, nil)
	if err != nil {
		panic(err)
	}
	for k, v := range headers {
		req.Header.Set(k, v)
	}

	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	respBody, err := ioutil.ReadAll(resp.Body)

	var respParsed map[string]interface{}
	json.Unmarshal(respBody, &respParsed)

	fmt.Printf("resp: %s\n", respBody)
}

// Post
func checkIn() (sessionID string, err error) {
	headers := map[string]string{
		"Content-Type": "application/json", // 如果是post请求,需指定content-type
		"Accept-Type":  "application/json",
		"User-Agent":   "QKAct-External-Client",
	}

	now := time.Now().Unix()
	password := fmt.Sprintf("%x", sha256.Sum256([]byte(username)))
	combineString := fmt.Sprintf("%s%d%s", username, now, password)
	hashString := fmt.Sprintf("%x", sha256.Sum256([]byte(combineString)))
	hashPwd := base64.StdEncoding.EncodeToString([]byte(hashString))[:12]

	body := map[string]string{
		"username": username,
		"timestamp":      fmt.Sprintf("%d", now),
		"passwordHash":   hashPwd,
		"deviceType":     "1",
		"connectionType": "0",
	}

	var reqBodyRd io.Reader
	reqBody, _ := json.Marshal(body)
	reqBodyRd = bytes.NewReader(reqBody)

	req, _ := http.NewRequest(http.MethodPost, sessionURL, reqBodyRd)
	for k, v := range headers {
		req.Header.Set(k, v)
	}

	resp, err := client.Do(req)
	if err != nil {
		panic(err)
	}

	respBody, err := ioutil.ReadAll(resp.Body)

	var respParsed map[string]interface{}
	json.Unmarshal(respBody, &respParsed)

	fmt.Printf("resp: %v\n", respParsed)

	for k, v := range respParsed {
		if k == "sessionID" {
			return v.(string), nil
		}
	}

	return "", nil
}

posted on 2024-03-01 11:15  进击的davis  阅读(552)  评论(0)    收藏  举报

导航