A Go HTTP client library for creating and sending API requests

Sling Build Status Coverage GoDoc

Sling is a Go HTTP client library for creating and sending API requests.

Slings store HTTP Request properties to simplify sending requests and decoding responses. Check usage or the examples to learn how to compose a Sling into your API client.

Features

  • Method Setters: Get/Post/Put/Patch/Delete/Head
  • Add or Set Request Headers
  • Base/Path: Extend a Sling for different endpoints
  • Encode structs into URL query parameters
  • Encode a form or JSON into the Request Body
  • Receive JSON success or failure responses

Install

go get github.com/dghubble/sling

Documentation

Read GoDoc

Usage

Use a Sling to set path, method, header, query, or body properties and create an http.Request.

type Params struct {
    Count int `url:"count,omitempty"`
}
params := &Params{Count: 5}

req, err := sling.New().Get("https://example.com").QueryStruct(params).Request()
client.Do(req)

Path

Use Path to set or extend the URL for created Requests. Extension means the path will be resolved relative to the existing URL.

// creates a GET request to https://example.com/foo/bar
req, err := sling.New().Base("https://example.com/").Path("foo/").Path("bar").Request()

Use Get, Post, Put, Patch, Delete, Head, Options, Trace, or Connect which are exactly the same as Path except they set the HTTP method too.

req, err := sling.New().Post("http://upload.com/gophers")

Headers

Add or Set headers for requests created by a Sling.

s := sling.New().Base(baseUrl).Set("User-Agent", "Gophergram API Client")
req, err := s.New().Get("gophergram/list").Request()

Query

QueryStruct

Define url tagged structs. Use QueryStruct to encode a struct as query parameters on requests.

// Github Issue Parameters
type IssueParams struct {
    Filter    string `url:"filter,omitempty"`
    State     string `url:"state,omitempty"`
    Labels    string `url:"labels,omitempty"`
    Sort      string `url:"sort,omitempty"`
    Direction string `url:"direction,omitempty"`
    Since     string `url:"since,omitempty"`
}
githubBase := sling.New().Base("https://api.github.com/").Client(httpClient)

path := fmt.Sprintf("repos/%s/%s/issues", owner, repo)
params := &IssueParams{Sort: "updated", State: "open"}
req, err := githubBase.New().Get(path).QueryStruct(params).Request()

Body

JSON Body

Define JSON tagged structs. Use BodyJSON to JSON encode a struct as the Body on requests.

type IssueRequest struct {
    Title     string   `json:"title,omitempty"`
    Body      string   `json:"body,omitempty"`
    Assignee  string   `json:"assignee,omitempty"`
    Milestone int      `json:"milestone,omitempty"`
    Labels    []string `json:"labels,omitempty"`
}
githubBase := sling.New().Base("https://api.github.com/").Client(httpClient)
path := fmt.Sprintf("repos/%s/%s/issues", owner, repo)

body := &IssueRequest{
    Title: "Test title",
    Body:  "Some issue",
}
req, err := githubBase.New().Post(path).BodyJSON(body).Request()

Requests will include an application/json Content-Type header.

Form Body

Define url tagged structs. Use BodyForm to form url encode a struct as the Body on requests.

type StatusUpdateParams struct {
    Status             string   `url:"status,omitempty"`
    InReplyToStatusId  int64    `url:"in_reply_to_status_id,omitempty"`
    MediaIds           []int64  `url:"media_ids,omitempty,comma"`
}
tweetParams := &StatusUpdateParams{Status: "writing some Go"}
req, err := twitterBase.New().Post(path).BodyForm(tweetParams).Request()

Requests will include an application/x-www-form-urlencoded Content-Type header.

Plain Body

Use Body to set a plain io.Reader on requests created by a Sling.

body := strings.NewReader("raw body")
req, err := sling.New().Base("https://example.com").Body(body).Request()

Set a content type header, if desired (e.g. Set("Content-Type", "text/plain")).

Extend a Sling

Each Sling creates a standard http.Request (e.g. with some path and query params) each time Request() is called. You may wish to extend an existing Sling to minimize duplication (e.g. a common client or base url).

Each Sling instance provides a New() method which creates an independent copy, so setting properties on the child won't mutate the parent Sling.

const twitterApi = "https://api.twitter.com/1.1/"
base := sling.New().Base(twitterApi).Client(authClient)

// statuses/show.json Sling
tweetShowSling := base.New().Get("statuses/show.json").QueryStruct(params)
req, err := tweetShowSling.Request()

// statuses/update.json Sling
tweetPostSling := base.New().Post("statuses/update.json").BodyForm(params)
req, err := tweetPostSling.Request()

Without the calls to base.New(), tweetShowSling and tweetPostSling would reference the base Sling and POST to "https://api.twitter.com/1.1/statuses/show.json/statuses/update.json", which is undesired.

Recap: If you wish to extend a Sling, create a new child copy with New().

Sending

Receive

Define a JSON struct to decode a type from 2XX success responses. Use ReceiveSuccess(successV interface{}) to send a new Request and decode the response body into successV if it succeeds.

// Github Issue (abbreviated)
type Issue struct {
    Title  string `json:"title"`
    Body   string `json:"body"`
}
issues := new([]Issue)
resp, err := githubBase.New().Get(path).QueryStruct(params).ReceiveSuccess(issues)
fmt.Println(issues, resp, err)

Most APIs return failure responses with JSON error details. To decode these, define success and failure JSON structs. Use Receive(successV, failureV interface{}) to send a new Request that will automatically decode the response into the successV for 2XX responses or into failureV for non-2XX responses.

type GithubError struct {
    Message string `json:"message"`
    Errors  []struct {
        Resource string `json:"resource"`
        Field    string `json:"field"`
        Code     string `json:"code"`
    } `json:"errors"`
    DocumentationURL string `json:"documentation_url"`
}
issues := new([]Issue)
githubError := new(GithubError)
resp, err := githubBase.New().Get(path).QueryStruct(params).Receive(issues, githubError)
fmt.Println(issues, githubError, resp, err)

Pass a nil successV or failureV argument to skip JSON decoding into that value.

Modify a Request

Sling provides the raw http.Request so modifications can be made using standard net/http features. For example, in Go 1.7+ , add HTTP tracing to a request with a context:

req, err := sling.New().Get("https://example.com").QueryStruct(params).Request()
// handle error

trace := &httptrace.ClientTrace{
   DNSDone: func(dnsInfo httptrace.DNSDoneInfo) {
      fmt.Printf("DNS Info: %+v\n", dnsInfo)
   },
   GotConn: func(connInfo httptrace.GotConnInfo) {
      fmt.Printf("Got Conn: %+v\n", connInfo)
   },
}

req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
client.Do(req)

Build an API

APIs typically define an endpoint (also called a service) for each type of resource. For example, here is a tiny Github IssueService which lists repository issues.

const baseURL = "https://api.github.com/"

type IssueService struct {
    sling *sling.Sling
}

func NewIssueService(httpClient *http.Client) *IssueService {
    return &IssueService{
        sling: sling.New().Client(httpClient).Base(baseURL),
    }
}

func (s *IssueService) ListByRepo(owner, repo string, params *IssueListParams) ([]Issue, *http.Response, error) {
    issues := new([]Issue)
    githubError := new(GithubError)
    path := fmt.Sprintf("repos/%s/%s/issues", owner, repo)
    resp, err := s.sling.New().Get(path).QueryStruct(params).Receive(issues, githubError)
    if err == nil {
        err = githubError
    }
    return *issues, resp, err
}

Example APIs using Sling

Create a Pull Request to add a link to your own API.

Motivation

Many client libraries follow the lead of google/go-github (our inspiration!), but do so by reimplementing logic common to all clients.

This project borrows and abstracts those ideas into a Sling, an agnostic component any API client can use for creating and sending requests.

Contributing

See the Contributing Guide.

License

MIT License

Owner
Dalton Hubble
Friends with the machines | Kubernetes | @poseidon Typhoon and Matchbox | Previously @lyft, @coreos, @twitter, MIT
Dalton Hubble
Comments
  • Rework body handling to use providers

    Rework body handling to use providers

    Hi !

    Currently, sling only handles JSON, form and raw reader bodies. These 3 types are hard coded in the Sling struct, by having one field for every type.

    While it was still possible to send other types of bodies (xml, multipart, ...), their usage wasn't as fluent as the other 3 types.

    This PR proposes the following:

    • Introduce the BodyProvider interface
    • Rework the existing supported types to use the interface above
    • Add support for multipart and file upload using the same abstraction
    • Keep the existing shortcut body methods to not break clients

    Adding support for a new body types would not require touching the Sling struct. Simply a new implementation of the interface and maybe a factory function would do.

    Users wanting to handle other types can simply implement the BodyProvider and use the API as seamlessly as the baked-in types:

    sling.New().
            Post("http://localhost:4000/customer").
            BodyProvider(YamlBody(customer))
            ...
    

    The code in this PR is not finished (it could use some more polish and tests). I just wanted to get your feedback on this proposal before investing more time and effort on it.

  • Introduce response decoder interface

    Introduce response decoder interface

    I use Sling to get data from JSON APIs, soon I will have the use case to receive data from XML APIs, too. Hence I suggest a way that allows decoding responses from arbitrary sources:

    • Introduce a ResponseDecoder interface that abstracts away the actual decoding
    • Provide a default jsonDecoder that does what the library is doing at the moment: decoding from JSON (keep bc)
    • I also added a new test case, not sure if this is sufficient.
    • Documentation is missing for the new feature, I need to add this.

    Is there a chance of getting this merged?

  • Allows plain body to be set, rather than just JSON or Form variables

    Allows plain body to be set, rather than just JSON or Form variables

    Hi Dalton,

    I wanted to use https://github.com/Jeffail/gabs with Sling, but found that you don't allow setting the body manually, only from an existing JSON-marshallable structure or form data. Here's a PR that allows you to manually set the body.

    I'm pretty new to Go, so if I've done anything daft feel free to let me know and I'll amend it.

    Cheers,

    Andy

  • Add basic auth support: Sling.SetBasicAuth()

    Add basic auth support: Sling.SetBasicAuth()

    Sling is one of the nicest libraries I found, it's composable and elegant. However I found it frustrating that it lacked Basic Auth support which is a must have when writing API clients, so I thought I would add here.

    Tried to keep it as simple as possible and follow existing conventions (allow chaining etc ...)

    Let me know if you have any questions.

  • Extend xml support

    Extend xml support

    Hi! Sling is a very nice api client builder and I like it a lot. But it would be nice to handle request/response in xml form too. I saw patch from @maxbeutel with ResponseDecoder and appreciate it. I suggest to extend it and add WithXMLResponse() method that will switch Sling to handle xml response and add BodyXML() method to send xml requests.

  • Add Context support.

    Add Context support.

    Contexts are increasingly commonplace in the Go ecosystem, and it makes sense that a library meant to wrap the standard “net/http”.Request supports them without forcing the low-level Request and Do methods on you.

    Also, added Timeout as a convenience.

  • JSON body is not decoded when Content-Type: application/json is not present

    JSON body is not decoded when Content-Type: application/json is not present

    Thus, I think this is logical to decode JSON whether header is set or not, because user must explicitly pass successV/failureV.

    Broken test defines current behaviour, If you agree I will fix it too.

  • Add Content-Length checking before decoding

    Add Content-Length checking before decoding

    We have a pattern with REST APIs at work where they return a 201 with a Location header for the new resource and an empty response body. Sling would attempt to decode the empty response body and return an EOF error.

    • Add a check before attempting to decode the response body to make sure that it's a non-zero Content-Length.
    • Update the tests to check that this specific case works
    • Update the GoDoc and comments
  • Add support for response body decoders, including standard json and json protobuf

    Add support for response body decoders, including standard json and json protobuf

    Added a Decoder interface, default to JSON from encoding/json.

    Included a Protobuf JSON decoder using the gogo/protobuf/jsonpb marshaller as the first alternative.

  • Save response body when parsing JSON

    Save response body when parsing JSON

    As the json decoder consumes the reader and there are cases where we want to preserve the response body to further process outside sling a flag to restore it has been included

  • Add Sling.Safely()

    Add Sling.Safely()

    When safe mode is enabled, all Sling methods copy the underlying sling instance before mutating the Sling object effectively making Sling immutable.

    This is just a proof of concept to see whether it makes sense. It needs more documentation and testing before it's shippable, but I wanted to get feedback before doing that work.

    I think ultimately this mode would be inverted as 'unsafe mode' so that we do the least surprising thing by default and allow clients to opt-in to mutable state, but I've erred on the side of not changing existing behavior.

  • Allow context aware requests

    Allow context aware requests

    Here we introduce RequestWithContext() and ReceiveWithContext() method which allow consumers to pass in a context. This is useful for working with HTTP requests where the consumer might want to control request lifetime via a context.

Related tags
go http api to handle phishing resources requests

go http api to handle phishing resources requests (auth check, prometheus metrics, pushing to rabbit, logging to elasticsearch)

Oct 8, 2021
This is a simple single-host reverse proxy that intercept and save HTTP requests and responses
This is a simple single-host reverse proxy that intercept and save HTTP requests and responses

HTTP Telescope Debug HTTP requests using a reverse proxy. Description This is a simple single-host reverse proxy that intercept and save HTTP requests

Mar 20, 2022
Replacement of ApacheBench(ab), support for transactional requests, support for command line and package references to HTTP stress testing tool.

stress stress is an HTTP stress testing tool. Through this tool, you can do a stress test on the HTTP service and get detailed test results. It is ins

Aug 23, 2022
A golang tool which makes http requests and prints the address of the request along with the MD5 hash of the response.

Golang Tool This repository is a golang tool which makes http requests to the external server and prints the address of the request along with the MD5

Oct 17, 2021
Gourl: Performs (multiple) HTTP requests and gathers stats

Gourl: Performs (multiple) HTTP requests and gathers stats

Mar 9, 2022
Nap is a file-based framework for automating the execution of config-driven HTTP requests and scripts.

Nap Nap is a file-based framework for automating the execution of config-driven HTTP requests and scripts. Installation Options Using go get $ go inst

Nov 17, 2022
Http client call for golang http api calls

httpclient-call-go This library is used to make http calls to different API services Install Package go get

Oct 7, 2022
This project aims for printing HTTP requests from outside simply

HTTP Debug Server This project aims for printing HTTP requests from outside simp

Jan 29, 2022
A Go "clone" of the great and famous Requests library

GRequests A Go "clone" of the great and famous Requests library License GRequests is licensed under the Apache License, Version 2.0. See LICENSE for t

Dec 23, 2022
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
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 Resty Co

Jan 1, 2023
Gotcha is an high level HTTP client with a got-like API
Gotcha is an high level HTTP client with a got-like API

Gotcha is an alternative to Go's http client, with an API inspired by got. It can interface with other HTTP packages through an adapter.

Dec 7, 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
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
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
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
Simple HTTP package that wraps net/http

Simple HTTP package that wraps net/http

Jan 17, 2022