Simple HTTP and REST client library for Go

Resty

Simple HTTP and REST client library for Go (inspired by Ruby rest-client)

Features section describes in detail about Resty capabilities

Build Status Code Coverage Go Report Card Release Version GoDoc License Mentioned in Awesome Go

Resty Communication Channels

Chat on Gitter - Resty Community Twitter @go_resty

News

  • v2.4.0 released and tagged on Jan 11, 2021.
  • v2.3.0 released and tagged on May 20, 2020.
  • v2.0.0 released and tagged on Jul 16, 2019.
  • v1.12.0 released and tagged on Feb 27, 2019.
  • v1.0 released and tagged on Sep 25, 2017. - Resty's first version was released on Sep 15, 2015 then it grew gradually as a very handy and helpful library. Its been a two years since first release. I'm very thankful to Resty users and its contributors.

Features

  • GET, POST, PUT, DELETE, HEAD, PATCH, OPTIONS, etc.
  • Simple and chainable methods for settings and request
  • Request Body can be string, []byte, struct, map, slice and io.Reader too
    • Auto detects Content-Type
    • Buffer less processing for io.Reader
    • Native *http.Request instance may be accessed during middleware and request execution via Request.RawRequest
    • Request Body can be read multiple times via Request.RawRequest.GetBody()
  • Response object gives you more possibility
    • Access as []byte array - response.Body() OR Access as string - response.String()
    • Know your response.Time() and when we response.ReceivedAt()
  • Automatic marshal and unmarshal for JSON and XML content type
  • Easy to upload one or more file(s) via multipart/form-data
    • Auto detects file content type
  • Request URL Path Params (aka URI Params)
  • Backoff Retry Mechanism with retry condition function reference
  • Resty client HTTP & REST Request and Response middlewares
  • Request.SetContext supported
  • Authorization option of BasicAuth and Bearer token
  • Set request ContentLength value for all request or particular request
  • Custom Root Certificates and Client Certificates
  • Download/Save HTTP response directly into File, like curl -o flag. See SetOutputDirectory & SetOutput.
  • Cookies for your request and CookieJar support
  • SRV Record based request instead of Host URL
  • Client settings like Timeout, RedirectPolicy, Proxy, TLSClientConfig, Transport, etc.
  • Optionally allows GET request with payload, see SetAllowGetMethodPayload
  • Supports registering external JSON library into resty, see how to use
  • Exposes Response reader without reading response (no auto-unmarshaling) if need be, see how to use
  • Option to specify expected Content-Type when response Content-Type header missing. Refer to #92
  • Resty design
    • Have client level settings & options and also override at Request level if you want to
    • Request and Response middleware
    • Create Multiple clients if you want to resty.New()
    • Supports http.RoundTripper implementation, see SetTransport
    • goroutine concurrent safe
    • Resty Client trace, see Client.EnableTrace and Request.EnableTrace
      • Since v2.4.0, trace info contains a RequestAttempt value, and the Request object contains an Attempt attribute
    • Debug mode - clean and informative logging presentation
    • Gzip - Go does it automatically also resty has fallback handling too
    • Works fine with HTTP/2 and HTTP/1.1
  • Bazel support
  • Easily mock Resty for testing, for e.g.
  • Well tested client library

Included Batteries

  • Redirect Policies - see how to use
    • NoRedirectPolicy
    • FlexibleRedirectPolicy
    • DomainCheckRedirectPolicy
    • etc. more info
  • Retry Mechanism how to use
    • Backoff Retry
    • Conditional Retry
  • SRV Record based request instead of Host URL how to use
  • etc (upcoming - throw your idea's here).

Supported Go Versions

Initially Resty started supporting go modules since v1.10.0 release.

Starting Resty v2 and higher versions, it fully embraces go modules package release. It requires a Go version capable of understanding /vN suffixed imports:

  • 1.9.7+
  • 1.10.3+
  • 1.11+

It might be beneficial for your project 😄

Resty author also published following projects for Go Community.

  • aah framework - A secure, flexible, rapid Go web framework.
  • THUMBAI - Go Mod Repository, Go Vanity Service and Simple Proxy Server.
  • go-model - Robust & Easy to use model mapper and utility methods for Go struct.

Installation

# Go Modules
require github.com/go-resty/resty/v2 v2.4.0

Usage

The following samples will assist you to become as comfortable as possible with resty library.

// Import resty into your code and refer it as `resty`.
import "github.com/go-resty/resty/v2"

Simple GET

// Create a Resty Client
client := resty.New()

resp, err := client.R().
    EnableTrace().
    Get("https://httpbin.org/get")

// Explore response object
fmt.Println("Response Info:")
fmt.Println("  Error      :", err)
fmt.Println("  Status Code:", resp.StatusCode())
fmt.Println("  Status     :", resp.Status())
fmt.Println("  Proto      :", resp.Proto())
fmt.Println("  Time       :", resp.Time())
fmt.Println("  Received At:", resp.ReceivedAt())
fmt.Println("  Body       :\n", resp)
fmt.Println()

// Explore trace info
fmt.Println("Request Trace Info:")
ti := resp.Request.TraceInfo()
fmt.Println("  DNSLookup     :", ti.DNSLookup)
fmt.Println("  ConnTime      :", ti.ConnTime)
fmt.Println("  TCPConnTime   :", ti.TCPConnTime)
fmt.Println("  TLSHandshake  :", ti.TLSHandshake)
fmt.Println("  ServerTime    :", ti.ServerTime)
fmt.Println("  ResponseTime  :", ti.ResponseTime)
fmt.Println("  TotalTime     :", ti.TotalTime)
fmt.Println("  IsConnReused  :", ti.IsConnReused)
fmt.Println("  IsConnWasIdle :", ti.IsConnWasIdle)
fmt.Println("  ConnIdleTime  :", ti.ConnIdleTime)
fmt.Println("  RequestAttempt:", ti.RequestAttempt)
fmt.Println("  RemoteAddr    :", ti.RemoteAddr.String())

/* Output
Response Info:
  Error      : <nil>
  Status Code: 200
  Status     : 200 OK
  Proto      : HTTP/2.0
  Time       : 457.034718ms
  Received At: 2020-09-14 15:35:29.784681 -0700 PDT m=+0.458137045
  Body       :
  {
    "args": {},
    "headers": {
      "Accept-Encoding": "gzip",
      "Host": "httpbin.org",
      "User-Agent": "go-resty/2.4.0 (https://github.com/go-resty/resty)",
      "X-Amzn-Trace-Id": "Root=1-5f5ff031-000ff6292204aa6898e4de49"
    },
    "origin": "0.0.0.0",
    "url": "https://httpbin.org/get"
  }

Request Trace Info:
  DNSLookup     : 4.074657ms
  ConnTime      : 381.709936ms
  TCPConnTime   : 77.428048ms
  TLSHandshake  : 299.623597ms
  ServerTime    : 75.414703ms
  ResponseTime  : 79.337µs
  TotalTime     : 457.034718ms
  IsConnReused  : false
  IsConnWasIdle : false
  ConnIdleTime  : 0s
  RequestAttempt: 1
  RemoteAddr    : 3.221.81.55:443
*/

Enhanced GET

// Create a Resty Client
client := resty.New()

resp, err := client.R().
      SetQueryParams(map[string]string{
          "page_no": "1",
          "limit": "20",
          "sort":"name",
          "order": "asc",
          "random":strconv.FormatInt(time.Now().Unix(), 10),
      }).
      SetHeader("Accept", "application/json").
      SetAuthToken("BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F").
      Get("/search_result")


// Sample of using Request.SetQueryString method
resp, err := client.R().
      SetQueryString("productId=232&template=fresh-sample&cat=resty&source=google&kw=buy a lot more").
      SetHeader("Accept", "application/json").
      SetAuthToken("BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F").
      Get("/show_product")


// If necessary, you can force response content type to tell Resty to parse a JSON response into your struct
resp, err := client.R().
      SetResult(result).
      ForceContentType("application/json").
      Get("v2/alpine/manifests/latest")

Various POST method combinations

// Create a Resty Client
client := resty.New()

// POST JSON string
// No need to set content type, if you have client level setting
resp, err := client.R().
      SetHeader("Content-Type", "application/json").
      SetBody(`{"username":"testuser", "password":"testpass"}`).
      SetResult(&AuthSuccess{}).    // or SetResult(AuthSuccess{}).
      Post("https://myapp.com/login")

// POST []byte array
// No need to set content type, if you have client level setting
resp, err := client.R().
      SetHeader("Content-Type", "application/json").
      SetBody([]byte(`{"username":"testuser", "password":"testpass"}`)).
      SetResult(&AuthSuccess{}).    // or SetResult(AuthSuccess{}).
      Post("https://myapp.com/login")

// POST Struct, default is JSON content type. No need to set one
resp, err := client.R().
      SetBody(User{Username: "testuser", Password: "testpass"}).
      SetResult(&AuthSuccess{}).    // or SetResult(AuthSuccess{}).
      SetError(&AuthError{}).       // or SetError(AuthError{}).
      Post("https://myapp.com/login")

// POST Map, default is JSON content type. No need to set one
resp, err := client.R().
      SetBody(map[string]interface{}{"username": "testuser", "password": "testpass"}).
      SetResult(&AuthSuccess{}).    // or SetResult(AuthSuccess{}).
      SetError(&AuthError{}).       // or SetError(AuthError{}).
      Post("https://myapp.com/login")

// POST of raw bytes for file upload. For example: upload file to Dropbox
fileBytes, _ := ioutil.ReadFile("/Users/jeeva/mydocument.pdf")

// See we are not setting content-type header, since go-resty automatically detects Content-Type for you
resp, err := client.R().
      SetBody(fileBytes).
      SetContentLength(true).          // Dropbox expects this value
      SetAuthToken("<your-auth-token>").
      SetError(&DropboxError{}).       // or SetError(DropboxError{}).
      Post("https://content.dropboxapi.com/1/files_put/auto/resty/mydocument.pdf") // for upload Dropbox supports PUT too

// Note: resty detects Content-Type for request body/payload if content type header is not set.
//   * For struct and map data type defaults to 'application/json'
//   * Fallback is plain text content type

Sample PUT

You can use various combinations of PUT method call like demonstrated for POST.

// Note: This is one sample of PUT method usage, refer POST for more combination

// Create a Resty Client
client := resty.New()

// Request goes as JSON content type
// No need to set auth token, error, if you have client level settings
resp, err := client.R().
      SetBody(Article{
        Title: "go-resty",
        Content: "This is my article content, oh ya!",
        Author: "Jeevanandam M",
        Tags: []string{"article", "sample", "resty"},
      }).
      SetAuthToken("C6A79608-782F-4ED0-A11D-BD82FAD829CD").
      SetError(&Error{}).       // or SetError(Error{}).
      Put("https://myapp.com/article/1234")

Sample PATCH

You can use various combinations of PATCH method call like demonstrated for POST.

// Note: This is one sample of PUT method usage, refer POST for more combination

// Create a Resty Client
client := resty.New()

// Request goes as JSON content type
// No need to set auth token, error, if you have client level settings
resp, err := client.R().
      SetBody(Article{
        Tags: []string{"new tag1", "new tag2"},
      }).
      SetAuthToken("C6A79608-782F-4ED0-A11D-BD82FAD829CD").
      SetError(&Error{}).       // or SetError(Error{}).
      Patch("https://myapp.com/articles/1234")

Sample DELETE, HEAD, OPTIONS

// Create a Resty Client
client := resty.New()

// DELETE a article
// No need to set auth token, error, if you have client level settings
resp, err := client.R().
      SetAuthToken("C6A79608-782F-4ED0-A11D-BD82FAD829CD").
      SetError(&Error{}).       // or SetError(Error{}).
      Delete("https://myapp.com/articles/1234")

// DELETE a articles with payload/body as a JSON string
// No need to set auth token, error, if you have client level settings
resp, err := client.R().
      SetAuthToken("C6A79608-782F-4ED0-A11D-BD82FAD829CD").
      SetError(&Error{}).       // or SetError(Error{}).
      SetHeader("Content-Type", "application/json").
      SetBody(`{article_ids: [1002, 1006, 1007, 87683, 45432] }`).
      Delete("https://myapp.com/articles")

// HEAD of resource
// No need to set auth token, if you have client level settings
resp, err := client.R().
      SetAuthToken("C6A79608-782F-4ED0-A11D-BD82FAD829CD").
      Head("https://myapp.com/videos/hi-res-video")

// OPTIONS of resource
// No need to set auth token, if you have client level settings
resp, err := client.R().
      SetAuthToken("C6A79608-782F-4ED0-A11D-BD82FAD829CD").
      Options("https://myapp.com/servers/nyc-dc-01")

Multipart File(s) upload

Using io.Reader

profileImgBytes, _ := ioutil.ReadFile("/Users/jeeva/test-img.png")
notesBytes, _ := ioutil.ReadFile("/Users/jeeva/text-file.txt")

// Create a Resty Client
client := resty.New()

resp, err := client.R().
      SetFileReader("profile_img", "test-img.png", bytes.NewReader(profileImgBytes)).
      SetFileReader("notes", "text-file.txt", bytes.NewReader(notesBytes)).
      SetFormData(map[string]string{
          "first_name": "Jeevanandam",
          "last_name": "M",
      }).
      Post("http://myapp.com/upload")

Using File directly from Path

// Create a Resty Client
client := resty.New()

// Single file scenario
resp, err := client.R().
      SetFile("profile_img", "/Users/jeeva/test-img.png").
      Post("http://myapp.com/upload")

// Multiple files scenario
resp, err := client.R().
      SetFiles(map[string]string{
        "profile_img": "/Users/jeeva/test-img.png",
        "notes": "/Users/jeeva/text-file.txt",
      }).
      Post("http://myapp.com/upload")

// Multipart of form fields and files
resp, err := client.R().
      SetFiles(map[string]string{
        "profile_img": "/Users/jeeva/test-img.png",
        "notes": "/Users/jeeva/text-file.txt",
      }).
      SetFormData(map[string]string{
        "first_name": "Jeevanandam",
        "last_name": "M",
        "zip_code": "00001",
        "city": "my city",
        "access_token": "C6A79608-782F-4ED0-A11D-BD82FAD829CD",
      }).
      Post("http://myapp.com/profile")

Sample Form submission

// Create a Resty Client
client := resty.New()

// just mentioning about POST as an example with simple flow
// User Login
resp, err := client.R().
      SetFormData(map[string]string{
        "username": "jeeva",
        "password": "mypass",
      }).
      Post("http://myapp.com/login")

// Followed by profile update
resp, err := client.R().
      SetFormData(map[string]string{
        "first_name": "Jeevanandam",
        "last_name": "M",
        "zip_code": "00001",
        "city": "new city update",
      }).
      Post("http://myapp.com/profile")

// Multi value form data
criteria := url.Values{
  "search_criteria": []string{"book", "glass", "pencil"},
}
resp, err := client.R().
      SetFormDataFromValues(criteria).
      Post("http://myapp.com/search")

Save HTTP Response into File

// Create a Resty Client
client := resty.New()

// Setting output directory path, If directory not exists then resty creates one!
// This is optional one, if you're planning using absoule path in
// `Request.SetOutput` and can used together.
client.SetOutputDirectory("/Users/jeeva/Downloads")

// HTTP response gets saved into file, similar to curl -o flag
_, err := client.R().
          SetOutput("plugin/ReplyWithHeader-v5.1-beta.zip").
          Get("http://bit.ly/1LouEKr")

// OR using absolute path
// Note: output directory path is not used for absolute path
_, err := client.R().
          SetOutput("/MyDownloads/plugin/ReplyWithHeader-v5.1-beta.zip").
          Get("http://bit.ly/1LouEKr")

Request URL Path Params

Resty provides easy to use dynamic request URL path params. Params can be set at client and request level. Client level params value can be overridden at request level.

// Create a Resty Client
client := resty.New()

client.R().SetPathParams(map[string]string{
   "userId": "[email protected]",
   "subAccountId": "100002",
}).
Get("/v1/users/{userId}/{subAccountId}/details")

// Result:
//   Composed URL - /v1/users/[email protected]/100002/details

Request and Response Middleware

Resty provides middleware ability to manipulate for Request and Response. It is more flexible than callback approach.

// Create a Resty Client
client := resty.New()

// Registering Request Middleware
client.OnBeforeRequest(func(c *resty.Client, req *resty.Request) error {
    // Now you have access to Client and current Request object
    // manipulate it as per your need

    return nil  // if its success otherwise return error
  })

// Registering Response Middleware
client.OnAfterResponse(func(c *resty.Client, resp *resty.Response) error {
    // Now you have access to Client and current Response object
    // manipulate it as per your need

    return nil  // if its success otherwise return error
  })

OnError Hooks

Resty provides OnError hooks that may be called because:

  • The client failed to send the request due to connection timeout, TLS handshake failure, etc...
  • The request was retried the maximum amount of times, and still failed.

If there was a response from the server, the original error will be wrapped in *resty.ResponseError which contains the last response received.

// Create a Resty Client
client := resty.New()

client.OnError(func(req *resty.Request, err error) {
  if v, ok := err.(*resty.ResponseError); ok {
    // v.Response contains the last response from the server
    // v.Err contains the original error
  }
  // Log the error, increment a metric, etc...
})

Redirect Policy

Resty provides few ready to use redirect policy(s) also it supports multiple policies together.

// Create a Resty Client
client := resty.New()

// Assign Client Redirect Policy. Create one as per you need
client.SetRedirectPolicy(resty.FlexibleRedirectPolicy(15))

// Wanna multiple policies such as redirect count, domain name check, etc
client.SetRedirectPolicy(resty.FlexibleRedirectPolicy(20),
                        resty.DomainCheckRedirectPolicy("host1.com", "host2.org", "host3.net"))
Custom Redirect Policy

Implement RedirectPolicy interface and register it with resty client. Have a look redirect.go for more information.

// Create a Resty Client
client := resty.New()

// Using raw func into resty.SetRedirectPolicy
client.SetRedirectPolicy(resty.RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error {
  // Implement your logic here

  // return nil for continue redirect otherwise return error to stop/prevent redirect
  return nil
}))

