Go青训营第二个小项目详解(小白进) | 青训营

项目要求

在控制台获取用户输入的单词,随后在控制台打印出单词、音标以及释义

需求分析

通过对项目要求的分析,可以把项目大致分为 4 个部分

  1. 从控制台获取用户输入
  2. 封装一个http请求用于查询单词,并返回请求响应
  3. 解析收到的响应,封装成对象
  4. 控制查询,输出以及退出程序的逻辑

项目实现及分析

通过之前的需求分析,可以把项目拆分成多个函数来实现,拆分成多个函数的目的是为了让函数职责分明,便于阅读和维护

我使用的结构体

我把结构体放在了另一个包中,如果想利用我的代码复现效果的小伙伴可以在main包里面直接定义需要的结构体,我把代码放在下面

Go 复制代码
// 请求参数结构体
type DictRequest struct {
	// 翻译类型
	TransType string `json:"trans_type"`
	// 待翻译的单词
	Source string `json:"source"`
}

//响应数据结构体
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      []interface{} `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"`
}

main函数

Go 复制代码
func main() {
	// 基于标准输入流创建一个缓冲读取器
	reader := bufio.NewReader(os.Stdin)
	for {
		fmt.Print("请输入要查询的单词,输入#退出程序:")
		// 从控制台获取输入
		input, err := reader.ReadString('\n')
		// 错误处理
		if err != nil {
			log.Fatal(err)
		}
		// 去除多余的\r\n
		input = strings.TrimSuffix(input, "\r\n")
		// 判断是否要退出程序
		if input == "#" {
			fmt.Println("期待您的下次使用,再见!")
			break
		}
		// 调用封装好的translate方法
		result := translate(input)
		dictionary := &result.Dictionary
		fmt.Println()
		// 打印单词以及音标
		fmt.Println(dictionary.Entry, dictionary.Prons.En)
		// 循环打印单词的释义
		for _, explanation := range dictionary.Explanations {
			fmt.Println(explanation)
		}
		fmt.Println()
	}

}

main函数里面主要负责整体的流程控制以及实现需求中的1 ,2部分,注释我也写的比较详细,这里做一些注释之外的解析,包括对用到的一些标准库进行解释

bufio是Go语言的一个标准库,它实现了缓冲 I/O 功能。它封装了 io.Readerio.Writer 接口,提供了一些有用的方法进行缓冲读写。
reader := bufio.NewReader(os.Stdin)就是使用bufio并且基于os.Stdin来创建了一个*bufio.Reader类型的变量reder,os.Stdin是操作系统的标准输入流,通常指向命令行终端的输入,这也符合我们的要求,我们就是要从命令行终端去读取用户输入的数据

这里我们通过input, err := reader.ReadString('\n')方法来获取输入,意思就是从标准控输入读取字符,只到遇到特定的字符'\n',需要注意的是,这个方法会把'\n'也读取进去 ,也就是说这里的input变量包含'\n',所以在在后面我们需要调用strings库的方法来处理input变量。我在代码里是去除了\r\n的后缀,是因为在实际测试中我发现我读取进来的'\n'之前还有一个字符'\r'

这里调用了一个自定义的方法translate,他的函数签名是这样的func translate(word string) domain.DictResponse 把要查询的单词作为入参,返回一个我定义好的结构体,包含了单词相关的查询信息,如何快速生成代码已经在课程里面有了介绍,这里不做过多讲解,接下来解析一下这个自定义的translate方法

translate函数

Go 复制代码
// 翻译单词,返回自定义响应对象
func translate(word string) domain.DictResponse {
	// 创建自定义的请求对象
	dictRequest := domain.DictRequest{TransType: "en2zh", Source: word}
	buf, err := json.Marshal(dictRequest)
	if err != nil {
		log.Fatal(err)
	}
	// 从字节数组创建 *bytes.Reader
	data := bytes.NewReader(buf)
	// 调用自定义的请求方法,获取response
	resp, err := sendHttp(data)
	// 异常处理
	if err != nil {
		log.Fatal(err)
	}
	// 读取response中的数据
	bodyText, err := io.ReadAll(resp.Body)
	// 延迟关闭流
	defer resp.Body.Close()
	// 异常处理
	if err != nil {
		log.Fatal(err)
	}
	// 反序列化成对象
	dictResponse := domain.DictResponse{}
	err = json.Unmarshal(bodyText, &dictResponse)
	// 异常处理
	if err != nil {
		log.Fatal(err)
	}
	return dictResponse
}

translate函数负责翻译单词,接收一个单词作为入参,然后返回一个对象,对象包含了单词的解析,是不是很好理解。这里的domain.DictRequest结构体也是根据老师教的代码生成的方法构造出来的,和老师讲的不同的是,我把这个结构体单独放在了一个包里面,这样是为了让main包里面的代码更简洁

