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

2019-10-02 15:00:36 +08:00
 winterssy

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

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

功能

安装

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()
3165 次点击
所在节点    分享创造
5 条回复
GoLand
2019-10-02 15:57:09 +08:00
Go 里面就不叫线程安全了,bro。
winterssy
2019-10-02 17:44:44 +08:00
@GoLand 那叫协程安全,goroutine 安全?理解意思就行了...
EdwardLeeJan
2019-10-02 20:38:35 +08:00
不错,贴合 http 协议的使用方式
jedihy
2019-10-04 23:58:44 +08:00
支持 https 吗
SunspotsInys
2021-04-23 22:42:55 +08:00
是不维护了吗?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/606006

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX