go语言发送http请求和响应

162 阅读11分钟

记录一下在字节青训营中学到的内容 这里用到的三个网站是彩云小译在线生成代码json转换器

首先我们想要做到在翻译网站中获取到请求和响应 在这里插入图片描述 拿到这些数据我们要学会怎么去查看这个页面的信息

json格式的代码

查看网页信息

右键选中检查 在这里插入图片描述

选中上方栏目的Network 在这里插入图片描述

选中刚刚输入good所生成的dict 在这里插入图片描述 检查一下其Headers是否是包含Post请求 在这里插入图片描述 这里的Payload中的en2zh就是英文转换成中文的意思 在这里插入图片描述 选中其中的Preview里面可以看到具体的翻译 在这里插入图片描述

获取网页的cURL

这里我们右键copy一下它的cURL,等会会用到。 这里的cURL是一个命令行工具,用于在服务器中传输数据用的 具体感兴趣的同学可以参考这个文章cURL是什么 在这里插入图片描述

代码转换

然后打开第二个网站 将刚刚cope的cURL复制到这个框内 在这里插入图片描述 底下就会生成go语言的代码将它复制粘贴到idea就好了

package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"strings"
)

func main() {
	// http.Client创建了一个http客户端实例,并把值赋给client变量用来接受和传送http响应和请求
	client := &http.Client{}
	// 这里的data用了strings.NewReader的方法将字符串转换成流,可以节省内存
	var data = strings.NewReader(`{"trans_type":"en2zh","source":"good"}`)
	// 创建请求
	req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
	if err != nil {
		log.Fatal(err)
	}
	// 设置请求头
	req.Header.Set("accept", "application/json, text/plain, */*")
	req.Header.Set("accept-language", "zh")
	req.Header.Set("app-name", "xiaoyi")
	req.Header.Set("authorization", "Bearer")
	req.Header.Set("content-type", "application/json;charset=UTF-8")
	req.Header.Set("device-id", "67e26c3ec5a2bd9d1f1a8472eed9443c")
	req.Header.Set("origin", "http://fanyi.caiyunapp.com")
	req.Header.Set("os-type", "web")
	req.Header.Set("os-version", "")
	req.Header.Set("priority", "u=1, i")
	req.Header.Set("referer", "http://fanyi.caiyunapp.com/")
	req.Header.Set("sec-ch-ua", `"Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"`)
	req.Header.Set("sec-ch-ua-mobile", "?0")
	req.Header.Set("sec-ch-ua-platform", `"Linux"`)
	req.Header.Set("sec-fetch-dest", "empty")
	req.Header.Set("sec-fetch-mode", "cors")
	req.Header.Set("sec-fetch-site", "cross-site")
	req.Header.Set("user-agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36")
	req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")
	//发起请求
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	// 这个defer会在函数结束之后从下往上触发释放这个流
	// 读取响应
	bodyText, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", bodyText)
}

这里我已经在代码中做出详细的注释了就不一一解析了

我们每次发送请求的时候应该是传递值而不是直接的json序列,所以进入下一步

传参的http代码

我们需要定义一个结构体用作传参

type DictRequest struct {
	TransType string `json:"trans_type"`
	Source    string `json:"source"`
	UserId    string `json:"user_id"`
}

实际上我们在上一段代码中并没有用到UserId,这个参数是可以省略的

以下是完整的代码

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
)

type DictRequest struct {
	TransType string `json:"trans_type"`
	Source    string `json:"source"`
	UserId    string `json:"user_id"`
}

