网络请求、JSON解析 ~ 够浪|Go主题月

394 阅读4分钟

说明

系统提供服务能力离不开网络,数据通过网络传输离不开数据的编码和解码

网络请求

go 中网络请求

和其他语言一样,go 语言也自带了网络请求库 net/http

使用 net/http

GET 请求

func main() {

  // 请求地址
	const url = "https://api.apiopen.top/getJoke?page=1&count=2&type=video"

	res, err := http.Get(url)
	if err != nil {
		fmt.Println(err)
		return
	}

  // 延迟关闭流
	defer res.Body.Close()

  fmt.Println(res)
  fmt.Println(res.Body)

}
  • url 为请求地址,浏览器访问返回

  • 通过 http.Get 发起 GET 方式请求

  • http.Get 会返回 resp *Response, err error

    • resp 响应数据
    • err 这次请求是否发生了错误

  • 打印 res
&{200  200 HTTP/1.1 1 1 map[Access-Control-Allow-Headers:[Content-Type, x-requested-with, X-Custom-Header, Authorization] Access-Control-Allow-Methods:[POST, GET, OPTIONS, DELETE] Access-Control-Allow-Origin:[*] Access-Control-Max-Age:[3600] Cache-Control:[private] Content-Type:[application/json;charset=UTF-8] Date:[Wed, 24 Mar 2021 02:07:32 GMT] Expires:[Thu, 01 Jan 1970 00:00:00 GMT]] 0xc0001a2040 -1 [chunked] false false map[] 0xc000108000 0xc000234000}
  • res 还有常用的状态码、Header 信息

读取 Body 信息

res.Body 中就是请求回来的数据流 field Body io.ReadCloser

可以使用 I\O 操作工具类 ioutil 包来便利读取流信息

...

data, err := ioutil.ReadAll(res.Body)

...
  • ioutil.ReadAll 返回 []byte, error
  • [] type 就是返回数据

字节转字符串

使用内建 string 进行字节和字符串转换

fmt.Println(string(data))

输出转换后的内容

POST 请求

使用写好的 SpringBoot 服务提供 POST 登录

moose 服务是 SpringBoot + OAuth2.0 + Mybatis,提供了一些基本的 API 操作,有兴趣的可以看下 源码地址:gitee.com/shizidada/m…

启动服务,使用 go http 访问登录 http://localhost:7000/api/v1/account/login

PostForm 方式


func TestPostLogin() {
	loginUrl := "http://localhost:7000/api/v1/account/login"

	values := url.Values{}
	values.Add("accountName", "江景")
	values.Add("password", "123")
	values.Add("loginType", "password")

	res, err := http.PostForm(loginUrl, values)
	if err != nil {
		fmt.Println(err)
		return
	}

	data, err := ioutil.ReadAll(res.Body)
	fmt.Println(string(data))
	// {"code":200,"message":"请求成功","data":"790e6c22-2ec8-4d1a-a852-163efbc144cc"}
}

func main() {

	TestPostLogin()

}
  • 封装一个函数 TestPostLogin
  • loginUrl 登录地址
  • url.Values{} 是一个 map 类型,封装 POST 请求参数
type Values map[string][]string
  • http.PostForm 发送表单形式请求
  • 与 GET 请求一样,请求会返回 resp *Response, err error
  • 解析数据一样

JSON 反序列化

使用 go 语言自带包 encoding/json 解析

解析本地 json 文件

创建测试 json 文件

{
  "name": "测试用户",
  "age": 12,
  "sport": ["篮球", "羽毛球"]
}

Student 结构体

type Student struct {
	Name  string
	Age   int16
	Sport []string
}

解析

func JsonParseFile() {
	filepath := `E:\Code\moose-workspace\go-learn\http\test.json`

	file, err := os.Open(filepath)
	if err != nil {
		return
	}

	var stu Student
	if err = json.NewDecoder(file).Decode(&stu); err != nil {
		return
	}
	fmt.Println(stu)
}
  • 从文件中读取数据
  • json.NewDecoder 创建解码对象,Decode 解码
  • 运行输出
$ go run "e:\Code\moose-workspace\go-learn\http\main.go"
{测试用户 12 [篮球 羽毛球]}

解析简单数据结构

在登录之后,获取到数据,把返回的数据封装成一个结构体

修改,函数返回值 return data, nil

func TestPostLogin() ([]byte, error) {
	loginUrl := "http://localhost:7000/api/v1/account/login"

	values := url.Values{}
	values.Add("accountName", "江景")
	values.Add("password", "123")
	values.Add("loginType", "password")

	res, err := http.PostForm(loginUrl, values)
	if err != nil {
		fmt.Println(err)
		return nil, err
	}

	data, err := ioutil.ReadAll(res.Body)
	fmt.Println(string(data))
	// 790e6c22-2ec8-4d1a-a852-163efbc144cc
	return data, nil
}

LoginResponse 结构体

type LoginResponse struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Data    string `json:"data"`
}

反序列化方法

func json.Unmarshal(data []byte, v interface{}) error

func ParseSimple() {
	var loginResponse LoginResponse
	data, err := TestPostLogin()
	if err != nil {
		fmt.Println(err)
		return
	}
	err = json.Unmarshal(data, &loginResponse)
	if err != nil {
		fmt.Println("JSON解析错误")
		return
	}
	fmt.Println("解析出来 :: ", loginResponse.Data)
}
  • 定义 LoginResponse 变量
  • 调用 TestPostLogin 回去请求的数据
  • 调用 json.Unmarshal 解析数据,传递数据(byte)和需要解析成那种数据格式,传递地址(&loginResponse)
  • 如果解析没有发送错误,就表示解析成功,那么 loginResponse 变量就会值
  • fmt.Println("解析出来 :: ", loginResponse.Data)

解析复杂一点数据

解析 GET 请求返回的数据封装结构体

看上一步 GET 请求

修改 TestGet,返回值和 POST 一致

分析返回的数据

  • code,message 为 string 类型
  • result 字段返回了一个数组,数组中是一个个对象

  • 定义一个结构体,result 字段用切面,切面数据类型可以定义一个结构体或者用 map 映射类型

VideoResponse 结构体

type VideoResponse struct {
	Code    int         `json:"code"`
	Message string      `json:"message"`
	Result  []VideoItem `json:"result"`
}

VideoItem 结构体

type VideoItem struct {
	Sid                 string `json:"sid"`
	Text                string `json:"text"`
	Type                string `json:"type"`
	Thumbnail           string `json:"thumbnail"`
	Video               string `json:"video"`
	Up                  string `json:"up"`
	Down                string `json:"down"`
	Comment             string `json:"comment"`
	Uid                 string `json:"uid"`
	Name                string `json:"name"`
	Header              string `json:"header"`
	TopCommentsContent  string `json:"top_comments_content"`
	TopCommentsVoiceuri string `json:"top_comments_voiceuri"`
	TopCommentsUid      string `json:"top_comments_uid"`
	TopCommentsName     string `json:"top_comments_name"`
	TopCommentsHeader   string `json:"top_comments_header"`
	Passtime            string `json:"passtime"`
}

解析测试

func ParseComplicated() {
	var result VideoResponse

	data, err := TestGet()
	if err != nil {
		fmt.Println(err)
		return
	}

	err = json.Unmarshal(data, &result)
	if err != nil {
		fmt.Println("JSON解析错误")
		return
	}

	for _, value := range result.Result {
		fmt.Println(value.Comment)
		fmt.Println(value.TopCommentsName)
	}
}
  • 遍历 result.Result,看是否能够拿到对应的值

JSON 序列化

func JsonSerialize() {
	videoItems := make([]VideoItem, 2)
	videoRsponse := VideoResponse{
		Code:    200,
		Message: "请求成功",
		Result:  videoItems,
	}
	fmt.Printf("%#v", videoRsponse)
}

序列化方法

func json.Marshal(v interface{}) ([]byte, error)

data, err := json.Marshal(videoRsponse)
	if err != nil {
		return
	}

	fmt.Printf("%v", string(data))
	// fmt.Println(string(data))

第三方 json 解析库

github.com/buger/jsonp…

github.com/tidwall/gjs…

github.com/bitly/go-si…

总结

网络是传递数据的纽带;数据解析是交互的过程。

往期

文件操作、终端输入、文件存储~ 够浪|Go主题月 客户端和服务端建立长链接、实现消息推送|Go主题月 Gin 框架在实践中学习 ~ 够浪|Go主题月