这是我参与「第五届青训营 」笔记创作活动的第3天,这里复盘总结一下三个简单实战项目(猜谜游戏、在线词典、SOCKS5代理)中的在线词典
在线词典
这里我们实现一个在线单词查询功能,使用户在命令行里面查询一个单词,我们能调用第三方的API查询到单词的翻译并打印出来。学习的要点在于使用Go语言来发送HTTP请求,解析JSON信息,还将学会如何使用代码生成来提高开发效率。
以下回顾设计流程:
1.这里我们以彩云科技提供的在线翻译为例,先打开彩云翻译网页,然后打开浏览器开发者工具。当点击翻译按钮后,浏览器会发送一系列请求,我们能很轻松地找到那个用来查询单词的请求。这是一个HTTP的post的请求,请求的header的相当的复杂,有十来个。 然后请求头是一个json里面有两个字段,一个是代表你要你是从什么语言转化成什么语言,source就是你要查询的单词。API的返回结果里面会有Wiki和dictionary两个字段。我们需要用的结果主要在dictionary.Explanations字段里面。其他有些字段里面还包括音标等信息。
2.我们可以在Golang里面去模拟发送这个请求。因为这个请求比较复杂,用代码构造很麻烦,实际上我们有一种非常简单的方式来生成代码,我们可以右键浏览器里面的copy as curl。copy完成之后大家可以在终端粘贴一下curl命令,应该可以成功返回一大串json。
3.然后我们打开一个网站,粘贴curl请求,在右边的语言里面选Golang就能够看到一串很长的代码,我们直接把它copy到我们的编辑器里面。有几个header比较复杂,生成代码有转义导致的编译错误,删掉这几行即可。
我们来看一下这生成的代码,首先第12行我们创建了一个HTTP client, 创建的时候可以指定很多参数,包括比如请求的超时是否使用cookie等。接下来是构造一个HTTP请求,这是一个post请求,然后会用到HTTP .NewRequest。第一个参数是http方法POST,第二个参数是URL,最后一个参数是body,body因为可能很大,为了支持流式发送,是一个只读流。我们用了strings.NewReader来把字符串转换成一个流。这样我们就成功构造了一个HTTP request。
接下来我们需要对这个HTTP request来设置一堆header。
接下来我们把我们调用client.do request,就能得到response如果请求失败的话,那么这个error会返回nil,会打印错误并且退出进程。response有它的HTTP状态码,response header和body。body同样是一个流,在golang里面,为了避免资源泄露,你需要加一个defer来手动关闭这个流,这个defer会在这个函数运行结束之后去执行。接下来我们是用iuil.ReadAll来读取这个流,能得到整个body。我们再用print打印出来。
我们来运行生成的代码,此时我们已经能够成功地发出请求,把返回的JSON打印出来。但是现在那个输入是固定的,而我们是要从一个变量来输入,此时我们需要用到JSON序列化
3.在Golang里面。我们需要生成一段JSON,常用的方式是我们先构造出来一个结构体,这个结构体和我们需要生成的JSON的结构是一一对应的。
我们再来定义一个request变量,初始化每个结构体成员,再调用JSON.marshaler来得到这个序列化之后的字符串。不同于之前那里是个字符串,我们这里是个字节数组。所以我们把strings.newReader改成bytes点new ready然后来构造那个request上的body。接下来代码不变,然后我们就能成功地创建一个变量来发送HTTP请求。这一步完成之后,我们可以再执行一遍, 应该结果是完全不变的。
4.接下来我们要做的是把这个response body来解析出来。在js/Pyton这些脚本语言里面,body是个字典或者map的结构,可以直接从里面取值。但是golang是个强类型语言,这种做法并不是最佳实践。更常用的方式是和request的一样,写一个结构体,把返回的JSON反序列化到结构体里面。但是我们在浏览器里面可以看到这个API返回的结构非常复杂,如果要一一定义结构体字段,非常繁琐并且容易出错。
此时有一个小技巧,网上有对应的代码生成工具,我们可以打开这个网站,把json字符串粘贴进去,这样我们就能够生成对应结构体。在某些时刻,我们如果不需要对这个返回结果,做很多精细的操作,我们可以选择转换嵌套,能让生成的代码更加紧凑。
这样我们就得到了一个response结构体。接下来我们修改代码,我们先定一个response结构体的对象,然后我们用JSON.unmarshal把body反序列化到这个结构体里面,再试图打印出来
现在我们再运行一下, 这里打印的时候使用了%#V,这样可以让打印出来的结果比较容易读。我们现在离最终版本已经很近了,接下来我们需要修改代码为打印response里面的特定字段。
观察那个json可以看出我们需要的结果是在Dictionary.explanations。我们用for range循环来迭代它,然后直接打印结构,参照一些词典的显示方式,我们可以在那个前面打印出这个单词和它的音标。这里有英式音标和美式音标。
5.现在我们的程序的输入还是写死的。我们把代码的主体改成个query函数,查询的单词作为参数传递进来。然后我们写一个简单的main函数,这个main函数首先判断下命令和参数的个数,如果它不是两个,那么我们就打印出错误信息,退出程序。否则就获取到用户输入的单词,然后执行query函数。
这样我们再运行程序就能得到我们想要的效果