func main() {
	// http.Client创建了一个http客户端实例,并把值赋给client变量用来接受和传送http响应和请求
	client := &http.Client{}
	request := DictRequest{TransType: "en2zh", Source: "good"}
	// 这里将结构体数据转换成json格式用来传输数据
	buf, err := json.Marshal(request)
	if err != nil {
		log.Fatal(err)
	}
	// 这里将字符串转换成流来节省内存
	var data = bytes.NewReader(buf)
	// 创建请求
	req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
	if err != nil {
		log.Fatal(err)
	}
	// 设置请求头
	req.Header.Set("accept", "application/json, text/plain, */*")
	req.Header.Set("accept-language", "zh")
	req.Header.Set("app-name", "xiaoyi")
	req.Header.Set("authorization", "Bearer")
	req.Header.Set("content-type", "application/json;charset=UTF-8")
	req.Header.Set("device-id", "67e26c3ec5a2bd9d1f1a8472eed9443c")
	req.Header.Set("origin", "http://fanyi.caiyunapp.com")
	req.Header.Set("os-type", "web")
	req.Header.Set("os-version", "")
	req.Header.Set("priority", "u=1, i")
	req.Header.Set("referer", "http://fanyi.caiyunapp.com/")
	req.Header.Set("sec-ch-ua", `"Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"`)
	req.Header.Set("sec-ch-ua-mobile", "?0")
	req.Header.Set("sec-ch-ua-platform", `"Linux"`)
	req.Header.Set("sec-fetch-dest", "empty")
	req.Header.Set("sec-fetch-mode", "cors")
	req.Header.Set("sec-fetch-site", "cross-site")
	req.Header.Set("user-agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36")
	req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")
	//发起请求
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	// 这个defer会在函数结束之后从下往上触发释放这个流
	// 读取响应
	bodyText, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%s\n", bodyText)
}

注释写的很明确了,就不赘述了

运行结果

{"rc":0,"wiki":{},"dictionary":{"prons":{"en-us":"[g\u028ad]","en":"[gud]"},"explanations":["a.\u597d\u7684;\u5584\u826f\u7684;\u5feb\u4e50\u7684;\u771f\u6b63\u7684;\u5bbd\u5927\u7684;\u6709\u76ca\u7684;\u8001\u7ec3\u7684;\u5e78\u798f\u7684;\u5fe0\u5b9e\u7684;\u4f18\u79c0\u7684;\u5b8c\u6574\u7684;\u5f7b\u5e95\u7684;\u4e30\u5bcc\u7684","n.\u5229\u76ca;\u597d\u5904;\u5584\u826f;\u597d\u4eba","ad.=well"],"synonym":["excellent","fine","nice","splendid","proper"],"antonym":["bad","wrong","evil","harmful","poor"],"wqx_example":[["to the good","\u6709\u5229,\u6709\u597d\u5904"],["good, bad and indifferent","\u597d\u7684,\u574f\u7684\u548c\u4e00\u822c\u7684"],["good innings","\u957f\u5bff"],["good and ...","\u5f88,\u9887;\u5b8c\u5168,\u5f7b\u5e95"],["do somebody's heart good","\u5bf9\u67d0\u4eba\u7684\u5fc3\u810f\u6709\u76ca,\u4f7f\u67d0\u4eba\u611f\u5230\u6109\u5feb"],["do somebody good","\u5bf9\u67d0\u4eba\u6709\u76ca"],["be good for","\u5bf9\u2026\u6709\u6548,\u9002\u5408,\u80dc\u4efb"],["be good at","\u5728\u2026\u65b9\u9762(\u5b66\u5f97,\u505a\u5f97)\u597d;\u5584\u4e8e"],["as good as one's word","\u4fe1\u5b88\u8bfa\u8a00,\u503c\u5f97\u4fe1\u8d56"],["as good as","\u5b9e\u9645\u4e0a,\u51e0\u4e4e\u7b49\u4e8e"],["all well and good","\u4e5f\u597d,\u8fd8\u597d,\u5f88\u4e0d\u9519"],["a good","\u76f8\u5f53,\u8db3\u8db3"],["He is good at figures . ","\u4ed6\u5584\u4e8e\u8ba1\u7b97\u3002"]],"entry":"good","type":"word","related":[],"source":"wenquxing"}}

接下来要做的就是解析这段bodyText

解析http响应

我们获取到的http响应是一个json字典,可以通过像python一样的字典索引的方法去做,不过最好的方法是写一个结构体来承载这个数据。 直接根据响应的response来写结构体容易写漏,我们这里可以直接copy一下。

在这里插入图片描述 打开第三个网站json转换器 将刚刚copy的内容粘贴进去 在这里插入图片描述 选中转换-嵌套 在这里插入图片描述 将这个结构体复制到代码里面就好了

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
)

type DictResponse struct {
	Rc   int `json:"rc"`
	Wiki struct {
	} `json:"wiki"`
	Dictionary struct {
		Prons struct {
			EnUs string `json:"en-us"`
			En   string `json:"en"`
		} `json:"prons"`
		Explanations []string      `json:"explanations"`
		Synonym      []string      `json:"synonym"`
		Antonym      []string      `json:"antonym"`
		WqxExample   [][]string    `json:"wqx_example"`
		Entry        string        `json:"entry"`
		Type         string        `json:"type"`
		Related      []interface{} `json:"related"`
		Source       string        `json:"source"`
	} `json:"dictionary"`
}
type DictRequest struct {
	TransType string `json:"trans_type"`
	Source    string `json:"source"`
	UserId    string `json:"user_id"`
}

