【Go语言】解析处理结构化响应数据

前言

之前我们已经讲了如何通过各种技术来利用TCP协议的特性创建可用的客户端和服务器。下面我们来探讨一下OSI模型的几种上层协议,那就先从HTTP开始吧!

1、基础知识

HTTP

HTTP是一种无状态协议:服务器不会维护每个请求的状态,而是通过多种方式对其进行跟踪,这些方式可能包括会话标识符cookieHTTP标头等。客户端和服务器有责任正确协商和验证状态。

客户端和服务器之间的通信可以同步或异步进行,但它们要以请求/响应的方式循环进行。可以在请求中添加几个选项和标头,以影响服务器的行为并创建可用的web应用程序。最常见的是服务器托管web浏览器渲染的文件,以生成数据的图形化、组织化和时尚化的表示形式。API通常使用XMLJSONMSGRPC进行通信。某些情况下,检索到的数据可能是二进制格式,表示要下载的任意文件类型。

调用HTTP API

Gonet/http 标准包包含多个便捷函数,可便捷地发送POSTGETHEAD请求,函数使用形式如下:

Get (url string) (resp *Response, err error) Head (url string) (resp *Response, err error) Post (url string, bodyType string, body io.Reader) (resp *Response, err error)

函数Post() 具有两个附加参数 (bodyType 和 io.Reader) ,其中bodyType用于接收正文的Content-Type HTTP标头 (通常为application/x-www-form-urlencoded)

生成一个请求

我们可以使用函数NewRequest()创建结构体Request, 然后使用函数Client 的方法 Do 发送该结构体。http.NewRequest() 的函数原型如下

func NewRequest (method, url string, body io.Reader) (req *Request, err error)

需要将HTTP动词和目标URL提供给函数NewRequest() 作为其前两个参数。可以选择通过传入io.Reader作为第三个参数来提供请求正文。下面进行演示

//没有HTTP正文的调用,发送一个DELETE请求 reg, err := http.NewRequest("DELETE", "https://www.baidu.com/robots.txt", nil) var client http.Client resp, err := client.Do(reg) // 读取响应正文并关闭 //带有io.Reader正文的PUT请求(类似于PATCH请求) form := url.Values{} form.Add("foo", "bar") var client http.Cilent req, err := http.NewRequest( "PUT" "https://www.goole.com/robots.txt" strings.NewReader(form.Encode) ) resp, err := client.Do(reg)

使用结构化响应解析

我们知道,只要是执行与HTTP相关的任务,就必须检查HTTP响应的各个组成部分。包括读取响应正文访问cookie标头或仅检查HTTP的状态代码。下面我们就使用 ioutil.ReadAll() 函数从响应正文读取数据,进行一些错误检查,并将HTTP状态代码和响应正文打印到stdout

//处理HTTP响应正文 resp, err := http.Get("https://www.baidu.com/robots.txt") if err != nil { log.Panicln(err) } //打印HTTP状态 fmt.Println(resp.Status) //读取并显示响应正文 body, err := ioutil.ReadAll(resp.Body) if err != nil { log.Panicln(err) } fmt.Println(string(body)) resp.Body.Close()

2、处理HTTP响应正文

将上述步骤结合起来就可以得到我们的主程序

package main import ( "fmt" "io/ioutil" "log" "net/http" "net/url" "strings" ) func main() { resp, err := http.Get("https://www.baidu.com/robots.txt") if err != nil { log.Panicln(err) } //print http status fmt.Println(resp.Status) //read and display response body body, err := ioutil.ReadAll(resp.Body) if err != nil { log.Panicln(err) } fmt.Println(string(body)) resp.Body.Close() resp, err = http.Head("https://www.baidu.com/robots.txt") if err != nil { log.Panicln(err) } resp.Body.Close() fmt.Println(resp.Status) form := url.Values{} form.Add("foo", "bar") resp, err = http.Post( "https://www.baidu.com?robots.txt", "application/x-www-form-urlencoded", strings.NewReader(form.Encode()), ) //POST函数调用对表单数据进行URL编码时,将Content-Type设置为 application/x-www-form-urlencoded if err != nil { log.Panicln(err) } resp.Body.Close() fmt.Println(resp.Status) req, err := http.NewRequest("DELETE", "https://www.baidu.com/robots.txt", nil) if err != nil { log.Panicln(err) } var client http.Client resp, err = client.Do(req) if err != nil { log.Panicln(err) } resp.Body.Close() fmt.Println(resp.Status) req, err = http.NewRequest("PUT", "https://www.baidu.com/robots.txt", strings.NewReader(form.Encode())) if err != nil { log.Panicln(err) } resp, err = client.Do(req) if err != nil { log.Panicln(err) } resp.Body.Close() fmt.Println(resp.Status) }

程序运行后,结果如下:

200 OK User-agent: Baiduspider Disallow: /baidu Disallow: /s? Disallow: /ulink? Disallow: /link? Disallow: /home/news/data/ Disallow: /bh User-agent: Googlebot Disallow: /baidu Disallow: /s? Disallow: /shifen/ Disallow: /homepage/ Disallow: /cpro Disallow: /ulink? Disallow: /link? Disallow: /home/news/data/ Disallow: /bh ......//部分展示

需要注意的是:

我们在发送请求建立连接时,除了主goroutine外,还会新增两个goroutine, readLoop 和 writeLoop,而我们在Close之后会进行回收,如果不加Close的话,可能会造成goroutine泄露。

有兴趣的话可以看看这篇文章,讲的很清楚https://juejin.cn/post/6987372070120194055

3、解码一个JSON响应正文

当我们在与使用JSON进行通信的API交互,一个名为 /ping 的端点返回以下服务器状态的响应:

{"Message": "All is good with the world", "Status": "Success"}

下面构造程序与此端点进行交互并解码JSON消息

我们先想一下流程,要解析结构化数据类型,首先要定义一个用来表示响应数据的结构体,然后把数据解码到该结构体。

package main import ( "encoding/json" "log" "net/http" ) type Status struct { Message string Status string } func main() { res, err := http.Post( "http://IP:PORT/ping", "application/json", nil, ) if err != nil { log.Fatalln(err) } var status Status if err := json.NewDecoder(res.Body).Decode(&status); err != nil { log.Fatalln(err) } defer res.Body.Close() log.Printf("%s -> %s\n", status.Status, status.Message) }

总结

现在我们了解了如何构建自定义HTTP请求以及接收其响应,并且知道了如何解析结构化的数据,以便客户端可以查询信息以确定可执行的或相关联的数据。今天的内容就到这里了,如果有什么疑问欢迎在评论区一起讨论,go!go!go!

参考 : 《Black Hat Go:Go Programming for Hackers and Pentesters》


__EOF__

本文作者上岸!
本文链接https://www.cnblogs.com/Seversan-Sickle/p/17228236.html
关于博主:编程小萌新一名,希望从今天开始慢慢提高,一步步走向技术的高峰!
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Seversan-Sickle  阅读(100)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示