🐜 打印HTTP请求/响应以便在Go中进行调试

3,427 阅读3分钟

在Go中编写HTTP服务器或客户端时,将完整的HTTP请求或响应打印到标准输出,以利于调试。在这种情况下,使用 fmt.Println()在这种情况下可能是不够的,因为输出没有格式化,难以阅读。更好的主意是使用 httputil.DumpRequest(), httputil.DumpRequestOut()httputil.DumpResponse()函数,这些函数是为了漂亮地打印HTTP请求和响应。这样的pretty-printing或dumping意味着请求/响应以类似于它从服务器发送和接收的格式呈现。

使用这些函数的规则很简单。

记录客户端的请求和响应

请看下面的例子,了解如何使用函数 "和 "来转储HTTP客户端的请求和响应。 httputil.DumpRequestOut()httputil.DumpResponse()函数转储HTTP客户端请求和响应。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package main
import (
"fmt"
"log"
"net/http"
"net/http/httputil"
)
const serverAddr = "http://example.com/"
func main() {
req, err := http.NewRequest(http.MethodGet, serverAddr, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Add("test-header", "test-header-value")
reqDump, err := httputil.DumpRequestOut(req, true)
if err != nil {
log.Fatal(err)
}
fmt.Printf("REQUEST:\n%s", string(reqDump))
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
respDump, err := httputil.DumpResponse(resp, true)
if err != nil {
log.Fatal(err)
}
fmt.Printf("RESPONSE:\n%s", string(respDump))
}

输出。

REQUEST:
GET / HTTP/1.1
Host: example.com
User-Agent: Go-http-client/1.1
Test-Header: test-header-value
Accept-Encoding: gzip
RESPONSE:
HTTP/1.1 200 OK
Accept-Ranges: bytes
Age: 357425
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
Date: Fri, 25 Feb 2022 06:48:03 GMT
Etag: "3147526947+gzip"
Expires: Fri, 04 Mar 2022 06:48:03 GMT
Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
Server: ECS (bsa/EB18)
Vary: Accept-Encoding
X-Cache: HIT
<!doctype html>
<html>
<head>
<title>Example Domain</title>
<meta charset="utf-8" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
...

在服务器端记录请求

从下面的例子中,你可以了解到如何使用和函数来漂亮地打印传入的服务器请求。 httputil.DumpRequest()函数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main
import (
"fmt"
"log"
"net/http"
"net/http/httputil"
)
const port = 8080
func indexHandler(w http.ResponseWriter, r *http.Request) {
reqDump, err := httputil.DumpRequest(r, true)
if err != nil {
log.Fatal(err)
}
fmt.Printf("REQUEST:\n%s", string(reqDump))
w.Write([]byte("Hello World"))
}
func main() {
http.HandleFunc("/", indexHandler)
log.Printf("Starting HTTP server at port: %d\n", port)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
}

输出。

2022/02/25 07:03:20 Starting HTTP server at port: 8080
REQUEST:
GET / HTTP/1.1
Host: localhost:8080
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,en-US;q=0.8,en;q=0.7
Connection: keep-alive
Dnt: 1
Sec-Ch-Ua: " Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "macOS"
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36
REQUEST:
GET /favicon.ico HTTP/1.1
Host: localhost:8080
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,en-US;q=0.8,en;q=0.7
Connection: keep-alive
Dnt: 1
Referer: http://localhost:8080/
Sec-Ch-Ua: " Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "macOS"
Sec-Fetch-Dest: image
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36