这是我参与「第三届青训营 -后端场」笔记创作活动的第1篇笔记
Go语言的简介
Google出品的通用性编程语言
-
高性能、高并发
-
语法简单、学习曲线平缓
-
丰富的标准库
-
完善的工具链:编译、代码格式化、代码提示、单元测试等
-
静态链接:编译结构默认都是静态链接的,只需要拷贝编译之后的可执行文件就能部署运行,体积小,部署快捷
-
快速编译
-
跨平台:能在常见的平台运行,也能开发Android和IOS,路由器、树莓派上也能跑
-
垃圾回收
//简单实现http服务器 package main import( "net/http" ) func main() { //将路由/指向处理的文件 http.Handle("/",http.FileServer(http.Dir("."))) //增添8080端口,并启动服务器 http.ListenAndServe(":8080",nil) }
Go语言入门
开发环境
-
安装Go语言
-
相关下载网址:
-
开启go module
-
Windows
go env -w GO111MODULE=on -
Linux或macOS
export GO111MODULE=on
-
-
配置goproxy:
-
阿里云配置:
-
Windows下:
go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/ -
Linux或macOS下:
export GOPROXY=https://mirrors.aliyun.com/goproxy/
-
-
七牛云配置:
-
Windows下:
go env -w GOPROXY=https://goproxy.cn,direct -
Linux或macOS
export GOPROXY=https://goproxy.cn,direct -
goland当中直接配置也可以
-
File→setting→Go Modules
-
-
-
-
-
配置集成开发环境
- vscode
- goland
基础语法和标准库
这里设计的大多都是包的使用,就记录个别示例
-
字符串格式化
//拿结构体来举个例子,伪代码 type point struct { x, y int } p := point{1, 2} fmt.Printfln(p) //{1 2} fmt.Printf("p=%v\n",p) //{p={1 2} fmt.Printf("p=%+v\n",p)//p={x:1 y:2} fmt.Printf("p=%#v\n",p)//p=main.point{x:1, y:2} -
Json字段
Go语言实战
猜谜游戏
一个之前学C的时候做过的小游戏,逻辑比较简单,主要是有以下几点:
- bufio.NewReader()和fmt.Scanf()中读取输入的区别
- 使用math/rand包中的rand.Intn()方法之前,需要设置随机数种子,可以用时间戳设置
代码:
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
maxNum := 100
//如果不加随机数种子,那么每次生成的随机数会是同一个
//这里用启动时的时间戳来生成随机数种子
rand.Seed(time.Now().UnixNano())
secretNumber := rand.Intn(maxNum)
fmt.Println("Please input your guess")
for {
//读取一行
var guess int
_, err := fmt.Scanf("%d\r\n", &guess)
if err != nil {
fmt.Println("Invalid input. Please enter an integer value")
continue
}
fmt.Println("You guess is", guess)
//猜测逻辑
if guess > secretNumber {
fmt.Println("Your guess is bigger than the secret number. Please try again")
} else if guess < secretNumber {
fmt.Println("Your guess is smaller than the secret number. Please try again")
} else {
fmt.Println("Correct, you Legend!")
break
}
}
}
命令行词典
学习内容:
- 调用第三方的API去查询翻译
- 使用Go发送http请求
- 解析Json
- 使用代码生成提高开发效率
实现:
翻译网址:fanyi.caiyunapp.com
-
打开开发者模式(右键选择检查或者直接Chrome中F12)
-
随便翻译(为了抓包)
-
network → 找到dict请求(每个网站的请求不同,需要找到翻译的那个请求)
-
查看Headers、Preview、Payload,提取关键信息
-
这个请求比较多和复杂,自己实现会麻烦,可以通过下面方式进行一个快速生成请求
-
右击network中刚刚找到的那个dict请求,选择Copy → Copy as cURL
-
代码生成的网址curlconverter.com/#go ,打开网址选择Go之后直接复制刚刚的cURL,然后复制生成的代码(这网站目前只能够输入cURL(bash) 还不支持cURL(cmd)
-
有几个Header比较复杂,所以代码生成的时候是会发生转移错误,在这个例子中直接删掉即可,可以正常运行的(但是我这里并没有报错,可以正常运行)
代码:
package main import ( "fmt" "io/ioutil" "log" "net/http" "strings" ) func main() { client := &http.Client{} //转换成流,减少内存占用 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-CN,zh;q=0.9") req.Header.Set("Connection", "keep-alive") req.Header.Set("Content-Type", "application/json;charset=UTF-8") req.Header.Set("Origin", "https://fanyi.caiyunapp.com") req.Header.Set("Referer", "https://fanyi.caiyunapp.com/") 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/101.0.4951.54 Safari/537.36") req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi") req.Header.Set("app-name", "xy") req.Header.Set("os-type", "web") req.Header.Set("sec-ch-ua", `" Not A;Brand";v="99", "Chromium";v="101", "Google Chrome";v="101"`) req.Header.Set("sec-ch-ua-mobile", "?0") req.Header.Set("sec-ch-ua-platform", `"Windows"`) //发起请求 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) } fmt.Printf("%s\n", bodyText) }仔细看代码是可以看出标准库的强大的,还有一些要点,例如40-44行代码中先判错再defer关闭流 上面代码执行之后可以得到Response的body
-
-
创建与Response一一对应的结构体来反序列化Json,Response也比较复杂,所以还是使用代码生成
- 网址:oktools.net/json2go
- 将翻译网站的Response中的内容复制粘贴进网站中可以生成对应的结构体(记住是翻译网站的,上面程序输出的bodyText不行)
-
打印之前先把bodyText反序列化,然后打印结构体
var dictResponse DictResponse err = json.Unmarshal(bodyText, &dictResponse) if err != nil { log.Fatal(err) } fmt.Printf("%#v\n", dictResponse) -
反序列化之前需要在前面进行一个状态码的检验,找出结构体当中我们需要的内容,只打印这部分就行的
if resp.StatusCode != 200 { log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText)) }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就是需要翻译的词 query(word) } -
试验结果
-
课后作业,增加另一种翻译引擎的支持,再修改代码实现并行请求两个翻译引擎来提高响应速度
-
最后是用了金山词霸的翻译,因为有一些是Preview里面有但是Response里面没有,要么就是Response里面的字符是转码过的
-
最终实现的功能是命令行可以同时输入多个需要翻译的单词,将并行交替运用两个引擎进行查询
-
总结
本节课也是参加青训营的第一节正式课,还是收获满满的,虽然自己之前学过go语言,但是老师还是讲了很多自己以前没学到的东西,也是给了自己查漏补缺的方向。实战当中的两个小作业也是挺有意思的,尤其是第二个,之前自己只知道通过一些开发者的API去进行一个调用,这个小作业用的却是http访问去使用网页的功能,不过这个还是有一些局限性,在找其他能用的翻译网站的时候发现了一些网站要么不用json返回、要么就是返回内容是经过加密的,不过还是学到了蛮多有意思的东西。最后就是那几个宝藏网站,一个是通过cURL生产对应代码,另一个就是可以自动生成json结构体,真是太棒了。