Go 语言基础实战 — 在线词典 | 青训营笔记

121 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 6 天

上一篇我们介绍了第一个实战项目——猜谜游戏,通过不同的 5 个 Version 最终实现了整个项目。此篇我们接着来介绍第二个项目——在线词典,通过击破三个难点来实现此次实战项目。

项目介绍

用户可以在命令行里面查询一个单词,能通过调用第三方的 API (以彩云翻译为例)查询到单词的翻译并打印出来。 这个例子中,我们会学会如何使用 Go 语言来发送 HTTP请求、解析

项目难点

  • 进行抓包,对其进行分析
  • 生成请求 request body
  • 解析 response body

下面就针对以上难点,逐一击破并实现本次实战项目——在线词典。

项目实现

01. 抓包

  1. 首先打开 彩云翻译 ,右键检查打开浏览器的开发者工具(以Chorme为例)

屏幕截图 2023-01-23 125734.png

  1. 输入查询的单词,点击翻译按钮,浏览器会发送一系列请求,我们就能抓住用来查询单词的请求

    • 请求头是一个 JSON 里面包括两个字段,一个是 source 表示查询的单词,另一个是 trans_type 表示从什么语言翻译成什么语言

    载荷.png

    • API 的返回结果中有 dictionary 和 wiki 两个字段,项目所需要的结果主要在 dictionary.explanations字段里面。此外,还包括音标、例句等信息的字段。

    载荷.png

02. 生成请求代码

一般情况下,我们需要在 Golang 中去发送这个请求,过程比较复杂,使用代码构造十分麻烦。实际上有一种简单的方式来生成代码。 1. 右击浏览器里面的以 cURL(bush) 格式复制

copy as curl.png

2. 打开 代码生成网页 在 curl command 中粘贴并选择编程语言 Go ,生成的代码中有编译错误的话删除即可

代码生成.png

3. 运行生成的代码,能看到我们已经能够成功地发出请求,也打印出来返回的 JSON 串。

代码生成执行结果.png

但是目前输入是固定的,我们需要的是用户输入,即要从一个变量来输入,此时就需要用到 JSON 序列化

4. 生成 request body

在Golang里面,需要生成一段JSON,常用的方式是先构造出来一个结构体,这个结构体和需要生成的JSON的结构是一一对应

request body.png

在我们这个例子中,结构体包含三个字段。我们再定义一个变量,初始化每个结构体成员,再调用 JSON.marshaler 来得到这个序列化之后的字符串。 不同于之前是字符串,我们这里是字节数组,所以我们把 strings.NewReader 改成 tytes.NewReader 然后再构造 request body ,接不来代码不变。然后我们就能成功地进入一个变量来发HTTP请求。这一步完成之后,结果应该是完全不变的。

屏幕截图 2023-01-23 135621.png

03. 解析 response body

在 js/Python 这些脚本语言里面, body是一个字典或者 map 的结构,可以直接从里面取值。但是 Golang 是个强类型语言,这种做法并不是最佳的。 更常用的方式是和request的一样,写一个结构体,把返回的JSON反序列化到结构体里面。但是我们在浏览器里面可以看到这个 API 返回的结构非常复杂,如果要一一定义结构体字段,非常繁琐并且容易出错。

这里介绍一种简单的方式

在这个网页中 oktools.net/json2go 将JSON字符串粘贴进去,便可以生成对应的结构体。

屏幕截图 2023-01-23 135603.png

转化-展开     独立多个结构体

转化-嵌套     一个巨大的结构体,代码更加紧凑

这样我们就得到了一个 response 结构体,如下所示:

type AutoGenerated 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 []interface{} `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"`
}

运行代码,打印的使用 %#v, 这样可以让打印的结果比较容易读。

屏幕截图 2023-01-23 140430.png

此时我们离最终版本已经很近了,接下来我们修改代码来打印 response 里面的特定字段

04. 打印结果

观察返回结果的 JSON,可以看出我们所需要的结果在 dictionary.explanations 字段可以采用 for range 循环迭代它,然后参照一些词典的显示方式直接打印,同时也可以打印其他想要的东西,例如单词、音标和例句等等。

fmt.Printf("%#v\n", dictResponse)
fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
for _, item := range dictResponse.Dictionary.Explanations {
    fmt.Println(item)
}
if resp.StatusCode != 200 { 
    log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText)) 
}

05. 完善程序

现在我们的程序的输入还是写死的。我们把的主体改成一个 query 函数查询的单同作为参数传进来,然后写一个简单的 main 函数,这个main函数首先会判断命令和参数的个数是否正确,若不正确就打印错误信息并退出游戏。若正确就获取到用户输入的单词然后执行 query 函数。

屏幕截图 2023-01-23 142110.png

06. 测试程序

这样我们的项目——在线词典就算完成了,我们可以在 cmd命令行 或 终端 测试——输入命令 go run main.go employment ,执行结果如下所示:

执行结果.png

个人总结

顾名思义,实战项目就需要动手练习,如果只是纸上谈兵那仅仅是不够的,我们需要在实践中发现自己的不足,同时在实践中还会发现自己在这方面的薄弱点。与此同时,我也打算再进一步对代码进行重构,使代码更加规范。

如果笔记中有错误的地方也希望掘友们可以及时的提出纠正。