三十的博客

Go Http 学习

发布时间
阅读量 加载中...

本文目标

HTTP 服务器

go
package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
)

func main() {
	// 设置路由处理函数
	http.HandleFunc("/", requestHandler)

	// 启动HTTP服务器
	fmt.Println("Server started at http://localhost:8089")
	log.Fatal(http.ListenAndServe(":8089", nil))
}

// 请求处理函数
func requestHandler(w http.ResponseWriter, r *http.Request) {
	// 设置响应头
	w.Header().Set("Content-Type", "application/json")

	// 1. 打印请求基本信息
	fmt.Printf("\n=== 收到新请求 ===\n")
	fmt.Printf("方法: %s\n", r.Method)
	fmt.Printf("URL: %s\n", r.URL.String())

	// 2. 打印请求头
	fmt.Println("\n请求头:")
	for name, values := range r.Header {
		for _, value := range values {
			fmt.Printf("%s: %s\n", name, value)
		}
	}

	// 3. 处理GET请求参数
	if r.Method == "GET" {
		fmt.Println("\nGET参数:")
		queryParams := r.URL.Query()
		for key, values := range queryParams {
			for _, value := range values {
				fmt.Printf("%s: %s\n", key, value)
			}
		}
	}

	// 4. 处理POST请求体
	if r.Method == "POST" {
		// 读取请求体
		body, err := io.ReadAll(r.Body)
		if err != nil {
			http.Error(w, "读取请求体失败", http.StatusBadRequest)
			return
		}
		defer r.Body.Close()

		fmt.Println("\n请求体:")
		fmt.Println(string(body))

		// 如果是表单POST请求,也打印表单参数
		if r.Header.Get("Content-Type") == "application/x-www-form-urlencoded" {
			if parsedBody, err := url.ParseQuery(string(body)); err == nil {
				fmt.Println("\n表单参数:")
				for key, values := range parsedBody {
					for _, value := range values {
						fmt.Printf("%s: %s\n", key, value)
					}
				}
			}
		}
	}

	// 5. 返回JSON响应
	response := map[string]interface{}{
		"status":  "success",
		"message": "请求已接收",
		"method":  r.Method,
		"path":    r.URL.Path,
	}

	jsonResponse, err := json.Marshal(response)
	if err != nil {
		http.Error(w, "生成JSON响应失败", http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(jsonResponse)
}

使用标准库 net/http

最简单的 GET 请求

go
package main

import (
	"fmt"
	"io"
	"net/http"
)

func main() {
	// 发起GET请求
	resp, err := http.Get("http://localhost:8089")
	if err != nil {
		fmt.Println("请求失败:", err)
		return
	}

	// 确保关闭响应体
	defer resp.Body.Close()

	// 读取响应内容
	// Deprecated: As of Go 1.16, this function simply calls [io.ReadAll].
	// body, err := ioutil.ReadAll(resp.Body)
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("读取相应失败", err)
		return
	}

	fmt.Println(string(body))
}

服务端输出:

=== 收到新请求 ===
方法: GET
URL: /

请求头:
User-Agent: Go-http-client/1.1
Accept-Encoding: gzip

GET 参数:

带参数的 GET 请求

go
package main

import (
	"fmt"
	"io"
	"net/http"
	"net/url"
)

func main() {
	// 构建URL参数
	params := url.Values{}
	params.Add("name", "zhangsan")
	params.Add("age", "18")

	// 构造完整 URL
  // Encode() 方法将参数编码为 URL 安全格式
	reqUrl := "http://localhost:8089?" + params.Encode()

	// 发起GET请求
	resp, err := http.Get(reqUrl)
	if err != nil {
		fmt.Println("请求失败:", err)
		return
	}

	// 确保关闭响应体
	defer resp.Body.Close()

	// 读取响应内容
	// Deprecated: As of Go 1.16, this function simply calls [io.ReadAll].
	// body, err := ioutil.ReadAll(resp.Body)
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("读取相应失败", err)
		return
	}

	fmt.Println(string(body))
}

服务端输出:

=== 收到新请求 ===
方法: GET
URL: /?age=18&name=zhangsan

请求头:
User-Agent: Go-http-client/1.1
Accept-Encoding: gzip

GET 参数:
age: 18
name: zhangsan

POST 请求(表单数据)

go
package main

import (
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
)

func main() {
	// 准备表单数据
	formData := url.Values{}
	formData.Set("username", "admin")
	formData.Set("password", "123456")

	// 发起 POST 请求
  // 第二个参数指定内容类型为表单数据
  // strings.NewReader() 创建一个 io.Reader 从字符串
	resp, err := http.Post(
		"http://localhost:8089",
		"application/x-www-form-urlencoded",
		strings.NewReader(formData.Encode()),
	)
	if err != nil {
		fmt.Println("请求失败:", err)
		return
	}

	// 确保关闭响应体
	defer resp.Body.Close()

	// 读取响应内容
	// Deprecated: As of Go 1.16, this function simply calls [io.ReadAll].
	// body, err := ioutil.ReadAll(resp.Body)
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("读取相应失败", err)
		return
	}

	fmt.Println(string(body))
}

服务端输出:

=== 收到新请求 ===
方法: POST
URL: /

请求头:
User-Agent: Go-http-client/1.1
Content-Length: 30
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip

请求体:
password=123456&username=admin

表单参数:
password: 123456
username: admin

POST 请求(JSON 数据)

go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
)

func main() {
	// 准备 json 数据
	requestBody, err := json.Marshal(map[string]string{
		"username": "admin",
		"password": "123456",
	})

	if err != nil {
		fmt.Println("JSON 编码失败", err)
		return
	}

	// 发起 POST 请求
  // 第二个参数指定内容类型为 application/json
  // bytes.NewBuffer() 创建一个 io.Reader 从字节切片
	resp, err := http.Post(
		"http://localhost:8089",
		"application/json",
		bytes.NewBuffer(requestBody),
	)
	if err != nil {
		fmt.Println("请求失败:", err)
		return
	}

	// 确保关闭响应体
	defer resp.Body.Close()

	// 读取响应内容
	// Deprecated: As of Go 1.16, this function simply calls [io.ReadAll].
	// body, err := ioutil.ReadAll(resp.Body)
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("读取相应失败", err)
		return
	}

	fmt.Println(string(body))
}

服务器端输出:

=== 收到新请求 ===
方法: POST
URL: /

请求头:
User-Agent: Go-http-client/1.1
Content-Length: 40
Content-Type: application/json
Accept-Encoding: gzip

请求体:
{"password":"123456","username":"admin"}

更灵活的请求 http.Client

go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"
)

func main() {
	// 创建自定义客户端
	client := &http.Client{
		Timeout: time.Second * 10, // 设置超时时间
	}

	// 准备请求体
	requestBody, _ := json.Marshal(map[string]string{
		"query": "hi",
	})

	// 创建请求
	req, err := http.NewRequest(
		"POST",
		"http://localhost:8089",
		bytes.NewBuffer(requestBody),
	)

	if err != nil {
		fmt.Println("创建请求失败:", err)
		return
	}

	// 设置请求头
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer abc123")

	// 发送请求
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("请求失败:", err)
		return
	}
	defer resp.Body.Close()

	// 读取响应内容
	// Deprecated: As of Go 1.16, this function simply calls [io.ReadAll].
	// body, err := ioutil.ReadAll(resp.Body)
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("读取相应失败", err)
		return
	}

	fmt.Println(string(body))
}

服务端输出:

=== 收到新请求 ===
方法: POST
URL: /

请求头:
User-Agent: Go-http-client/1.1
Content-Length: 14
Authorization: Bearer abc123
Content-Type: application/json
Accept-Encoding: gzip

请求体:
{"query":"hi"}

使用第三方库

使用 resty

resty 是一个流行的 HTTP 客户端库,简化了请求过程。

特点:​​

go
package main

import (
	"fmt"
	"github.com/go-resty/resty/v2"
)

func main() {
	// 创建客户端
	client := resty.New()

	// 发起 GET 请求
	resp, err := client.R().
		SetQueryParams(map[string]string{
			"page":  "1",
			"limit": "20",
		}).
		SetHeader("Accept", "application/json").
		Get("http://localhost:8089")

	if err != nil {
		fmt.Println("请求失败:", err)
		return
	}

	fmt.Println("状态码:", resp.StatusCode())
	fmt.Println("响应体:", string(resp.Body()))
}

服务器端输出:

=== 收到新请求 ===
方法: GET
URL: /?limit=20&page=1

请求头:
User-Agent: go-resty/2.16.5 (https://github.com/go-resty/resty)
Accept: application/json
Accept-Encoding: gzip

GET 参数:
limit: 20
page: 1

使用 fasthttp

fasthttp 是一个高性能的 HTTP 客户端和服务器实现。

特点:​​

go
package main

import (
	"fmt"
	"github.com/valyala/fasthttp"
)

func main() {
	// 创建请求和响应对象
	req := fasthttp.AcquireRequest()
	resp := fasthttp.AcquireResponse()
	defer func() {
		fasthttp.ReleaseRequest(req)
		fasthttp.ReleaseResponse(resp)
	}()

	// 设置请求
	req.SetRequestURI("http://localhost:8089")
	req.Header.SetMethod("GET")
	req.Header.Set("User-Agent", "MyClient")

	// 发送请求
	if err := fasthttp.Do(req, resp); err != nil {
		fmt.Println("请求失败:", err)
		return
	}

	fmt.Println("状态码:", resp.StatusCode())
	fmt.Println("响应体:", string(resp.Body()))
}

服务器端输出:

=== 收到新请求 ===
方法: GET
URL: /

请求头:
User-Agent: MyClient

GET 参数:

#Http #Golang