初识Go(一) | 青训营笔记

125 阅读6分钟

这是我参与「第三届青训营 - 后端场」笔记创作活动的的第一篇笔记。

1. Go的基本语法

学习一个新的语言的最快的方法就是看GO的文档,简单易懂的有菜鸟教程www.runoob.com/go/go-tutor… ,还有更全面深入一些的Go语言圣经books.studygolang.com/gopl-zh/

2. 简单应用实例——在线字典

我们需要定义两个部分:1、请求部分的结构体(通过查看payload) 2、响应部分的结构体(查看preview)

2.1 代码生成

curl 是常用的命令行工具,用来请求 Web 服务器。它的名字就是客户端(client)的 URL 工具的意思。

它的功能非常强大,命令行参数多达几十种。如果熟练的话,完全可以取代 Postman 这一类的图形界面工具。

具体的用法可以用curl -help来查询。 curl-pre 从这里来获取curl的请求代码curlconverter.com/#go 对于这个一个请求他要经历四个部分:1、创建请求 2、设置请求头 3、发起请求 4、读取响应

// 创建请求 (类型, url, 数据流)
req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
if err != nil {
   log.Fatal(err)
}

// 发起请求
resp, err := client.Do(req)
if err != nil {
		log.Fatal(err)
}
defer resp.Body.Close() // 关闭respond防止资源泄露

// 读取响应
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)
    }

请求头:

通常HTTP消息包括客户机向服务器的请求消息和服务器向客户机的响应消息。客户端向服务器发送一个请求,请求头包含请求的方法、URI、协议版本、以及包含请求修饰符、客户信息和内容的类似于MIME的消息结构。服务器以一个状态行作为响应,相应的内容包括消息协议的版本,成功或者错误编码加上包含服务器信息、实体元信息以及可能的实体内容。

Http协议定义了很多与服务器交互的方法,最基本的有4种,分别是GET、POST、PUT、DELETE。一个URL地址用于描述一个网络上的资源,而HTTP中的GET、POST、PUT、 DELETE就对应着对这个资源的查、改、增、删4个操作,我们最常见的就是GET和POST了。GET一般用于获取 / 查询资源信息,而POST一般用于更新资源信息。

HTTP头信息解读

HTTP的头域包括通用头、请求头、响应头和实体头四个部分。每个头域由一个域名,冒号(:)和域值三部分组成。

  • 通用头部:是客户端和服务器都可以使用的头部,可以在客户端、服务器和其他应用程序之间提供一些非常有用的通用功能,如Date头部。
  • 请求头部:是请求报文特有的,它们为服务器提供了一些额外信息,比如客户端希望接收什么类型的数据,如Accept头部。
  • 响应头部:便于客户端提供信息,比如,客服端在与哪种类型的服务器进行交互,如Server头部。
  • 实体头部:指的是用于应对实体主体部分的头部,比如,可以用实体头部来说明实体主体部分的数据类型,如Content-Type头部。

在建立连接之前的准备工作,我们需要对要申请的数据进行预处理,符合json格式。

Json(Javascript Object Nanotation)是一种数据交换格式,常用于前后端数据传输。任意一端将数据转换成json字符串,另一端再将该字符串解析成相应的数据结构,如string类型,strcut对象等。

// 为了满足json.Marshal的传入请求
type DictRequest struct {
    TransType string `json:"trans_type"`
    Source    string `json:"source"`
    UserID    string `json:"user_id"`
}

func query(word string) {
	client := &http.Client{}
	request := DictRequest{TransType: "en2zh", Source: word}
  // Json.Marshal:将数据编码成json字符串(也称序列化),对应有json.Unmarshal。
	buf, err := json.Marshal(request)
	if err != nil {
		log.Fatal(err)
	}
	var data = bytes.NewReader(buf)
}

只要是可导出成员(变量首字母大写),都可以转成json。因成员变量sex是不可导出的,故无法转成json。

如果变量打上了json标签,如Name旁边的 json:"name" ,那么转化成的json key就用该标签“name”,否则取变量名作为key,如“Age”,“HIgh”。

bool类型也是可以直接转换为json的value值。Channel, complex 以及函数不能被编码json字符串。当然,循环的数据结构也不行,它会导致marshal陷入死循环。

指针变量,编码时自动转换为它所指向的值,如cla变量。 (当然,不传指针,Stu struct的成员Class如果换成Class struct类型,效果也是一模一样的。只不过指针更快,且能节省内存空间。)

最后,强调一句:json编码成字符串后就是纯粹的字符串了。

2.2 解析 response body

oktools.net/json2go json2go preview里面的全部都是json格式,可以通过上面的网站进行操作,将json文件直接转换成我们需要的结构体。

// req是请求,通过这段http的请求来获取
var data = bytes.NewReader(buf)
req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)
if err != nil {
	log.Fatal(err)
}

// resp是响应,HTTP Body 数据是HTTP事务消息中在heads之后(如果有的话)紧随其后发送的数据字节(对于HTTP / 0.9,不发送头)大多数请求是没有body的GET请求。大多数包含body的请求是使用POST或PUT请求方式。返回的是序列化后的json格式。
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)

// 将得到的序列化的json,反序列化变成字符串进行显示
var dictResponse DictResponse
err = json.Unmarshal(bodyText, &dictResponse)
if err != nil {
	log.Fatal(err)
}
fmt.Printf("%#v\n", dictResponse)

关于defer

函数返回的过程是这样的:先给返回值赋值,然后调用defer表达式,最后才是返回到调用函数中。

2.3 打印结果

if resp.StatusCode != 200 {
		log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))
}

进行respond的状态检验,例如url错了的话会返回404。

fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)
	for _, item := range dictResponse.Dictionary.Explanations {
		fmt.Println(item)
}

for这里是因为json文件,我们只需要打印他的value,不用知道key,除非需要显示key。

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)
}

os包以跨平台的方式,提供了一些与操作系统交互的函数和变量。程序的命令行参数可从os包的Args变量获取;os包外部使用os.Args访问该变量。

os.Args的第一个元素:os.Args[0],是命令本身的名字;其它的元素则是程序启动时传给它的参数。s[m:n]形式的切片表达式,产生从第m个元素到第n-1个元素的切片,下个例子用到的元素包含在os.Args[1:len(os.Args)]切片中。如果省略切片表达式的m或n,会默认传入0或len(s),因此前面的切片可以简写成os.Args[1:]。