V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
winterssy
V2EX  ›  分享创造

sreq - 一个简单,易用和线程安全的 Golang 网络请求库

  •  
  •   winterssy · 2019-10-02 15:00:36 +08:00 · 3121 次点击
    这是一个创建于 1639 天前的主题,其中的信息可能已经有所发展或是发生改变。

    各位 v 友我又来了,国庆前分享过一款为 Go 打造的网络请求库,那会的设计实现并发需要手动加锁,性能大打折扣。这次分享的是回炉重造的 真线程安全 版本,并删除了冗余的 API,不到 500 行代码实现完整功能。PS:纯属分享,不是推广,开发者水平有限也不推荐在生产环境使用。

    Github: https://github.com/winterssy/sreq

    功能

    • 简便地发送 GET/HEAD/POST/PUT/PATCH/DELETE/OPTIONS 等 HTTP 请求。
    • 简便地设置参数,请求头,或者 Cookies。
    • 简便地发送 Form 表单,JSON 数据,或者上传文件。
    • 简便地设置 Basic 认证,Bearer 令牌。
    • 自动管理 Cookies。
    • 自定义 HTTP 客户端。
    • 简便地设置请求上下文。
    • 简便地对响应解码,输出字节码,字符串,或者对 JSON 反序列化。
    • 并发安全。

    安装

    go get -u github.com/winterssy/sreq
    

    使用

    import "github.com/winterssy/sreq"
    

    例子

    sreq 发送请求几乎跟基础库 net/http 一样,无缝切换。举个栗子,如果你之前的请求是这样的:

    resp, err := http.Get("http://www.baidu.com")
    

    使用 sreq 你只须这样:

    resp, err := sreq.Get("http://www.baidu.com").Resolve()
    

    设置参数

    data, err := sreq.
        Get("http://httpbin.org/get",
            sreq.WithParams(sreq.Value{
                "key1": "value1",
                "key2": "value2",
            }),
           ).
        Text()
    if err != nil {
        panic(err)
    }
    fmt.Println(data)
    

    设置请求头

    data, err := sreq.
        Get("http://httpbin.org/get",
            sreq.WithHeaders(sreq.Value{
                "Origin":  "http://httpbin.org",
                "Referer": "http://httpbin.org",
            }),
           ).
        Text()
    if err != nil {
        panic(err)
    }
    fmt.Println(data)
    

    设置 Cookies

    data, err := sreq.
        Get("http://httpbin.org/cookies/set",
            sreq.WithCookies(
                &http.Cookie{
                    Name:  "name1",
                    Value: "value1",
                },
                &http.Cookie{
                    Name:  "name2",
                    Value: "value2",
                },
            ),
           ).
        Text()
    if err != nil {
        panic(err)
    }
    fmt.Println(data)
    

    发送 Form 表单

    data, err := sreq.
        Post("http://httpbin.org/post",
             sreq.WithForm(sreq.Value{
                 "key1": "value1",
                 "key2": "value2",
             }),
            ).
        Text()
    if err != nil {
        panic(err)
    }
    fmt.Println(data)
    

    发送 JSON 数据

    data, err := sreq.
        Post("http://httpbin.org/post").
        JSON(sreq.Data{
            "msg": "hello world",
            "num": 2019,
        }).
        Send().
        Text()
    if err != nil {
        panic(err)
    }
    fmt.Println(data)
    

    上传文件

    data, err := sreq.
        Post("http://httpbin.org/post", sreq.WithFiles(
            &sreq.File{
                FieldName: "testimage1",
                FileName:  "testimage1.jpg",
                FilePath:  "./testdata/testimage1.jpg",
            },
            &sreq.File{
                FieldName: "testimage2",
                FileName:  "testimage2.jpg",
                FilePath:  "./testdata/testimage2.jpg",
            },
        )).
        Text()
    if err != nil {
        panic(err)
    }
    fmt.Println(data)
    

    设置 Basic 认证

    data, err := sreq.
        Get("http://httpbin.org/basic-auth/admin/pass",
            sreq.WithBasicAuth("admin", "pass"),
           ).
        Text()
    if err != nil {
        panic(err)
    }
    fmt.Println(data)
    

    设置 Bearer 令牌

    data, err := sreq.
        Get("http://httpbin.org/bearer",
            sreq.WithBearerToken("sreq"),
           ).
        Text()
    if err != nil {
        panic(err)
    }
    fmt.Println(data)
    

    设置默认请求选项

    如果你希望每个 HTTP 请求都带上一些默认选项,可以通过自定义 sreq 客户端实现。

    req := sreq.New(nil,
    	sreq.WithParams(sreq.Value{
    		"defaultKey1": "defaultValue1",
    		"defaultKey2": "defaultValue2",
    	}),
    )
    
    data, err := req.
        Get("http://httpbin.org/get",
            sreq.WithParams(sreq.Value{
                "key1": "value1",
                "key2": "value2",
            }),
           ).
        Text()
    if err != nil {
        panic(err)
    }
    fmt.Println(data)
    
    data, err = req.
        Get("http://httpbin.org/get",
            sreq.WithParams(sreq.Value{
                "key3": "value3",
                "key4": "value4",
            }),
           ).
        Text()
    if err != nil {
        panic(err)
    }
    fmt.Println(data)
    

    自定义 HTTP 客户端

    sreq 没有提供直接修改传输层、重定向策略、cookie jar、超时、代理或者其它能通过构造 *http.Client 实现配置的 API,你可以通过自定义 sreq 客户端来设置它们。

    transport := &http.Transport{
        Proxy: http.ProxyFromEnvironment,
        DialContext: (&net.Dialer{
            Timeout:   30 * time.Second,
            KeepAlive: 30 * time.Second,
        }).DialContext,
        MaxIdleConns:          100,
        IdleConnTimeout:       90 * time.Second,
        TLSHandshakeTimeout:   10 * time.Second,
        ExpectContinueTimeout: 1 * time.Second,
    }
    redirectPolicy := func(req *http.Request, via []*http.Request) error {
        return http.ErrUseLastResponse
    }
    jar, _ := cookiejar.New(&cookiejar.Options{
        PublicSuffixList: publicsuffix.List,
    })
    timeout := 120 * time.Second
    
    httpClient := &http.Client{
        Transport:     transport,
        CheckRedirect: redirectPolicy,
        Jar:           jar,
        Timeout:       timeout,
    }
    
    req := sreq.New( httpClient)
    data, err := req.
        Get("http://httpbin.org/get").
        Text()
    if err != nil {
        panic(err)
    }
    fmt.Println(data)
    

    并发安全

    sreq 是线程安全的,你可以无障碍地在 goroutines 中使用它。

    const MaxWorker = 1000
    wg := new(sync.WaitGroup)
    
    for i := 0; i < MaxWorker; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
    
            params := sreq.Value{}
            params.Set(fmt.Sprintf("key%d", i), fmt.Sprintf("value%d", i))
    
            data, err := sreq.
                Get("http://httpbin.org/get",
                    sreq.WithParams(params),
                   ).
                Text()
            if err != nil {
                return
            }
    
            fmt.Println(data)
        }(i)
    }
    
    wg.Wait()
    
    5 条回复    2021-04-23 22:42:55 +08:00
    GoLand
        1
    GoLand  
       2019-10-02 15:57:09 +08:00
    Go 里面就不叫线程安全了,bro。
    winterssy
        2
    winterssy  
    OP
       2019-10-02 17:44:44 +08:00 via Android
    @GoLand 那叫协程安全,goroutine 安全?理解意思就行了...
    EdwardLeeJan
        3
    EdwardLeeJan  
       2019-10-02 20:38:35 +08:00 via iPhone
    不错,贴合 http 协议的使用方式
    jedihy
        4
    jedihy  
       2019-10-04 23:58:44 +08:00 via iPhone
    支持 https 吗
    SunspotsInys
        5
    SunspotsInys  
       2021-04-23 22:42:55 +08:00
    是不维护了吗?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   953 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 21:09 · PVG 05:09 · LAX 14:09 · JFK 17:09
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.