Go Http 学习
本文目标
- 了解 Go 中常见的发起网络请求方式
- 掌握 Go 中发起网络请求的基本流程
- 掌握 Go 中发起网络请求的常用参数
- 掌握 Go 中发起网络请求的常用方法
- 掌握 Go 中发起网络请求的常用库
- 掌握 Go 中发起网络请求的常用技巧
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 客户端库,简化了请求过程。
特点:
- 链式调用,代码更简洁
- 内置 JSON 处理
- 支持重试、中间件等高级功能
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 客户端和服务器实现。
特点:
- 性能极高,适合高并发场景
- 对象重用机制减少 GC 压力
- API 设计与标准库不同
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 参数: