从零开始解读第二个小项目(新手看过来!!!) | 青训营

写在前面

本人准大二,之前也没有后端开发经验(本人主业是搞人工智能的),也没有学过计算机网络(408只学了数据结构),也没有go语言基础,语言只学过C++和Python。这篇文章完全是我现学现卖的,所以我会从初学者的视角讲讲这个项目,希望可以帮到和我一样的初学者。

解析正文

源代码:

jspackage 复制代码
import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

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

type DictResponse struct {
	Rc   int `json:"rc"`
	Wiki struct {
		KnownInLaguages int `json:"known_in_laguages"`
		Description     struct {
			Source string      `json:"source"`
			Target interface{} `json:"target"`
		} `json:"description"`
		ID   string `json:"id"`
		Item struct {
			Source string `json:"source"`
			Target string `json:"target"`
		} `json:"item"`
		ImageURL  string `json:"image_url"`
		IsSubject string `json:"is_subject"`
		Sitelink  string `json:"sitelink"`
	} `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"`
}

func query(word string) {
	client := &http.Client{}
	request := DictRequest{TransType: "en2zh", Source: word}
	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("Connection", "keep-alive")
	req.Header.Set("DNT", "1")
	req.Header.Set("os-version", "")
	req.Header.Set("sec-ch-ua-mobile", "?0")
	req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36")
	req.Header.Set("app-name", "xy")
	req.Header.Set("Content-Type", "application/json;charset=UTF-8")
	req.Header.Set("Accept", "application/json, text/plain, */*")
	req.Header.Set("device-id", "")
	req.Header.Set("os-type", "web")
	req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")
	req.Header.Set("Origin", "https://fanyi.caiyunapp.com")
	req.Header.Set("Sec-Fetch-Site", "cross-site")
	req.Header.Set("Sec-Fetch-Mode", "cors")
	req.Header.Set("Sec-Fetch-Dest", "empty")
	req.Header.Set("Referer", "https://fanyi.caiyunapp.com/")
	req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
	req.Header.Set("Cookie", "_ym_uid=16456948721020430059; _ym_d=1645694872")
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	bodyText, err := ioutil.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
	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)
}

首先我认为,看代码之前,搞清楚整个宏观在干什么是很有必要的。所以我先从宏观上分析一下: 代码主要包含以下几个部分:

  1. 结构体定义:定义了两个结构体DictRequestDictResponse,分别用于构造请求数据和解析响应数据。DictRequest用于构造查询请求的JSON数据,DictResponse用于解析从API接收到的JSON响应数据。
  2. query函数:该函数接收一个英文单词作为参数,通过HTTP POST请求向https://api.interpreter.caiyunai.com/v1/dict地址发送查询请求,并解析返回的JSON数据。它使用http.Client来执行HTTP请求,并将请求数据编码为JSON格式,并将响应数据解析到DictResponse结构体中。然后,它输出单词的UK和US发音,并逐行输出单词的释义。
  3. main函数:主函数从命令行参数中获取一个英文单词,然后调用query函数进行查询。

在具体实现中,query函数通过构造HTTP请求头和请求体,并调用http.ClientDo方法发送POST请求到目标API接口。然后根据HTTP响应状态码判断是否成功获取数据,并将获取到的JSON数据解析到DictResponse结构体中。

这段代码使用了标准库中的net/httpencoding/json包,以及其他一些辅助包用于读取命令行参数和输出结果。它利用第三方API(https://api.interpreter.caiyunai.com/v1/dict)来查询英文单词的释义和发音。

也许在看这段的时候会遇到很多不懂的名词。什么是API?什么是JSON?什么是HTTP请求?请求头和请求体又是什么?如何发送请求?没关系,我们一个一个来看它:

API:

API是Application Programming Interface(应用程序编程接口)的缩写,它是一组定义了不同软件组件之间交互的规则和协议。API允许不同的应用程序、服务或库之间进行通信和数据交换,使它们能够互相协作,实现特定功能。

API可以看作是应用程序之间的桥梁,通过这个桥梁,应用程序可以相互调用对方提供的功能,获取数据或执行特定操作。API定义了接口的格式和规范,包括请求的数据格式、返回的数据格式、使用的协议等,这使得应用程序的开发者能够很容易地理解和使用其他应用程序的功能。

常见的API类型包括:

  1. Web API:Web API是一种基于HTTP协议的API,通过URL和HTTP请求方法(如GET、POST、PUT、DELETE等)来调用和传递数据。常见的Web API格式包括RESTful API和SOAP API。
  2. 库API:库API是一个应用程序库或框架提供的一组函数和数据结构,其他程序可以通过调用这些函数来访问库的功能。
  3. 操作系统API:操作系统API允许应用程序与操作系统进行交互,以获取系统资源、执行文件操作、处理网络通信等。
  4. 第三方API:第三方API是由第三方服务提供商开发的API,可以用于获取第三方服务的功能和数据,例如社交媒体API、支付API等。

这个项目中用到的应该是Web API,让我们的程序能够和网站连接。

JSON:

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它是一种文本格式,用于在不同的系统之间进行数据交换。JSON最初由Douglas Crockford提出,并在ECMA-404标准中进行了规范。

缩句:JSON是格式,方便我们传递信息。然后在go语言中,JSON格式和结构体格式是可以互相转化的,通过json.Marshal来实现。

HTTP请求:

HTTP请求是客户端向服务器发送请求以获取或操作资源的过程。HTTP(Hypertext Transfer Protocol)是一种用于传输超文本的协议,是Web通信的基础。HTTP请求由客户端发起,服务器接收并响应请求。

HTTP请求由两部分组成:请求头和请求体。

  1. 请求头(Request Header):请求头包含了客户端发送给服务器的一些元信息,用于描述请求的属性和要求。常见的请求头包括:

    • HTTP方法:GET、POST、PUT、DELETE等,表示请求的类型。
    • 请求的URL:用于指定请求的资源地址。
    • Host:指定请求的目标服务器的主机名和端口号。
    • User-Agent:指定客户端的类型和版本信息。
    • Accept:指定客户端可以接收的数据类型。
    • Content-Type:指定请求体中的数据类型。
    • Authorization:用于认证客户端的身份。
    • 等等
  2. 请求体(Request Body):请求体包含了客户端发送给服务器的实际数据。它通常用于在POST、PUT等请求方法中传递表单数据、JSON数据、文件上传等内容。

发起HTTP请求的过程通常由客户端(浏览器、移动应用或其他应用程序)发起,服务器接收并处理请求,并返回相应的响应数据。客户端通过HTTP请求与服务器进行交互,请求资源、提交数据等。

在不同编程语言中,可以使用相应的HTTP库或框架来发起HTTP请求。例如,在Go语言中可以使用net/http包来发起HTTP请求,Python中可以使用requests库,JavaScript中可以使用fetchXMLHttpRequest来发送HTTP请求。

在这个项目中,是通过

js 复制代码
resp, err := client.Do(req)

来发送请求的。

resp, err := client.Do(req) 是Go语言中使用net/http包进行HTTP请求的常见用法。

在这行代码中,client 是一个 http.Client 类型的实例,用于执行HTTP请求。http.Client 是Go语言中用于发送HTTP请求的客户端对象,提供了执行GET、POST、PUT等请求的方法。

req 是一个 http.Request 类型的实例,表示一个HTTP请求。在这里,req 是之前创建的一个 POST 请求对象,包含了请求的URL、请求方法、请求头和请求体等信息。

client.Do(req) 会发送 HTTP 请求,并返回一个 *http.Response 类型的响应对象 resp 和一个 error 类型的错误信息 err

在实际应用中,通常需要检查 err 是否为 nil,以确定请求是否成功。如果请求成功,可以通过 resp 来获取响应的内容,响应状态码,以及其他相关信息。

明白了这些概念,再回去看代码,再结合整个宏观架构来看,是不是清晰了许多呢? :)

心得

1.老师在定义两个结构体DictRequestDictResponse,分别用于构造请求数据和解析响应数据的时候,用了两个工具:Convert curl commands to Go (curlconverter.com)JSON转Golang Struct - 在线工具 - OKTools。这启示我们在工作中可以利用适当的工具减少工作量。 2.对于初学者,有些不懂的库和不懂的代码,可以问ChatGPT,它会给你很详细的解答。(P.S.我这文章有些专业部分也是请教ChatGPT的。不得不说,这大大减少了我找资料的时间)

3.- 一个学习小心得:听课的时候可以边看他的PPT。他的PPT(尤其是注释)写的很详细。(bytedance.feishu.cn/file/boxcnD...

相关推荐
CallBack8 个月前
Typora+PicGo+阿里云OSS搭建个人图床,纵享丝滑!
前端·青训营笔记
Taonce1 年前
站在Android开发者的角度认识MQTT - 源码篇
android·青训营笔记
AB_IN1 年前
打开抖音会发生什么 | 青训营
青训营笔记
monster1231 年前
结营感受(go) | 青训营
青训营笔记
翼同学1 年前
实践记录:使用Bcrypt进行密码安全性保护和验证 | 青训营
青训营笔记
hu1hu_1 年前
Git 的正确使用姿势与最佳实践(1) | 青训营
青训营笔记
星曈1 年前
详解前端框架中的设计模式 | 青训营
青训营笔记
tuxiaobei1 年前
文件上传漏洞 Upload-lab 实践(中)| 青训营
青训营笔记
yibao1 年前
高质量编程与性能调优实战 | 青训营
青训营笔记
小金先生SG1 年前
阿里云对象存储OSS使用| 青训营
青训营笔记