GO HTTP编程

177 阅读7分钟

HTTP 协议

  • HTTP :超文本传输协议Hyper Text Transfer Protocol。
  • HTTP 属于应用层协议,它在传输层用的是TCP 协议。
  • 无状态,对事务处理没有记忆能力(对比TCP 协议里的确认号)。如果要保存状态需要引用其他技术,如cookie。
  • 无连接,每次连接只处理一个请求。早期带宽和计算资源有限,这么做是为了追求传输速度快,后来通过Connection: Keep-Alive 实现长连接。HTTP 1.1废弃了Keep-Alive,默认支持长连接。

HTTP request

image.png

1. 请求方法

请求方法解释
GET请求获取Request-URI所标识的资源
POST向URI提交数据(例如提交表单或上传数据)
HEAD类似于GET,返回的响应中没有具体的内容,用于获取报头
PUT对服务器上已存在的资源进行更新
DELETE请求服务器删除指定的页面
CONNECTHTTP/1.1预留,能够将连接改为管道方式的代理服务器
OPTIONS查看服务端性能
TRACE回显服务器收到的请求,主要用于测试或诊断
PATCH同PUT,可只对资源的一部分更新,资源不存在时会创建
  • GET、POST 和HEAD 是HTTP 1.0 就有的,后面的请求方法是HTTP 1.1 新增的。
  • 客户端发起一个请求时,这个请求可能要穿过防火墙、代理、网关或其他一些应用程序。每个中间节点都可能会修改原始的HTTP 请求。TRACE 方法允许客户端在 最终将请求发送给服务器时,看看它变成了什么样子。TRACE请求会在目的服务器端发起一个环回诊断。行程最后一站的服务器会弹回一条TRACE响应,并在响应主体中携带它收到的原始请求报文。这样客户端就可以查看在所有中间HTTP应用程序组成的请求/响应链上,原始报文是否,以及如何被毁坏或修改过。
  • CONNECT 方法是HTTP 1.1协议预留的,能够将连接改为管道方式的代理服务器。通常用于SSL加密服务器的链接与非加密的HTTP代理服务器的通信。
  • OPTIONS 方法请求Web 服务器告知其支持的各种功能。通过使用OPTIONS,客户端可以在与服务器进行交互之前,确定服务器的能力,这样它就可以更方便地与具备不同特性的代理和服务器进行互操作了。
    实际中server对各种request method的处理方式可能不是按协议标准来的,比如server 收到PUT 请求时偏偏执行DELET E操作,同理仅用一个GET方法也能实现增删改查的全部功能。大多数浏览器只支持GET 和POST 。

2. URL

  • URI:uniform resource identifier,统一资源标识符,用来唯一的标识一个资源。
  • URL: uniform resource locator,统一资源定位器,它是一种具体的URI ,指明了如何locate 这个资源。
  • URL举例

image.png

3. 协议版本

现在广泛应用的协议版本为HTTP 1.1

4. 请求头

Header解释示例
Accept指定客户端能够接收的内容类型Accept: text/plain, text/html
Accept-Charset浏览器可以接受的字符编码集Accept-Charset: iso-8859-5
Accept-Encoding指定浏览器可以支持的web服务器返回内容压缩编码类型Accept-Encoding: compress, gzip
Accept-Language浏览器可接受的语言Accept-Language: en,zh
AuthorizationHTTP授权的授权证书Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Cache-Control指定请求和响应遵循的缓存机制Cache-Control: no-cache
Connection表示是否需要持久连接(HTTP 1.1默认进行持久连接)Connection: close
CookieHTTP请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器Cookie: $Version=1; Skin=new;
Content-Length请求的内容长度Content-Length: 348
Content-Type指定正文(body)的数据格式Content-Type: application/x-www-form-urlencoded
User-Agent浏览器信息Mozilla/5.0 (Windows NT 6.1; Win64; x64)

Content-Type

  • application/x-www-form-urlencoded

    • 浏览器的原生form表单,如果不设置 Content-Type 属性,则默认以 application/x-www-form-urlencoded 方式传输数据
    • 正文例如:name=manu&message=this_is_great
  • multipart/form-data

    • 上传文件时使用multipart/form-data,支持多种文件格式
    • 正文例如: name="text"name="file"; filename="chrome.png"Content-Type: image/png... content of chrome.png
  • application/json

    • 正文例如:{"title":"test","sub":[1,2,3]}
  • text/xml

    • 正文例如: examples.getStateName

5.请求正文

GET 没有请求正文。POST 既可以把一部分参数放URL 里,也可以把一部分参数放请求正文里,如下是一个完整的POST 请求

POST /post?id=1234&page=1 HTTP/1.1
Content-Type: application/x-www-form-urlencoded

name=manu&message=this_is_great

HTTP response

image.png

1. 响应状态即话术

codephrase说明
200Ok请求成功
400Bad Request客户端有语法错误,服务端不理解
401Unauthorized请求未经授权
403Forbidden服务端拒绝提供服务
404Not Found请求资源不存在
500Internal Server Error服务器发生不可预期的错误
503Server Unavailable服务器当前有问题,过段时间可能恢复

2. 响应头

Header解释示例
Allow对某网络资源的有效的请求行为Allow: GET, HEAD
Date原始服务器消息发出的时间Date: Tue, 15 Nov 2010 08:12:31 GMT
Content-Encoding服务器支持的返回内容压缩编码类型Content-Encoding: gzip
Content-Language响应体的语言Content-Language: en,zh
Content-Length响应体的长度Content-Length: 348
Cache-Control指定请求和响应遵循的缓存机制Cache-Control: no-cache
Content-Type返回内容的MIME类型Content-Type: text/html; charset=utf-8

3.响应正文

  • 响应正文可以是html、json、xml、普通文本,等等。
  • 完整http response举例:
HTTP/1.1 200 OK 
Date: Fri, 22 May 2009 06:07:21 GMT 
Content-Type: text/html; charset=UTF-8 

<html> 
	<head></head>
	 <body>
		 <!--body goes here--> 
	</body> 
</html>

HTTPS

  • HTTP + 加密 + 认证 + 完整性保护 = HTTPS (HTTP Secure)

image.png

GO HTTP标准库

  • http.ListenAndServe(addr string, handler http.Handler) error:
    • ListenAndServe 使用指定的监听地址和处理器启动一个HTTP 服务端。处理器参数通常是nil ,这表示采用包变量DefaultServeMux 作为处理器。Handle 和HandleFunc 函数可以向DefaultServeMux 添加处理器。
  • 包变量DefaultServeMux

image.png

  • type ServeMux
    • ServeMux 类型是HTTP 请求的多路转接器。它会将每一个接收的请求的URL 与一个注册模式的列表进行匹配,并调用和URL 最匹配的模式的处理器。
  • http.HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request))
    • HandleFunc 注册一个处理器函数handler 和对应的模式pattern(注册到DefaultServeMux)。

Server/Client 端 打印 Client/Server 端的请求/回复信息

