Golang网络编程-HTTP编程实战篇
作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
一.HTTP概述
1>.HTTP概述
一个Web服务器也被称为HTTP服务器,它通过HTTP (HyperText Transfer Protocol 超文本传输协议)协议与客户端通信。这个客户端通常指的是Web浏览器(其实手机端客户端内部也是浏览器实现的)。 Web服务器的工作原理可以简单地归纳为: 1>.客户机通过TCP/IP协议建立到服务器的TCP连接 2>.客户端向服务器发送HTTP协议请求包,请求服务器里的资源文档 3>.服务器向客户机发送HTTP协议应答包,如果请求的资源包含有动态语言的内容,那么服务器会调用动态语言的解释引擎负责处理“动态内容”,并将处理得到的数据返回给客户端 4>.客户机与服务器断开。由客户端解释HTML文档,在客户端屏幕上渲染图形结果
2>.HTTP协议
超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议,它详细规定了浏览器和万维网服务器之间互相通信的规则,通过因特网传送万维网文档的数据传送协议。
HTTP协议通常承载于TCP协议之上,有时也承载于TLS或SSL协议层之上,这个时候,就成了我们常说的HTTPS。
3>.HTTP请求报文格式说明
如上图所示,HTTP 请求报文由请求行、请求头部、空行、请求包体4个部分组成。 请求行 请求行由方法字段、URL字段 和HTTP 协议版本字段 3个部分组成,他们之间使用空格隔开。常用的 HTTP 请求方法有GET、POST。 GET: 当客户端要从服务器中读取某个资源时,使用GET 方法。GET 方法要求服务器将URL 定位的资源放在响应报文的数据部分,回送给客户端,即向服务器请求某个资源。 使用GET方法时,请求参数和对应的值附加在 URL 后面,利用一个问号(“?”)代表URL 的结尾与请求参数的开始,传递参数长度受限制,因此GET方法不适合用于上传数据。 通过GET方法来获取网页时,参数会显示在浏览器地址栏上,因此保密性很差。 POST: 当客户端给服务器提供信息较多时可以使用POST 方法,POST 方法向服务器提交数据,比如完成表单数据的提交,将数据提交给服务器处理。 GET 一般用于获取/查询资源信息,POST 会附带用户数据,一般用于更新资源信息。POST 方法将请求参数封装在HTTP 请求数据中,而且长度没有限制,因为POST携带的数据,在HTTP的请求正文中,以名称/值的形式出现,可以传输大量数据。
请求头部 请求头部为请求报文添加了一些附加信息,由“名/值”对组成,每行一对,名和值之间使用冒号分隔。请求头部通知服务器有关于客户端请求的信息,典型的请求头有: User-Agent: 请求的浏览器类型 Accept: 客户端可识别的响应内容类型列表,星号("*")用于按范围将类型分组,用"*/*"指示可接受全部类型,用"type/*"指示可接受 type 类型的所有子类型 Accept-Language: 客户端可接受的自然语言 Accept-Encoding: 客户端可接受的编码压缩格式 Accept-Charset: 可接受的应答的字符集 Host: 请求的主机名,允许多个域名同处一个IP 地址,即虚拟主机 connection: 连接方式(close或keepalive) Cookie: 存储于客户端扩展字段,向同一域名的服务端发送属于该域的cookie
空行 最后一个请求头之后是一个空行,发送回车符和换行符,通知服务器以下不再有请求头。
请求包体 请求包体不在GET方法中使用,而在POST方法中使用。POST方法适用于需要客户填写表单的场合。与请求包体相关的最常使用的是包体类型Content-Type和包体长度Content-Length。
4>.HTTP响应报文说明
如上图所示,HTTP 响应报文由状态行、响应头部、空行、响应包体4个部分组成。 状态行 状态行由 HTTP 协议版本字段、状态码和状态码的描述文本3个部分组成,他们之间使用空格隔开。 状态码:状态码由三位数字组成,第一位数字表示响应的类型,常用的状态码有五大类如下所示: 1xx: 表示服务器已接收了客户端请求,客户端可继续发送请求 2xx: 表示服务器已成功接收到请求并进行处理 3xx: 表示服务器要求客户端重定向 4xx: 表示客户端的请求有非法内容 5xx: 表示服务器未能正常处理客户端的请求而出现意外错误 常见的状态码举例: 200: 表示OK,即客户端请求成功 400: 表示Bad Request,即请求报文有语法错误 401: 表示Unauthorized,即未授权 403: 表示Forbidden,即服务器拒绝服务 404: 表示Not Found,即请求的资源不存在 500: 表示Internal Server Error,即服务器内部错误 503: 表示Server Unavailable,即服务器临时不能处理客户端请求(稍后可能可以) 响应头部 响应头可能包括: Location: 响应报头域用于重定向接受者到一个新的位置 Server: 响应报头域包含了服务器用来处理请求的软件信息及其版本 Vary: 指示不可缓存的请求头列表 Connection: 连接方式 空行 最后一个响应头部之后是一个空行,发送回车符和换行符,通知服务器以下不再有响应头部。 响应包体 服务器返回给客户端的文本信息。
5>.博主推荐阅读
http协议版本,工作机制及http服务器应用扫盲篇: https://www.cnblogs.com/yinzhengjie/p/11986869.html HTTP协议详解: https://www.cnblogs.com/yinzhengjie/p/12014076.html 前后端分离-Restful最佳实践 https://www.cnblogs.com/yinzhengjie/p/12037939.html
二.使用Golang编写一个简单的Web服务器(生产环境建议初学者使用开源的web框架,比如Beego,Gin等)
package main import ( "fmt" "net/http" ) func UserResp(resp http.ResponseWriter, req *http.Request) { fmt.Printf("请求方法: %s ", req.Method) fmt.Printf("浏览器发送请求文件路径: %s ", req.URL) fmt.Printf("请求头: %s ", req.Header) fmt.Printf("请求包体: %s ", req.Body) fmt.Printf("客户端网络地址: %s ", req.RemoteAddr) fmt.Printf("客户端Agent: %s ", req.UserAgent()) /** 给客户端回复数据 */ resp.Write([]byte("User response")) } func IndexResp(resp http.ResponseWriter, req *http.Request) { resp.Write([]byte("Index response")) } func main() { /** 为不同的请求注册不同的函数 */ http.HandleFunc("/user", UserResp) http.HandleFunc("/index", IndexResp) //开启服务器,监听客户端的请求 http.ListenAndServe("127.0.0.1:8080", nil) }
三.使用Golang发起HTTP请求
package main import ( "fmt" "net/http" ) func main() { /** 该URL是咱们自己刚刚编写简单的Web服务器对应的资源。 */ url := "http://127.0.0.1:8080/user" resp, err := http.Get(url) if err != nil { fmt.Println("获取数据失败,错误原因: ", err) return } defer resp.Body.Close() /** 获取从服务器端读到数据 */ fmt.Printf("状态: %s ", resp.Status) fmt.Printf("状态码: %v ", resp.StatusCode) fmt.Printf("响应头部: %s ", resp.Header) fmt.Println("响应包体: ", resp.Body) /** 定义切片缓冲区,临时存储读到的数据,并将每次读到的结果拼接到data中 */ buf := make([]byte, 4096) var data string for { n, _ := resp.Body.Read(buf) if n == 0 { fmt.Println("数据读取完毕....") break } if err != nil { fmt.Println("数据读取失败,错误原因: ", err) return } data += string(buf[:n]) } fmt.Printf("从服务端获取到的内容是: [%s] ", data) }