翻译单词肯定涉及到http请求,因为我们需要利用别人的接口来查询单词,然后解析别人的响应,所以我们在net/http库的基础上,有针对性的封装了一个sendHttp方法,方法签名是这样的func sendHttp(body io.Reader) (*http.Response, error),传入请求的body,然后把*http.Response返回给调用者。现在translate方法调用sendHttp方法并拿到了网络请求的响应指针,再通过iojson标准库把网络响应的流解析成结构体对象domain.DictResponse,并把解析好的结构体对象返回给translate的调用者。还记得我们在main方法里面调用了translate方法吧,那里就是拿到了translate方法返回的结构体对象,然后再输出到命令行终端。sendHttp方法大部分都是由老师教的自动生成的代码改造而来,接下来解析一下httpSend的代码

sendHttp方法

Go 复制代码
// 发送封装好的http请求,并返回Response
func sendHttp(body io.Reader) (*http.Response, error) {
	// 新建客户端
	client := &http.Client{}
	// 新建请求对象
	req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", body)
	// 错误处理
	if err != nil {
		log.Fatal(err)
	}
	// 设置一堆参数
	req.Header.Set("authority", "api.interpreter.caiyunai.com")
	req.Header.Set("accept", "application/json, text/plain, */*")
	req.Header.Set("accept-language", "zh-CN,zh;q=0.9,en;q=0.8")
	req.Header.Set("app-name", "xy")
	req.Header.Set("cache-control", "no-cache")
	req.Header.Set("content-type", "application/json;charset=UTF-8")
	req.Header.Set("device-id", "34fb7922cfdb90bab1c2676d847f1ece")
	req.Header.Set("origin", "https://fanyi.caiyunapp.com")
	req.Header.Set("os-type", "web")
	req.Header.Set("os-version", "")
	req.Header.Set("pragma", "no-cache")
	req.Header.Set("referer", "https://fanyi.caiyunapp.com/")
	req.Header.Set("sec-ch-ua", `"Not/A)Brand";v="99", "Google Chrome";v="115", "Chromium";v="115"`)
	req.Header.Set("sec-ch-ua-mobile", "?0")
	req.Header.Set("sec-ch-ua-platform", `"Windows"`)
	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 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36")
	req.Header.Set("x-authorization", "token:qgemv4jr1y38jyq6vhvi")
	// 发起http请求
	resp, err := client.Do(req)
	return resp, err
}

这里面涉及到几个对象,client是类型为*http.Client的客户端对象,http请求就是由这个对象发出,所以才有下面的resp, err := client.Do(req)。那么req对象很明显就是网络请求了,req对象的类型是*http.Request。这里创建请求的时候设置了请求的方式为POST以及指定了请求的地址为https://api.interpreter.caiyunai.com/v1/dict,请求的参数为body这个入参对象。大家下面看到的一堆req.Header.Set就是在设置请求头的内容,设置完成后就可以发送请求了。

总结

这个项目涉及到很多知识点,包括io操作,使用json实现结构体对象的序列化与反序列化,net/http实现网络请求,使用strings或者bytes来实现自身和流之间的转换。

这个项目里面的代码虽然是很初级的,但是对于像我这样才开始学习Go语言的同学来说也是受益匪浅。Go语言上手很简单,但是简单的前提是我们要在实践中去使用它,代码从来都不是背出来的,而是在使用的过程中,我们加深了对其的理解,然后自然而然的就能使用代码来完成自己的需求。这个项目做下来,我对于Go语言的基础语法以及一些常见的标准库又有了更深的理解,希望我们都能在学习的道路上越走越远。

相关推荐
夭要7夜宵3 天前
Go 垃圾回收 | 豆包MarsCode AI刷题
青训营笔记
末班车4224 天前
前端框架中的设计模式 | 豆包MarsCode AI刷题
青训营笔记
VanceLLF4 天前
神奇数字组合 | 豆包MarsCode AI刷题
青训营笔记
lann5 天前
Go 程序的优化 | 豆包MarsCode AI刷题
青训营笔记
用户52281271049785 天前
性能优化与调试技巧 | 豆包MarsCode AI刷题
青训营笔记
千慌百风定乾坤7 天前
Go 语言入门指南:基础语法和常用特性解析(下) | 豆包MarsCode AI刷题
青训营笔记
FOFO7 天前
青训营笔记 | HTML语义化的案例分析: 粗略地手绘分析juejin.cn首页 | 豆包MarsCode AI 刷题
青训营笔记
滑滑滑8 天前
后端实践-优化一个已有的 Go 程序提高其性能 | 豆包MarsCode AI刷题
青训营笔记
柠檬柠檬9 天前
Go 语言入门指南:基础语法和常用特性解析 | 豆包MarsCode AI刷题
青训营笔记
用户967136399659 天前
计算最小步长丨豆包MarsCodeAI刷题
青训营笔记