func RequestInformation(r *http.Request){
	fmt.Printf("request method:%s      ", r.Method)
	if r.Method == "POST" {
		name := r.PostFormValue("name")
		fmt.Printf("表单的name: %s\n", name)
	}
	fmt.Printf("request host: %s    ", r.Host)
	fmt.Printf("request url: %s \n", r.URL)
	fmt.Printf("request protocol: %s\n", r.Proto)
	fmt.Println("request Header")
	for k,v := range r.Header{
		fmt.Printf("%s: %v\n", k, v)
	}

	fmt.Println()
	fmt.Println("request cookie")
	if len(r.Cookies()) == 0{
		fmt.Println("no cookie")
	}else{
		for _, cookie := range r.Cookies(){
			fmt.Printf("name = %s, value:= %s \n", cookie.Name, cookie.Value)
		}
	}
	

	fmt.Println()
	fmt.Printf("request Body: ")
	bs := [1024]byte{}
	if n, err := r.Body.Read(bs[:]); err != nil{
		fmt.Println("Body没有内容")
	}else{
		fmt.Printf("%s\n",bs[:n])
	}
	// io.Copy(os.Stdout, r.Body)
	fmt.Println()
}
func ResponseInformation(resp *http.Response) {
	fmt.Printf("response protocol: %s  ", resp.Proto)
	fmt.Printf("response status: %s\n", resp.Status)
	fmt.Println("====== response header ====== ")
	for k, v := range resp.Header {
		fmt.Printf("%s, %v\n", k, v)
	}
	fmt.Println("response body")
	bs := [1024]byte{}
	if n, err := resp.Body.Read(bs[:]); err != nil{
		fmt.Println("Body没有内容")
	}else{
		fmt.Printf("%s\n",bs[:n])
	}

	// io.Copy(os.Stdout, resp.Body)
	fmt.Println()
}

Server端

写两个handler函数

  • ResponseWriter接口被HTTP处理器用于构造HTTP回复。
  • Request类型代表一个服务端接受到的或者客户端发送出去的HTTP请求。
func HomeHandler(w http.ResponseWriter, r *http.Request){
	fmt.Fprint(w, "Welcome to my home !")
	RequestInformation(r)
}

func AHandler(w http.ResponseWriter, r *http.Request){
	w.Write([]byte("welcome to A's room !"))
	RequestInformation(r)
}

将他们注册到DefaultServeMux

func main(){
	http.HandleFunc("/", HomeHandler)
	http.HandleFunc("/A", AHandler)
	if err := http.ListenAndServe("127.0.0.1:5656", nil); err != nil{
		fmt.Print(err)
		return
	}
}

运行

image.png image.png

Client 端

简单请求

  • http.Get(url string) (resp *http.Response, err error)
    • 向指定URL 发送GET请求
  • http.Post(url string, contentType string, body io.Reader) (resp *http.Response, err error)
    • Post向指定的URL发出一个POST请求。body的Type为POST数据的类型, body为POST数据,作为请求的主体。
  • http.PostForm(url string, data url.Values) (resp *http.Response, err error)
    • 发出一个POST请求,url.Values类型的data会被编码为请求的主体。
    • url.Values类型为:map[string][]string
  • http.Head(url string) (resp *http.Response, err error)
    • HEAD类似于GET,但HEAD方法只能取到http response报文头部,取不到resp.Body

构建复杂请求

  • HEAD、GET、POST 默认都属于简单请求 Simple Request,通过http.NewRequest可以支持全部的request method
  • http.NewRequest(method string, url string, body io.Reader) (*http.Request, error)
    • NewRequest使用指定的方法、网址和可选的主题创建并返回一个新的*Request
  • type Client :Client类型代表HTTP客户端
  • (*http.Client).Do(req *http.Request) (*http.Response, error)
    • Do方法发送请求,返回HTTP回复
func ComplexRequest() {
	body := strings.NewReader("hello server, i am complex")
	if req, err := http.NewRequest("DELETE", "http://127.0.0.1:5656", body); err != nil {
		fmt.Println(err)
		return
	} else {
		// 自定义请求头
		req.Header.Add("User-Agent", "季汉")
		req.Header.Add("emperor", "刘备")
		// 自定义cookie
		// HTTP请求中的cookie只包含name和value(服务器接收),domain、path等属性由浏览器使用
		req.AddCookie(&http.Cookie{
			Name:   "auth",
			Value:  "pass",
			Path:   "/",
			Domain: "localhost",
		})
                
		client := &http.Client{
			Timeout: 500 * time.Millisecond,  // 设置超时请求 
		}
                
		if resp, err := client.Do(req); err != nil {
			fmt.Println(err)
			return
		} else {
			defer resp.Body.Close()
			ResponseInformation(resp)
		}
	}

}