//---------------------------------------------------

// Using struct create more flexible redirect policy
type CustomRedirectPolicy struct {
  // variables goes here
}

func (c *CustomRedirectPolicy) Apply(req *http.Request, via []*http.Request) error {
  // Implement your logic here

  // return nil for continue redirect otherwise return error to stop/prevent redirect
  return nil
}

// Registering in resty
client.SetRedirectPolicy(CustomRedirectPolicy{/* initialize variables */})

Custom Root Certificates and Client Certificates

// Create a Resty Client
client := resty.New()

// Custom Root certificates, just supply .pem file.
// you can add one or more root certificates, its get appended
client.SetRootCertificate("/path/to/root/pemFile1.pem")
client.SetRootCertificate("/path/to/root/pemFile2.pem")
// ... and so on!

// Adding Client Certificates, you add one or more certificates
// Sample for creating certificate object
// Parsing public/private key pair from a pair of files. The files must contain PEM encoded data.
cert1, err := tls.LoadX509KeyPair("certs/client.pem", "certs/client.key")
if err != nil {
  log.Fatalf("ERROR client certificate: %s", err)
}
// ...

// You add one or more certificates
client.SetCertificates(cert1, cert2, cert3)

Custom Root Certificates and Client Certificates from string

// Custom Root certificates from string
// You can pass you certificates throught env variables as strings
// you can add one or more root certificates, its get appended
client.SetRootCertificateFromString("-----BEGIN CERTIFICATE-----content-----END CERTIFICATE-----")
client.SetRootCertificateFromString("-----BEGIN CERTIFICATE-----content-----END CERTIFICATE-----")
// ... and so on!

// Adding Client Certificates, you add one or more certificates
// Sample for creating certificate object
// Parsing public/private key pair from a pair of files. The files must contain PEM encoded data.
cert1, err := tls.X509KeyPair([]byte("-----BEGIN CERTIFICATE-----content-----END CERTIFICATE-----"), []byte("-----BEGIN CERTIFICATE-----content-----END CERTIFICATE-----"))
if err != nil {
  log.Fatalf("ERROR client certificate: %s", err)
}
// ...

// You add one or more certificates
client.SetCertificates(cert1, cert2, cert3)

Proxy Settings - Client as well as at Request Level

Default Go supports Proxy via environment variable HTTP_PROXY. Resty provides support via SetProxy & RemoveProxy. Choose as per your need.

Client Level Proxy settings applied to all the request

// Create a Resty Client
client := resty.New()

// Setting a Proxy URL and Port
client.SetProxy("http://proxyserver:8888")

// Want to remove proxy setting
client.RemoveProxy()

Retries

Resty uses backoff to increase retry intervals after each attempt.

Usage example:

// Create a Resty Client
client := resty.New()

// Retries are configured per client
client.
    // Set retry count to non zero to enable retries
    SetRetryCount(3).
    // You can override initial retry wait time.
    // Default is 100 milliseconds.
    SetRetryWaitTime(5 * time.Second).
    // MaxWaitTime can be overridden as well.
    // Default is 2 seconds.
    SetRetryMaxWaitTime(20 * time.Second).
    // SetRetryAfter sets callback to calculate wait time between retries.
    // Default (nil) implies exponential backoff with jitter
    SetRetryAfter(func(client *resty.Client, resp *resty.Response) (time.Duration, error) {
        return 0, errors.New("quota exceeded")
    })

Above setup will result in resty retrying requests returned non nil error up to 3 times with delay increased after each attempt.

You can optionally provide client with custom retry conditions:

// Create a Resty Client
client := resty.New()

client.AddRetryCondition(
    // RetryConditionFunc type is for retry condition function
    // input: non-nil Response OR request execution error
    func(r *resty.Response, err error) bool {
        return r.StatusCode() == http.StatusTooManyRequests
    },
)

Above example will make resty retry requests ended with 429 Too Many Requests status code.

Multiple retry conditions can be added.

It is also possible to use resty.Backoff(...) to get arbitrary retry scenarios implemented. Reference.

Allow GET request with Payload

// Create a Resty Client
client := resty.New()

// Allow GET request with Payload. This is disabled by default.
client.SetAllowGetMethodPayload(true)

Wanna Multiple Clients

// Here you go!
// Client 1
client1 := resty.New()
client1.R().Get("http://httpbin.org")
// ...

// Client 2
client2 := resty.New()
client2.R().Head("http://httpbin.org")
// ...

// Bend it as per your need!!!

Remaining Client Settings & its Options

// Create a Resty Client
client := resty.New()

// Unique settings at Client level
//--------------------------------
// Enable debug mode
client.SetDebug(true)

// Assign Client TLSClientConfig
// One can set custom root-certificate. Refer: http://golang.org/pkg/crypto/tls/#example_Dial
client.SetTLSClientConfig(&tls.Config{ RootCAs: roots })

// or One can disable security check (https)
client.SetTLSClientConfig(&tls.Config{ InsecureSkipVerify: true })

// Set client timeout as per your need
client.SetTimeout(1 * time.Minute)


// You can override all below settings and options at request level if you want to
//--------------------------------------------------------------------------------
// Host URL for all request. So you can use relative URL in the request
client.SetHostURL("http://httpbin.org")

// Headers for all request
client.SetHeader("Accept", "application/json")
client.SetHeaders(map[string]string{
        "Content-Type": "application/json",
        "User-Agent": "My custom User Agent String",
      })

// Cookies for all request
client.SetCookie(&http.Cookie{
      Name:"go-resty",
      Value:"This is cookie value",
      Path: "/",
      Domain: "sample.com",
      MaxAge: 36000,
      HttpOnly: true,
      Secure: false,
    })
client.SetCookies(cookies)

// URL query parameters for all request
client.SetQueryParam("user_id", "00001")
client.SetQueryParams(map[string]string{ // sample of those who use this manner
      "api_key": "api-key-here",
      "api_secert": "api-secert",
    })
client.R().SetQueryString("productId=232&template=fresh-sample&cat=resty&source=google&kw=buy a lot more")

// Form data for all request. Typically used with POST and PUT
client.SetFormData(map[string]string{
    "access_token": "BC594900-518B-4F7E-AC75-BD37F019E08F",
  })

// Basic Auth for all request
client.SetBasicAuth("myuser", "mypass")

// Bearer Auth Token for all request
client.SetAuthToken("BC594900518B4F7EAC75BD37F019E08FBC594900518B4F7EAC75BD37F019E08F")

// Enabling Content length value for all request
client.SetContentLength(true)

// Registering global Error object structure for JSON/XML request
client.SetError(&Error{})    // or resty.SetError(Error{})

Unix Socket

unixSocket := "/var/run/my_socket.sock"

// Create a Go's http.Transport so we can set it in resty.
transport := http.Transport{
	Dial: func(_, _ string) (net.Conn, error) {
		return net.Dial("unix", unixSocket)
	},
}

// Create a Resty Client
client := resty.New()

// Set the previous transport that we created, set the scheme of the communication to the
// socket and set the unixSocket as the HostURL.
client.SetTransport(&transport).SetScheme("http").SetHostURL(unixSocket)

// No need to write the host's URL on the request, just the path.
client.R().Get("/index.html")

Bazel support

Resty can be built, tested and depended upon via Bazel. For example, to run all tests:

bazel test :go_default_test

Mocking http requests using httpmock library

In order to mock the http requests when testing your application you could use the httpmock library.

When using the default resty client, you should pass the client to the library as follow:

// Create a Resty Client
client := resty.New()

// Get the underlying HTTP Client and set it to Mock
httpmock.ActivateNonDefault(client.GetClient())

More detailed example of mocking resty http requests using ginko could be found here.

Versioning

Resty releases versions according to Semantic Versioning

  • Resty v2 does not use gopkg.in service for library versioning.
  • Resty fully adapted to go mod capabilities since v1.10.0 release.
  • Resty v1 series was using gopkg.in to provide versioning. gopkg.in/resty.vX points to appropriate tagged versions; X denotes version series number and it's a stable release for production use. For e.g. gopkg.in/resty.v0.
  • Development takes place at the master branch. Although the code in master should always compile and test successfully, it might break API's. I aim to maintain backwards compatibility, but sometimes API's and behavior might be changed to fix a bug.

Contribution

I would welcome your contribution! If you find any improvement or issue you want to fix, feel free to send a pull request, I like pull requests that include test cases for fix/enhancement. I have done my best to bring pretty good code coverage. Feel free to write tests.

BTW, I'd like to know what you think about Resty. Kindly open an issue or send me an email; it'd mean a lot to me.

Creator

Jeevanandam M. ([email protected])

Core Team

Have a look on Members page.

Contributors

Have a look on Contributors page.

License

Resty released under MIT license, refer LICENSE file.

