logo
0
0
WeChat Login

clientx 是一个Go语言实现的增强型HTTP客户端库,基于Go标准库net/http包进行封装,提供了更简洁、更强大的HTTP请求功能。

功能特点

  • 支持所有HTTP方法(GET, POST, PUT, DELETE, HEAD, OPTIONS, CONNECT, TRACE)
  • 内置请求重试机制和可自定义的退避策略
  • 支持上下文(context)控制请求超时和取消
  • 提供函数式配置选项,使用更灵活
  • 支持常见数据格式:JSON、表单、多部分表单(文件上传)
  • 并发安全的客户端管理
  • 可自定义HTTP客户端配置
  • 支持中间件机制,可灵活扩展请求处理流程
  • 自定义错误类型,提供更详细的错误信息

安装

# 假设项目路径
go get cnb.cool/zhiqiangwang/pkg/clientx

使用示例

基本用法

发送GET请求

import (
    "context"
    "fmt"
    "cnb.cool/zhiqiangwang/pkg/clientx"
)

func main() {
    ctx := context.Background()
    resp, err := clientx.Get(ctx, "https://api.example.com/users")
    if err != nil {
        fmt.Printf("请求失败: %v\n", err)
        return
    }
    defer resp.Body.Close()
    
    // 处理响应...
}

发送POST请求

import (
    "context"
    "fmt"
    "cnb.cool/zhiqiangwang/pkg/clientx"
)

func main() {
    ctx := context.Background()
    data := []byte(`{"name":"张三","age":30}`)
    
    resp, err := clientx.Post(ctx, "https://api.example.com/users", data, 
        clientx.WithHeaders(map[string]string{
            "Content-Type": "application/json",
        })
    )
    if err != nil {
        fmt.Printf("请求失败: %v\n", err)
        return
    }
    defer resp.Body.Close()
    
    // 处理响应...
}

高级用法

使用JSON请求

import (
    "context"
    "fmt"
    "cnb.cool/zhiqiangwang/pkg/clientx"
)

func main() {
    ctx := context.Background()
    user := struct {
        Name string `json:"name"`
        Age  int    `json:"age"`
    }{
        Name: "张三",
        Age:  30,
    }
    
    resp, err := clientx.PostJSON(ctx, "https://api.example.com/users", user)
    if err != nil {
        fmt.Printf("请求失败: %v\n", err)
        return
    }
    defer resp.Body.Close()
    
    // 处理响应...
}

发送表单数据

import (
    "context"
    "fmt"
    "net/url"
    "cnb.cool/zhiqiangwang/pkg/clientx"
)

func main() {
    ctx := context.Background()
    form := url.Values{}
    form.Add("username", "admin")
    form.Add("password", "123456")
    
    resp, err := clientx.PostForm(ctx, "https://api.example.com/login", form)
    if err != nil {
        fmt.Printf("请求失败: %v\n", err)
        return
    }
    defer resp.Body.Close()
    
    // 处理响应...
}

文件上传

import (
    "context"
    "fmt"
    "cnb.cool/zhiqiangwang/pkg/clientx"
)

func main() {
    ctx := context.Background()
    
    // 打开文件
    file, err := clientx.OpenFile("avatar", "/path/to/avatar.jpg")
    if err != nil {
        fmt.Printf("打开文件失败: %v\n", err)
        return
    }
    
    // 准备上传数据
    uploadData := clientx.UploadFields{
        Fields: map[string]string{
            "username": "admin",
            "desc":     "用户头像",
        },
        Files: []clientx.File{file},
    }
    
    // 发送多部分表单请求
    resp, err := clientx.PostMForm(ctx, "https://api.example.com/upload", uploadData)
    if err != nil {
        fmt.Printf("上传失败: %v\n", err)
        return
    }
    defer resp.Body.Close()
    
    // 处理响应...
}

自定义请求配置

import (
    "context"
    "fmt"
    "time"
    "cnb.cool/zhiqiangwang/pkg/clientx"
)

func main() {
    ctx := context.Background()
    
    // 设置自定义重试策略和请求头
    resp, err := clientx.Get(ctx, "https://api.example.com/users",
        clientx.WithRetries(5), // 设置最大重试次数为5
        clientx.WithHeaders(map[string]string{
            "Authorization": "Bearer token123",
            "User-Agent":    "MyApp/1.0",
        }),
        clientx.WithBackoff(func(attempt int) time.Duration {
            // 自定义退避函数:线性增长
            return time.Duration(attempt*1000) * time.Millisecond
        }),
    )
    if err != nil {
        fmt.Printf("请求失败: %v\n", err)
        return
    }
    defer resp.Body.Close()
    
    // 处理响应...
}

使用上下文控制超时

import (
    "context"
    "fmt"
    "time"
    "cnb.cool/zhiqiangwang/pkg/clientx"
)

func main() {
    // 创建一个5秒超时的上下文
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    
    resp, err := clientx.Get(ctx, "https://api.example.com/slow-endpoint")
    if err != nil {
        fmt.Printf("请求失败: %v\n", err)
        return
    }
    defer resp.Body.Close()
    
    // 处理响应...
}

自定义HTTP客户端

import (
    "crypto/tls"
    "net/http"
    "time"
    "cnb.cool/zhiqiangwang/pkg/clientx"
)

func main() {
    // 创建自定义HTTP客户端
    customClient := &http.Client{
        Transport: &http.Transport{
            MaxIdleConns:        200,
            IdleConnTimeout:     120 * time.Second,
            TLSHandshakeTimeout: 15 * time.Second,
            TLSClientConfig: &tls.Config{
                InsecureSkipVerify: false, // 生产环境不应该跳过验证
            },
        },
        Timeout: 30 * time.Second,
    }
    
    // 设置为默认客户端
    clientx.SetClient(customClient)
    
    // 后续所有请求都会使用这个自定义客户端
    // ...
}

使用中间件

import (
    "context"
    "fmt"
    "log"
    "time"
    "net/http"
    "cnb.cool/zhiqiangwang/pkg/clientx"
)

func main() {
    // 创建一个日志中间件
    logMiddleware := func(next func(*http.Request) (*http.Response, error)) func(*http.Request) (*http.Response, error) {
        return func(req *http.Request) (*http.Response, error) {
            start := time.Now()
            log.Printf("开始请求: %s %s", req.Method, req.URL.String())
            
            resp, err := next(req)
            
            duration := time.Since(start)
            if err != nil {
                log.Printf("请求失败: %s %s, 错误: %v, 耗时: %v", req.Method, req.URL.String(), err, duration)
            } else {
                log.Printf("请求完成: %s %s, 状态码: %d, 耗时: %v", req.Method, req.URL.String(), resp.StatusCode, duration)
            }
            
            return resp, err
        }
    }
    
    ctx := context.Background()
    
    // 使用中间件发送请求
    resp, err := clientx.Get(ctx, "https://api.example.com/users",
        clientx.WithMiddleware(logMiddleware),
    )
    if err != nil {
        fmt.Printf("请求失败: %v\n", err)
        return
    }
    defer resp.Body.Close()
    
    // 处理响应...
}

处理自定义HTTP错误

import (
    "context"
    "fmt"
    "cnb.cool/zhiqiangwang/pkg/clientx"
)

func main() {
    ctx := context.Background()
    
    resp, err := clientx.Get(ctx, "https://api.example.com/nonexistent")
    if err != nil {
        // 检查是否为自定义HTTP错误
        if httpErr, ok := err.(*clientx.HTTPError); ok {
            fmt.Printf("HTTP错误: 状态码=%d, 方法=%s, URL=%s, 响应体=%s\n", 
                httpErr.StatusCode, httpErr.Method, httpErr.URL, string(httpErr.Body))
        } else {
            fmt.Printf("其他错误: %v\n", err)
        }
        return
    }
    defer resp.Body.Close()
    
    // 处理响应...
}

API参考

核心函数

通用请求函数

func Request(ctx context.Context, method, url string, body []byte, opts ...OptionFunc) (*http.Response, error)

HTTP方法函数