func main() {
	// http.Client创建了一个http客户端实例,并把值赋给client变量用来接受和传送http响应和请求
	client := &http.Client{}
	request := DictRequest{TransType: "en2zh", Source: "good"}
	// 这里将结构体数据转换成json格式用来传输数据
	buf, err := json.Marshal(request)
	if err != nil {
		log.Fatal(err)
	}
	// 这里将字符串转换成流来节省内存
	var data = bytes.NewReader(buf)
	// 创建请求
	req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
	if err != nil {
		log.Fatal(err)
	}
	// 设置请求头
	req.Header.Set("accept", "application/json, text/plain, */*")
	req.Header.Set("accept-language", "zh")
	req.Header.Set("app-name", "xiaoyi")
	req.Header.Set("authorization", "Bearer")
	req.Header.Set("content-type", "application/json;charset=UTF-8")
	req.Header.Set("device-id", "67e26c3ec5a2bd9d1f1a8472eed9443c")
	req.Header.Set("origin", "http://fanyi.caiyunapp.com")
	req.Header.Set("os-type", "web")
	req.Header.Set("os-version", "")
	req.Header.Set("priority", "u=1, i")
	req.Header.Set("referer", "http://fanyi.caiyunapp.com/")
	req.Header.Set("sec-ch-ua", `"Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"`)
	req.Header.Set("sec-ch-ua-mobile", "?0")
	req.Header.Set("sec-ch-ua-platform", `"Linux"`)
	req.Header.Set("sec-fetch-dest", "empty")
	req.Header.Set("sec-fetch-mode", "cors")
	req.Header.Set("sec-fetch-site", "cross-site")
	req.Header.Set("user-agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36")
	req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")
	//发起请求
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	// 这个defer会在函数结束之后从下往上触发释放这个流
	// 读取响应
	bodyText, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	var dictResponse DictResponse
	// 这里将json反序列化传入到dictResponse中
	err = json.Unmarshal(bodyText, &dictResponse)
	if err != nil {
		log.Fatal(err)
	}
	// 在这里的%#v会用最详细的方式来打印结构体
	fmt.Printf("%#v\n", dictResponse)
}

打印出来的结果是这样的

main.DictResponse{Rc:0, Wiki:struct {}{}, Dictionary:struct { Prons struct { EnUs string "json:\"en-us\""; En string "json:\"en\"" } "json:\"prons\""; Explanations []string "json:\"explanations\""; Synonym []string "json:\"synonym\""; Antonym []string "json:\"antonym\""; WqxExample [][]string "json:\"wqx_example\""; Entry string "json:\"entry\""; Type string "json:\"type\""; Related []interface {} "json:\"related\""; Source string "json:\"source\"" }{Prons:struct { EnUs string "json:\"en-us\""; En string "json:\"en\"" }{EnUs:"[gʊd]", En:"[gud]"}, Explanations:[]string{"a.好的;善良的;快乐的;真正的;宽大的;有益的;老练的;幸福的;忠实的;优秀的;完整的;彻底"}, Synonym:[]string{"excellent", "fine", "nice", "splendid", "proper"}, Antonym:[]string{"bad", "wrong", "evil", "harmful", "poor"}, WqxExample:[][]string{[]string{"to the good", "有利,有好处"}, []string{"good, bad andfferent", "好的,坏的和一般的"}, []string{"good innings", "长寿"}, []string{"good and ...", "很,颇;完全,彻底"}, []string{"do somebody's heart good", "对某人的心脏有益,使某人感到愉快"}, []string{"do somebody good", "对某人有效,适合,胜任"}, []string{"be good at", "在…方面(学得,做得)好;善于"}, []string{"as good as one's word", "信守诺言,值得信赖"}, []string{"as good as", "实际上,几乎等于"}, []string{"all well and good", "也好,还好,很不错"}g{"He is good at figures . ", "他善于计算。"}}, Entry:"good", Type:"word", Related:[]interface {}{}, Source:"wenquxing"}}

最后格式化一下输出

我们看到输出这么一长串数据并不是我们都需要的,我们可以格式化一下输出 这里我们输出的格式是单词,英式发音,美式发音,和中文意思i

fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
	for _, item := range dictResponse.Dictionary.Explanations {
		fmt.Println(item)
	}

完善代码

加一段卫式编程 刚刚的代码中获取response的时候,如果获取到数据就会进行打印,如果不是200,数据只是为空而已,所以我们这里加一段判断是否是正确响应

if resp.StatusCode != 200 {
		log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
	}

以下是完整代码

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
)

type DictResponse struct {
	Rc   int `json:"rc"`
	Wiki struct {
	} `json:"wiki"`
	Dictionary struct {
		Prons struct {
			EnUs string `json:"en-us"`
			En   string `json:"en"`
		} `json:"prons"`
		Explanations []string      `json:"explanations"`
		Synonym      []string      `json:"synonym"`
		Antonym      []string      `json:"antonym"`
		WqxExample   [][]string    `json:"wqx_example"`
		Entry        string        `json:"entry"`
		Type         string        `json:"type"`
		Related      []interface{} `json:"related"`
		Source       string        `json:"source"`
	} `json:"dictionary"`
}
type DictRequest struct {
	TransType string `json:"trans_type"`
	Source    string `json:"source"`
	UserId    string `json:"user_id"`
}

func query(word string) {
	// http.Client创建了一个http客户端实例,并把值赋给client变量用来接受和传送http响应和请求
	client := &http.Client{}
	request := DictRequest{TransType: "en2zh", Source: word}
	// 这里将结构体数据转换成json格式用来传输数据
	buf, err := json.Marshal(request)
	if err != nil {
		log.Fatal(err)
	}
	// 这里将字符串转换成流来节省内存
	var data = bytes.NewReader(buf)
	// 创建请求
	req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
	if err != nil {
		log.Fatal(err)
	}
	// 设置请求头
	req.Header.Set("accept", "application/json, text/plain, */*")
	req.Header.Set("accept-language", "zh")
	req.Header.Set("app-name", "xiaoyi")
	req.Header.Set("authorization", "Bearer")
	req.Header.Set("content-type", "application/json;charset=UTF-8")
	req.Header.Set("device-id", "67e26c3ec5a2bd9d1f1a8472eed9443c")
	req.Header.Set("origin", "http://fanyi.caiyunapp.com")
	req.Header.Set("os-type", "web")
	req.Header.Set("os-version", "")
	req.Header.Set("priority", "u=1, i")
	req.Header.Set("referer", "http://fanyi.caiyunapp.com/")
	req.Header.Set("sec-ch-ua", `"Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"`)
	req.Header.Set("sec-ch-ua-mobile", "?0")
	req.Header.Set("sec-ch-ua-platform", `"Linux"`)
	req.Header.Set("sec-fetch-dest", "empty")
	req.Header.Set("sec-fetch-mode", "cors")
	req.Header.Set("sec-fetch-site", "cross-site")
	req.Header.Set("user-agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36")
	req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")
	//发起请求
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	// 这个defer会在函数结束之后从下往上触发释放这个流
	// 读取响应
	bodyText, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	if resp.StatusCode != 200 {
		log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
	}
	var dictResponse DictResponse
	// 这里将json反序列化传入到dictResponse中
	err = json.Unmarshal(bodyText, &dictResponse)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
	for _, item := range dictResponse.Dictionary.Explanations {
		fmt.Println(item)
	}
}
func main() {
	if len(os.Args) < 2 {
		fmt.Fprintf(os.Stderr, `usage:simpleDict WORD example:simpleDict hello`)
		os.Exit(1)
	}
	word := os.Args[1]
	query(word)
}

这里具体解释一下main()这里的代码,这里的os.Args是字符串切片,包含了命令行传入的参数

  • 其中os.Arag[0]是程序的名称
  • os.Args[1]是第一个参数

如果长度小于2即代表没有传入参数 返回状态码数1表示异常退出 然后赋值给word最后调用刚刚写的方法执行

下面是运行结果

$ go run main.go love
love UK: [lʌv] US: [lʌv]
n.爱;喜爱;爱情
[俗]漂亮的东西;嗜好;(网球)零分;性欲
vt.求爱;热爱;茂盛;需要
vi.喜爱;热爱;出于怜悯;不奢求报酬