Owner
Go Resty
Simple HTTP and REST client library for Go
Go Resty
Comments
  • Collecting Inputs for Resty v2.0 and Plan the release

    Collecting Inputs for Resty v2.0 and Plan the release

    The goal of this thread to collect feedback's from resty users and plan v2.0.0 release. Action items get recorded here #167.

    • Things could be improved
    • Things could be added (generalized one's not specific use case)
    • Things could be removed

    Share your resty experiences for upcoming v2.0.0

  • Go Module

    Go Module

    The documentation suggests that one should use resty as follows:

    import "gopkg.in/resty.v1"
    

    (https://github.com/go-resty/resty/blob/ec17de1c59a99e0743dac77aaab6435f364fdc42/README.md#usage)

    But this brakes with Go modules, because go.mod declares the module as follows:

    https://github.com/go-resty/resty/blob/ec17de1c59a99e0743dac77aaab6435f364fdc42/go.mod#L1

    This causes troubles when I try to compile code with go11.1rc2 and go modules enabled.

    All is fine when I import resty like this:

    import "github.com/go-resty/resty"
    

    Now there are two options:

    a) Adjust the documentation and suggest to import "github.com/go-resty/resty" b) Adjust the go.mod to be module "gopkg.in/resty.v1

    Option (a) is good for all new project that start with go modules right away, but it's backward incompatible. Option (b) would be good for projects that used plain go get or dep until now and upgrade to go.mod, because they don't have to change all the import statements. There are no further drawbacks, except that this option needs a new release with an updated go.mod.

  • Potential race in resty with combination of retries and the buffers pool

    Potential race in resty with combination of retries and the buffers pool

    We have a golang project that uses resty as the http client.
    We started using the golang race detector in our staging environment and encountered the following race errors:

    ==================
    WARNING: DATA RACE
    Write at 0x00c00025e610 by goroutine 149:
      bytes.(*Buffer).Read()
          /usr/local/go/src/bytes/buffer.go:299 +0x4a
      io/ioutil.(*nopCloser).Read()
          <autogenerated>:1 +0x87
      net/http.(*http2clientStream).writeRequestBody()
          /usr/local/go/src/net/http/h2_bundle.go:7825 +0x635
      net/http.(*http2Transport).getBodyWriterState.func1()
          /usr/local/go/src/net/http/h2_bundle.go:9040 +0x13d
    
    Previous write at 0x00c00025e610 by goroutine 109:
      bytes.(*Buffer).Read()
          /usr/local/go/src/bytes/buffer.go:299 +0x4a
      io/ioutil.(*nopCloser).Read()
          <autogenerated>:1 +0x87
      net/http.(*http2clientStream).writeRequestBody()
          /usr/local/go/src/net/http/h2_bundle.go:7825 +0x635
      net/http.(*http2Transport).getBodyWriterState.func1()
          /usr/local/go/src/net/http/h2_bundle.go:9040 +0x13d
    
    Goroutine 149 (running) created at:
      net/http.http2bodyWriterState.scheduleBodyWrite()
          /usr/local/go/src/net/http/h2_bundle.go:9087 +0xb8
      net/http.(*http2ClientConn).roundTrip()
          /usr/local/go/src/net/http/h2_bundle.go:7626 +0x81b
      net/http.(*http2Transport).RoundTripOpt()
          /usr/local/go/src/net/http/h2_bundle.go:7037 +0x22d
      net/http.(*http2Transport).RoundTrip()
          /usr/local/go/src/net/http/h2_bundle.go:6999 +0x4b
      net/http.(*Transport).roundTrip()
          /usr/local/go/src/net/http/transport.go:463 +0xa21
      net/http.(*Transport).RoundTrip()
          /usr/local/go/src/net/http/roundtrip.go:17 +0x42
      net/http.send()
          /usr/local/go/src/net/http/client.go:250 +0x304
      net/http.(*Client).send()
          /usr/local/go/src/net/http/client.go:174 +0x1ca
      net/http.(*Client).do()
          /usr/local/go/src/net/http/client.go:641 +0x53a
      net/http.(*Client).Do()
          /usr/local/go/src/net/http/client.go:509 +0x42
      github.com/Rookout/golib/vendor/gopkg.in/resty%2ev1.(*Client).execute()
          /go/src/github.com/Rookout/golib/vendor/gopkg.in/resty.v1/client.go:819 +0x3dd
      github.com/Rookout/golib/vendor/gopkg.in/resty%2ev1.(*Request).Execute.func1()
          /go/src/github.com/Rookout/golib/vendor/gopkg.in/resty.v1/request.go:482 +0x1c4
      github.com/Rookout/golib/vendor/gopkg.in/resty%2ev1.Backoff()
          /go/src/github.com/Rookout/golib/vendor/gopkg.in/resty.v1/retry.go:85 +0x330
      github.com/Rookout/golib/vendor/gopkg.in/resty%2ev1.(*Request).Execute()
          /go/src/github.com/Rookout/golib/vendor/gopkg.in/resty.v1/request.go:476 +0x58c
      github.com/Rookout/golib/pkg/routes.(*CrudSession).executeInternal()
          /go/src/github.com/Rookout/golib/pkg/routes/crud_session.go:168 +0x47d
      github.com/Rookout/golib/pkg/routes.(*CrudSession).Execute()
          /go/src/github.com/Rookout/golib/pkg/routes/crud_session.go:175 +0xb8
      github.com/Rookout/golib/pkg/routes.(*CrudClient).Post()
          /go/src/github.com/Rookout/golib/pkg/routes/crud_client.go:42 +0x132
      github.com/Rookout/golib/pkg/control.(*Notifications).SendRookRuleStatus.func1()
          /go/src/github.com/Rookout/golib/pkg/control/notifications.go:172 +0x13c
    
    Goroutine 109 (finished) created at:
      net/http.http2bodyWriterState.scheduleBodyWrite()
          /usr/local/go/src/net/http/h2_bundle.go:9087 +0xb8
      net/http.(*http2ClientConn).roundTrip()
          /usr/local/go/src/net/http/h2_bundle.go:7626 +0x81b
      net/http.(*http2Transport).RoundTripOpt()
          /usr/local/go/src/net/http/h2_bundle.go:7037 +0x22d
      net/http.(*http2Transport).RoundTrip()
          /usr/local/go/src/net/http/h2_bundle.go:6999 +0x4b
      net/http.http2noDialH2RoundTripper.RoundTrip()
          /usr/local/go/src/net/http/h2_bundle.go:1019 +0x46
      net/http.(*Transport).roundTrip()
          /usr/local/go/src/net/http/transport.go:415 +0x1269
      net/http.(*Transport).RoundTrip()
          /usr/local/go/src/net/http/roundtrip.go:17 +0x42
      net/http.send()
          /usr/local/go/src/net/http/client.go:250 +0x304
      net/http.(*Client).send()
          /usr/local/go/src/net/http/client.go:174 +0x1ca
      net/http.(*Client).do()
          /usr/local/go/src/net/http/client.go:641 +0x53a
      net/http.(*Client).Do()
          /usr/local/go/src/net/http/client.go:509 +0x42
      github.com/Rookout/golib/vendor/gopkg.in/resty%2ev1.(*Client).execute()
          /go/src/github.com/Rookout/golib/vendor/gopkg.in/resty.v1/client.go:819 +0x3dd
      github.com/Rookout/golib/vendor/gopkg.in/resty%2ev1.(*Request).Execute.func1()
          /go/src/github.com/Rookout/golib/vendor/gopkg.in/resty.v1/request.go:482 +0x1c4
      github.com/Rookout/golib/vendor/gopkg.in/resty%2ev1.Backoff()
          /go/src/github.com/Rookout/golib/vendor/gopkg.in/resty.v1/retry.go:85 +0x330
      github.com/Rookout/golib/vendor/gopkg.in/resty%2ev1.(*Request).Execute()
          /go/src/github.com/Rookout/golib/vendor/gopkg.in/resty.v1/request.go:476 +0x58c
      github.com/Rookout/golib/pkg/routes.(*CrudSession).executeInternal()
          /go/src/github.com/Rookout/golib/pkg/routes/crud_session.go:168 +0x47d
      github.com/Rookout/golib/pkg/routes.(*CrudSession).Execute()
          /go/src/github.com/Rookout/golib/pkg/routes/crud_session.go:175 +0xb8
      github.com/Rookout/golib/pkg/routes.(*CrudClient).Post()
          /go/src/github.com/Rookout/golib/pkg/routes/crud_client.go:42 +0x132
      github.com/Rookout/golib/pkg/control.(*Notifications).SendRookRuleStatus.func1()
          /go/src/github.com/Rookout/golib/pkg/control/notifications.go:172 +0x13c
    ==================
    

    The code is not open source atm so I cannot show you exactly what wer'e doing but the main takeaways are:

    1. We share a single client across multiple go routines
    2. We create a new request struct for every request we execute (client.NewRequest())
    3. We use the retry feature Any idea what's the problem here? Is it a false positive or not?
      Thanks
  • Verifying module, checksum mismatch (with new version 2.5.0)

    Verifying module, checksum mismatch (with new version 2.5.0)

    I'm getting this error after upgrading from resty 2.4.0 to 2.5.0:

    github.com/go-resty/resty/[email protected]: verifying module: checksum mismatch
            downloaded: h1:GmCjQ7qW3Dzr3Lh7FvTrRJSWgYNWswDdOy+T8sgTAFI=
            sum.golang.org: h1:WFb5bD49/85PO7WgAjZ+/TJQ+Ty1XOcWEfD1zIFCM1c=
    
    SECURITY ERROR
    This download does NOT match the one reported by the checksum server.
    The bits may have been replaced on the origin server, or an attacker may
    have intercepted the download attempt.
    
    For more information, see 'go help module-auth'.
    

    Already did:

    go clean -modcache
    go get -u ./... && go mod tidy
    

    Any ideas? Thanks!

  • Big file upload

    Big file upload

    Hi,

    i tried my luck with the other issues, sorry if this is a duplicate.

    Is it possible to upload files without loading the entire content into memory ?

    Cheers !

  • v2 Release error with go mod

    v2 Release error with go mod

    I have tried to make a release v2 with the following approach

    • Merging into master
    • Having v2 branch

    Not sure, what is missing? ran into an error. So reverted the master back to the previous state.

    May be go mod vs github.com/go-resty/resty vs gopkg.in/resty.v1 - Need an investigation!!

    go: finding github.com/go-resty/resty/v2 v2.0.0
    go: finding golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b
    go: finding golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
    go: finding golang.org/x/text v0.3.0
    go: finding golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a
    go: finding github.com/go-resty/resty v1.12.0
    go: downloading github.com/go-resty/resty v1.12.0
    go: extracting github.com/go-resty/resty v1.12.0
    go: github.com/go-resty/[email protected]: parsing go.mod: unexpected module path "gopkg.in/resty.v1"
    go: error loading module requirements
    
  • Resty v2 release plan and Create migration guide from v1 to v2

    Resty v2 release plan and Create migration guide from v1 to v2

    This issue is to provide v2 release plan and track the migration guide from v1.x to v2.x

    Release Plan:

    • Create a v1 branch from master branch
      • v1 branch will be kept for critical bug fixes only for 6 months, no enhancements and no feature additions
      • Any bug fixes will be ported to master branch to reflect on v2 series
      • v1 will be unsupported and locked down in 6 months from the date of v2 release
    • Make Release Candidates
      • Fix any issues arises
    • Merge v2 branch into the master branch
    • Make a v2.0.0 release from the master branch

    Release Candidates:

    v2.0.0-rc.1 is ready to try it out.

    # add it go.mod
    github.com/go-resty/resty v2.0.0-rc.1
    

    Migration Guide: (In-progress)

    v2 brings https://github.com/go-resty/resty/issues?q=is%3Aclosed+milestone%3A%22v2.0.0+Milestone%22

  • DATA RACE in resty.(*Client).execute()

    DATA RACE in resty.(*Client).execute()

    Hello, I am use resty in multi-goroutine and case the DATA RACE

    test.go `package main

    import ( "github.com/go-resty/resty" "sync" )

    func main() { w := sync.WaitGroup{} for i := 0; i < 50; i++ { w.Add(1) go func() { defer w.Done() resty.R().Get("http://httpbin.org/get") }() } w.Wait() }go run -race test.go`

  • Support Error marshalling using application/problem+json content type

    Support Error marshalling using application/problem+json content type

    I just found out that Mailchimp uses this header which is unfortunate and unusual afaik but there is a spec: https://tools.ietf.org/html/rfc7807 that seems to define this.

    It would be nice to be able to use the SetError() functionality.

  • How to set Content-Length header

    How to set Content-Length header

    I'm trying to call an API, that expects 'Content-Length' = 0 for a HTTP PATCH Request. There is no request body for this request.

    It seems golang (from 1.8) does not allow to set Content-length with zero bytes. ( pls see -- https://github.com/golang/go/issues/20257)

    How do I set the header? I have tried

    restyClient.SetBody(http.NoBody).SetContentLength(true)
    

    And

    reqHeader := map[string]string {
    		"Content-Length": "0",
    }
    

    None of these 2 ways set the content-length header.

  • why resty very slow

    why resty very slow

    Resty vs Curl

    • Resty vs Curl
    • Access Same URL

    Result

    Resty + fmt :

    time ./main
    real	0m11.247s
    user	0m0.045s
    sys	0m0.027s
    

    curl :

    time curl "sameurl"
    real	0m1.373s
    user	0m0.069s
    sys	0m0.020s
    

    Code

    package main
    
    import (
    	"fmt"
    )
    
    import (
    	"gopkg.in/resty.v1"
    )
    
    func main() {
    	resp, _ := resty.R().Get("https://someurl")
    	fmt.Println(resp)
    }
    
  • How did the panic happen???

    How did the panic happen???

    Version: github.com/go-resty/resty/v2 v2.7.0

    import (
     "github.com/go-resty/resty/v2" 
     "fmt"
    )
    
    client := resty.New()
    response, err := client.R().Get("https://koooralive1.gq")
    fmt.Println("response, err")
    

    the panic message:

    net/http.(*http2pipe).closeWithError(0xc00003d001, 0xc00026ba18, {0x0, 0x0}, 0xc0001e79a8)
            /usr/lib/go/src/net/http/h2_bundle.go:3648 +0x23c
    net/http.(*http2pipe).CloseWithError(...)
            /usr/lib/go/src/net/http/h2_bundle.go:3635
    net/http.(*http2clientConnReadLoop).cleanup(0xc00026bb38)
            /usr/lib/go/src/net/http/h2_bundle.go:8404 +0x405
    panic({0x7acd60, 0xcc7210})
            /usr/lib/go/src/runtime/panic.go:1038 +0x215
    net/http.(*http2pipe).Write(0xc0002d0c00, {0xc00036e000, 0xc0002ca620, 0x8aa720})
            /usr/lib/go/src/net/http/h2_bundle.go:3627 +0x17b
    net/http.(*http2clientConnReadLoop).processData(0xc00026bfa0, 0xc000370120)
            /usr/lib/go/src/net/http/h2_bundle.go:8886 +0x41b
    net/http.(*http2clientConnReadLoop).run(0xc00026bfa0)
            /usr/lib/go/src/net/http/h2_bundle.go:8466 +0x4e7
    net/http.(*http2ClientConn).readLoop(0xc0002d0c00)
            /usr/lib/go/src/net/http/h2_bundle.go:8350 +0x79
    created by net/http.(*http2Transport).newClientConn
            /usr/lib/go/src/net/http/h2_bundle.go:7302 +0xb45
    panic: runtime error: invalid memory address or nil pointer dereference
            panic: err must be non-nil
    [signal SIGSEGV: segmentation violation code=0x1 addr=0x28 pc=0x64b63b]
    
  • Issues in using singleton resty

    Issues in using singleton resty

    should i create singleton resty in my project, converted my simple resty to singleton and faced the issue,

    request going to completely different domain which is not specified in url

    did anyone also faced the same?

  • Is this project alive??

    Is this project alive??

    Hi! I noticed there's not much going on with the project - Issues and PR's are piling. Resty seems to be one of the more popular clients out there, it would be a waste if it just slowly ceased to work.

    Are there any problems with maintenance, can I help?

  • Protocol not available when updating from go 1.17 to 1.19

    Protocol not available when updating from go 1.17 to 1.19

    I'm running resty in a wam application

    I got this error with default example :

    Error: Get "http://httpbin.org/get": dial tcp: lookup httpbin.org: Protocol not available
    wasm_exec.js:19 Response Status Code: 0
    wasm_exec.js:19 Response Status: 
    wasm_exec.js:19 Response Body: 
    wasm_exec.js:19 Response Time: 400.128µs
    

    https://gitlab.com/c1560/cryptofiscafacile/-/merge_requests/824

    I have tested http.get successfully

  • Remove default User-Agent header

    Remove default User-Agent header

    See discussion in #604.

    As resty is a generic HTTP client library, I think we should not advertise a User-Agent header. Leave that up to the end user as an implementation detail.

    I doubt this would break many user implementations, but we would need to highlight this as a potential breaking change in the release notes.

    Thoughts?

  • Why is ConnTime so long?

    Why is ConnTime so long?

    ufile download fileName: 67F0C1CA4548440EP08A07B6.jpg, trace: {DNSLookup:1.59315ms ConnTime:10.074076038s TCPConnTime:0s TLSHandshake:0s ServerTime:84.875535ms ResponseTime:20.539µs TotalTime:10.158972112s IsConnReused:true IsConnWasIdle:false ConnIdleTime:0s RequestAttempt:1 RemoteAddr:10.23.255.247:80}, status: 200
    
    // define global client
    defaultTransport := &http.Transport{
    		Proxy:                 http.ProxyFromEnvironment,
    		DialContext:           dialer.DialContext,
    		ForceAttemptHTTP2:     true,
    		MaxIdleConns:          100,
    		IdleConnTimeout:       90 * time.Second,
    		TLSHandshakeTimeout:   10 * time.Second,
    		ExpectContinueTimeout: 1 * time.Second,
    		MaxIdleConnsPerHost:   1000,
    	}
    uRfileClinet = resty.New()
    uRfileClinet.SetTransport(defaultTransport)
    
    // use in gin handler
    ctx, cacel := context.WithTimeout(context.Background(), time.Second*20)
    defer cacel()
    uFileResp, err := uRfileClinet.R().
    EnableTrace().
    SetContext(ctx).
    SetDoNotParseResponse(true).
    Get(url)
    log.LgReqAccess.Sugar().Infof("ufile_upstream_response_time: %s fileName: %s", time.Since(ufileUpstreamStartTime), fileName)
    trace := uFileResp.Request.TraceInfo()
    log.LgReqAccess.Sugar().Infof("ufile download fileName: %s, trace: %+v, status: %d", fileName, trace, uFileResp.StatusCode())
    if err != nil {
      log.LgReqAccess.Sugar().Errorf("ufile download error: %+v, fileName: %s", err, fileName)
      statusCode = uFileResp.StatusCode()
      return
    }
    defer uFileResp.RawBody().Close()
    
    _, err = io.Copy(c.Writer, uFileResp.RawBody())
    
fhttp is a fork of net/http that provides an array of features pertaining to the fingerprint of the golang http client.

fhttp The f stands for flex. fhttp is a fork of net/http that provides an array of features pertaining to the fingerprint of the golang http client. T

Jan 1, 2023
A Go HTTP client library for creating and sending API requests
A Go HTTP client library for creating and sending API requests

Sling Sling is a Go HTTP client library for creating and sending API requests. Slings store HTTP Request properties to simplify sending requests and d

Jan 7, 2023
Speak HTTP like a local. (the simple, intuitive HTTP console, golang version)

http-gonsole This is the Go port of the http-console. Speak HTTP like a local Talking to an HTTP server with curl can be fun, but most of the time it'

Jul 14, 2021
Simple HTTP package that wraps net/http

Simple HTTP package that wraps net/http

Jan 17, 2022
Http-conection - A simple example of how to establish a HTTP connection using Golang

A simple example of how to establish a HTTP connection using Golang

Feb 1, 2022
Cake is a lightweight HTTP client library for GO, inspired by Java Open-Feign.

Cake is a lightweight HTTP client library for GO, inspired by Java Open-Feign. Installation # With Go Modules, recommanded with go version > 1.16

Oct 6, 2022
Simple http client

www Simple http client for golang with user-friendly interface. Features Chainable API Direct file upload Timeout Cookie GZIP Charset detection Cleane

Nov 15, 2021
retryablehttp package provides a familiar HTTP client interface with automatic retries and exponential backoff.

retryablehttp package provides a familiar HTTP client interface with automatic retries and exponential backoff.

Jan 4, 2023
Fast HTTP package for Go. Tuned for high performance. Zero memory allocations in hot paths. Up to 10x faster than net/http
Fast HTTP package for Go. Tuned for high performance. Zero memory allocations in hot paths. Up to 10x faster than net/http

fasthttp Fast HTTP implementation for Go. Currently fasthttp is successfully used by VertaMedia in a production serving up to 200K rps from more than

Jan 2, 2023
NATS HTTP Round Tripper - This is a Golang http.RoundTripper that uses NATS as a transport.

This is a Golang http.RoundTripper that uses NATS as a transport. Included is a http.RoundTripper for clients, a server that uses normal HTTP Handlers and any existing http handler mux and a Caddy Server transport.

Dec 6, 2022
Full-featured, plugin-driven, extensible HTTP client toolkit for Go

gentleman Full-featured, plugin-driven, middleware-oriented toolkit to easily create rich, versatile and composable HTTP clients in Go. gentleman embr

Dec 23, 2022
An enhanced http client for Golang
An enhanced http client for Golang

go-http-client An enhanced http client for Golang Documentation on go.dev ?? This package provides you a http client package for your http requests. Y

Dec 23, 2022
An enhanced HTTP client for Go
An enhanced HTTP client for Go

Heimdall Description Installation Usage Making a simple GET request Creating a hystrix-like circuit breaker Creating a hystrix-like circuit breaker wi

Jan 9, 2023
Enriches the standard go http client with retry functionality.

httpRetry Enriches the standard go http client with retry functionality using a wrapper around the Roundtripper interface. The advantage of this libra

Dec 10, 2022
http client for golang
http client for golang

Request HTTP client for golang, Inspired by Javascript-axios Python-request. If you have experience about axios or requests, you will love it. No 3rd

Dec 18, 2022
A nicer interface for golang stdlib HTTP client

rq A nicer interface for golang stdlib HTTP client Documents rq: here client: here jar: here Why? Because golang HTTP client is a pain in the a... Fea

Dec 12, 2022
a Go HTTP client with timeouts

go-httpclient requires Go 1.1+ as of v0.4.0 the API has been completely re-written for Go 1.1 (for a Go 1.0.x compatible release see 1adef50) Provides

Nov 10, 2022
GoRequest -- Simplified HTTP client ( inspired by nodejs SuperAgent )
GoRequest -- Simplified HTTP client ( inspired by nodejs SuperAgent )

GoRequest GoRequest -- Simplified HTTP client ( inspired by famous SuperAgent lib in Node.js ) "Shooting Requests like a Machine Gun" - Gopher Sending

Jan 1, 2023
gout to become the Swiss Army Knife of the http client @^^@---> gout 是http client领域的瑞士军刀,小巧,强大,犀利。具体用法可看文档,如使用迷惑或者API用得不爽都可提issues
gout to become the Swiss Army Knife of the http client @^^@--->  gout 是http client领域的瑞士军刀,小巧,强大,犀利。具体用法可看文档,如使用迷惑或者API用得不爽都可提issues

gout gout 是go写的http 客户端,为提高工作效率而开发 构架 feature 支持设置 GET/PUT/DELETE/PATH/HEAD/OPTIONS 支持设置请求 http header(可传 struct,map,array,slice 等类型) 支持设置 URL query(可

Dec 29, 2022