func Get(ctx context.Context, url string, opts ...OptionFunc) (*http.Response, error)
func Post(ctx context.Context, url string, body []byte, opts ...OptionFunc) (*http.Response, error)
func Put(ctx context.Context, url string, body []byte, opts ...OptionFunc) (*http.Response, error)
func Delete(ctx context.Context, url string, body []byte, opts ...OptionFunc) (*http.Response, error)
func Head(ctx context.Context, url string, opts ...OptionFunc) (*http.Response, error)
func Options(ctx context.Context, url string, body []byte, opts ...OptionFunc) (*http.Response, error)
func Connect(ctx context.Context, url string, body []byte, opts ...OptionFunc) (*http.Response, error)
func Trace(ctx context.Context, url string, body []byte, opts ...OptionFunc) (*http.Response, error)

数据格式专用函数

func PostJSON(ctx context.Context, url string, payload any, opts ...OptionFunc) (*http.Response, error)
func PostForm(ctx context.Context, url string, form url.Values, opts ...OptionFunc) (*http.Response, error)
func PostMForm(ctx context.Context, url string, data UploadFields, opts ...OptionFunc) (*http.Response, error)

文件处理函数

func OpenFile(fieldName, filename string) (File, error)

客户端管理函数

func SetClient(client *http.Client)
func GetClient() *http.Client

配置选项

func WithRetries(n int) OptionFunc
func WithBackoff(f BackoffFunc) OptionFunc
func WithHeaders(h map[string]string) OptionFunc
func WithForceRetry() OptionFunc
func WithMiddleware(mw Middleware) OptionFunc
func WithMaxIdleConns(n int) OptionFunc
func WithMaxConnsPerHost(n int) OptionFunc
func WithIdleConnTimeout(d time.Duration) OptionFunc
func WithTimeout(timeout time.Duration) OptionFunc

数据结构

// Option 包含HTTP请求的配置选项
type Option struct {
    Retries     int
    Backoff     BackoffFunc
    Headers     map[string]string
    ForceRetry  bool
    Middlewares []Middleware
}

// BackoffFunc 定义重试间隔的计算函数
type BackoffFunc func(attempt int) time.Duration

// Middleware 定义中间件类型
type Middleware func(next func(*http.Request) (*http.Response, error)) func(*http.Request) (*http.Response, error)

// HTTPError 自定义HTTP错误类型
type HTTPError struct {
    StatusCode int
    Method     string
    URL        string
    Body       []byte
    Err        error
}

// File 表示上传的文件
type File struct {
    FieldName string
    FileName  string
    File      io.Reader
}

// UploadFields 包含多部分表单的字段和文件
type UploadFields struct {
    Fields map[string]string
    Files  []File
}

默认配置

  • 默认重试次数:3次
  • 默认退避策略:指数退避,初始500ms,最大30s
  • 默认超时时间:10秒
  • 默认TLS配置:跳过证书验证(注意:生产环境应修改此配置)
  • 默认会自动重试的HTTP方法:GET, HEAD, PUT, DELETE
  • 默认中间件链:空(无中间件)

注意事项

  1. 始终记得关闭响应体resp.Body,以避免资源泄漏
  2. 在生产环境中,建议自定义TLS配置,不要跳过证书验证
  3. 对于包含敏感数据的请求,确保使用HTTPS
  4. 使用上下文(context)来控制长请求的超时和取消
  5. 对于非幂等的HTTP方法(如POST),默认不会自动重试,除非使用WithForceRetry()选项
  6. 中间件的执行顺序是后进先出(LIFO),即最后添加的中间件最先执行
  7. 自定义错误类型HTTPError提供了更详细的错误信息,包括状态码、URL、方法和响应体内容

依赖

  • Go标准库:net/http, context, crypto/tls, io, bytes, encoding/json, mime/multipart, sync, time, runtime

About

一个Go语言实现的增强型HTTP客户端库,基于Go标准库`net/http`包进行封装,提供了更简洁、更强大的HTTP请求功能。

80.00 KiB
0 forks0 stars1 branches1 TagREADMEApache-2.0 license
Language
Go42.4%
Markdown38.4%
License16.6%
gitignore2.6%