Documentation ¶
Overview ¶
Package 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 the examples to learn how to compose a Sling into your API client.
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, or Head 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()
QueryStruct ¶
Define url parameter structs (https://godoc.org/github.com/google/go-querystring/query). 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()
Json Body ¶
Define JSON tagged structs (https://golang.org/pkg/encoding/json/). 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 (https://godoc.org/github.com/google/go-querystring/query). 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 generates an http.Request (say with some path and query params) each time Request() is called, based on its state. When creating different slings, you may wish to extend an existing Sling to minimize duplication (e.g. a common client).
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().
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.
Index ¶
- Constants
- func DecodeOnSuccess(resp *http.Response) bool
- func DefaultBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration
- func DefaultRetryPolicy(ctx context.Context, resp *http.Response, err error) (bool, error)
- func ErrorPropagatedRetryPolicy(ctx context.Context, resp *http.Response, err error) (bool, error)
- func LinearJitterBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration
- type Backoff
- type BodyProvider
- type CheckRetry
- type ContextExtractor
- type Doer
- type ErrorHandler
- type Fields
- type HttpWrapper
- type JsonpbDecoder
- type Logger
- type Raw
- type ReaderFunc
- type Request
- type Response
- type ResponseDecoder
- type RetryDoer
- type RetryOption
- func WithErrorHandler(errorHandler ErrorHandler) RetryOption
- func WithLogger(logger Logger) RetryOption
- func WithRetryBackoff(backoff Backoff) RetryOption
- func WithRetryPolicy(retryPolicy CheckRetry) RetryOption
- func WithRetryTimes(times int) RetryOption
- func WithRetryWaitMax(wait time.Duration) RetryOption
- func WithRetryWaitMin(wait time.Duration) RetryOption
- type Sling
- func (s *Sling) AddHeader(key, value string) *Sling
- func (s *Sling) AutoRetry(opts ...RetryOption) *Sling
- func (s *Sling) Base(rawURL string) *Sling
- func (s *Sling) Body(body io.Reader) *Sling
- func (s *Sling) BodyForm(bodyForm interface{}) *Sling
- func (s *Sling) BodyJSON(bodyJSON interface{}) *Sling
- func (s *Sling) BodyProvider(body BodyProvider) *Sling
- func (s *Sling) Client(httpWrapper *HttpWrapper) *Sling
- func (s *Sling) Connect(pathURL string) *Sling
- func (s *Sling) Context() context.Context
- func (s *Sling) Delete(pathURL string) *Sling
- func (s *Sling) Do(req *http.Request, successV, failureV interface{}) (*Response, error)
- func (s *Sling) Doer(doer Doer) *Sling
- func (s *Sling) Get(pathURL string) *Sling
- func (s *Sling) Head(pathURL string) *Sling
- func (s *Sling) New() *Sling
- func (s *Sling) Options(pathURL string) *Sling
- func (s *Sling) Patch(pathURL string) *Sling
- func (s *Sling) Path(path string) *Sling
- func (s *Sling) Post(pathURL string) *Sling
- func (s *Sling) Put(pathURL string) *Sling
- func (s *Sling) QueryParams(params map[string]string) *Sling
- func (s *Sling) QueryStruct(queryStruct interface{}) *Sling
- func (s *Sling) Receive(successV, failureV interface{}) (*Response, error)
- func (s *Sling) ReceiveSuccess(successV interface{}) (*Response, error)
- func (s *Sling) Request() (*http.Request, error)
- func (s *Sling) ResponseDecoder(decoder ResponseDecoder) *Sling
- func (s *Sling) SetBasicAuth(username, password string) *Sling
- func (s *Sling) SetBearerAuth(token string) *Sling
- func (s *Sling) SetContext(ctx context.Context) *Sling
- func (s *Sling) SetHeader(key, value string) *Sling
- func (s *Sling) Trace(pathURL string) *Sling
- func (s *Sling) WithSuccessDecider(isSuccess SuccessDecider) *Sling
- type SuccessDecider
Constants ¶
const ( MethodGet = "GET" MethodPost = "POST" MethodPut = "PUT" MethodDelete = "DELETE" MethodPatch = "PATCH" MethodHead = "HEAD" MethodOptions = "OPTIONS" MethodTrace = "TRACE" MethodConnect = "CONNECT" )
Variables ¶
This section is empty.
Functions ¶
func DecodeOnSuccess ¶
DecodeOnSuccess decide that we should decode on success response (http code 2xx)
func DefaultBackoff ¶
DefaultBackoff provides a default callback for Client.Backoff which will perform exponential backoff based on the attempt number and limited by the provided minimum and maximum durations.
It also tries to parse Retry-After response header when a http.StatusTooManyRequests (HTTP Code 429) is found in the resp parameter. Hence it will return the number of seconds the server states it may be ready to process more requests from this client.
func DefaultRetryPolicy ¶
DefaultRetryPolicy provides a default callback for Client.CheckRetry, which will retry on connection errors and server errors.
func ErrorPropagatedRetryPolicy ¶
ErrorPropagatedRetryPolicy is the same as DefaultRetryPolicy, except it propagates errors back instead of returning nil. This allows you to inspect why it decided to retry or not.
func LinearJitterBackoff ¶
LinearJitterBackoff provides a callback for Client.Backoff which will perform linear backoff based on the attempt number and with jitter to prevent a thundering herd.
min and max here are *not* absolute values. The number to be multiplied by the attempt number will be chosen at random from between them, thus they are bounding the jitter.
For instance: * To get strictly linear backoff of one second increasing each retry, set both to one second (1s, 2s, 3s, 4s, ...) * To get a small amount of jitter centered around one second increasing each retry, set to around one second, such as a min of 800ms and max of 1200ms (892ms, 2102ms, 2945ms, 4312ms, ...) * To get extreme jitter, set to a very wide spread, such as a min of 100ms and a max of 20s (15382ms, 292ms, 51321ms, 35234ms, ...)
Types ¶
type Backoff ¶
Backoff specifies a policy for how long to wait between retries. It is called after a failing request to determine the amount of time that should pass before trying again.
type BodyProvider ¶
type BodyProvider interface { // ContentType returns the Content-Type of the body. ContentType() string // Body returns the io.Reader body. Body() (io.Reader, error) }
BodyProvider provides Body content for http.Request attachment.
type CheckRetry ¶
CheckRetry specifies a policy for handling retries. It is called following each request with the response and error values returned by the http.Client. If CheckRetry returns false, the Client stops retrying and returns the response to the caller. If CheckRetry returns an error, that error value is returned in lieu of the error from the request. The Client will close any response body when retrying, but if the retry is aborted it is up to the CheckRetry callback to properly close any response body before returning.
type ContextExtractor ¶
type Doer ¶
Doer executes http requests. It is implemented by *http.Client. You can wrap *http.Client with layers of Doers to form a stack of client-side middleware.
type ErrorHandler ¶
ErrorHandler is called if retries are expired, containing the last status from the http library. If not specified, default behavior for the library is to close the body and return an error indicating how many tries were attempted. If overriding this, be sure to close the body if needed.
type HttpWrapper ¶
type HttpWrapper struct {
// contains filtered or unexported fields
}
func NewHttpWrapper ¶
func NewHttpWrapper(client *http.Client) *HttpWrapper
type JsonpbDecoder ¶
type JsonpbDecoder struct { }
jsonDecoder decodes http response JSON into a JSON-tagged struct value.
func (JsonpbDecoder) Decode ¶
func (d JsonpbDecoder) Decode(bytes []byte, v interface{}) error
Decode decodes the Response Body into the value pointed to by v. Caller must provide a non-nil v and close the resp.Body.
type Logger ¶
type Logger interface { WithContext(ctx context.Context) Logger WithFields(keyValues Fields) Logger Info(msg string) Infof(format string, args ...interface{}) Error(msg string) Errorf(format string, args ...interface{}) }
func NewDefaultLogger ¶
func NewDefaultLogger() Logger
type ReaderFunc ¶
ReaderFunc is the type of function that can be given natively to NewRequest
type Request ¶
type Request struct { // Embed an HTTP request directly. This makes a *Request act exactly // like an *http.Request so that all meta methods are supported. *http.Request // contains filtered or unexported fields }
Request wraps the metadata needed to create HTTP requests.
type ResponseDecoder ¶
type ResponseDecoder interface { // Decode decodes the response into the value pointed to by v. Decode(bytes []byte, v interface{}) error }
ResponseDecoder decodes http responses into struct values.
type RetryDoer ¶
type RetryDoer struct { HTTPClient Doer // Internal HTTP client. Logger Logger // Customer logger instance. Can be either Logger or LeveledLogger RetryWaitMin time.Duration // Minimum time to wait RetryWaitMax time.Duration // Maximum time to wait RetryMax int // Maximum number of retries // CheckRetry specifies the policy for handling retries, and is called // after each request. The default policy is DefaultRetryPolicy. CheckRetry CheckRetry // Backoff specifies the policy for how long to wait between retries Backoff Backoff // ErrorHandler specifies the custom error handler to use, if any ErrorHandler ErrorHandler }
func NewRetryDoer ¶
func NewRetryDoer(doer Doer, opts ...RetryOption) *RetryDoer
NewRetryDoer creates a new Client with default settings.
type RetryOption ¶
type RetryOption func(doer *RetryDoer)
func WithErrorHandler ¶
func WithErrorHandler(errorHandler ErrorHandler) RetryOption
func WithLogger ¶
func WithLogger(logger Logger) RetryOption
func WithRetryBackoff ¶
func WithRetryBackoff(backoff Backoff) RetryOption
func WithRetryPolicy ¶
func WithRetryPolicy(retryPolicy CheckRetry) RetryOption
func WithRetryTimes ¶
func WithRetryTimes(times int) RetryOption
func WithRetryWaitMax ¶
func WithRetryWaitMax(wait time.Duration) RetryOption
func WithRetryWaitMin ¶
func WithRetryWaitMin(wait time.Duration) RetryOption
type Sling ¶
type Sling struct {
// contains filtered or unexported fields
}
Sling is an HTTP Request builder and sender.
func (*Sling) AddHeader ¶
Add adds the key, value pair in Headers, appending values for existing keys to the key's values. Header keys are canonicalized.
func (*Sling) AutoRetry ¶
func (s *Sling) AutoRetry(opts ...RetryOption) *Sling
func (*Sling) Base ¶
Base sets the rawURL. If you intend to extend the url with Path, baseUrl should be specified with a trailing slash.
func (*Sling) Body ¶
Body sets the Sling's body. The body value will be set as the Body on new requests (see Request()). If the provided body is also an io.Closer, the request Body will be closed by http.Client methods.
func (*Sling) BodyForm ¶
BodyForm sets the Sling's bodyForm. The value pointed to by the bodyForm will be url encoded as the Body on new requests (see Request()). The bodyForm argument should be a pointer to a url tagged struct. See https://godoc.org/github.com/google/go-querystring/query for details.
func (*Sling) BodyJSON ¶
BodyJSON sets the Sling's bodyJSON. The value pointed to by the bodyJSON will be JSON encoded as the Body on new requests (see Request()). The bodyJSON argument should be a pointer to a JSON tagged struct. See https://golang.org/pkg/encoding/json/#MarshalIndent for details.
func (*Sling) BodyProvider ¶
func (s *Sling) BodyProvider(body BodyProvider) *Sling
BodyProvider sets the Sling's body provider.
func (*Sling) Client ¶
func (s *Sling) Client(httpWrapper *HttpWrapper) *Sling
Client sets the http Client used to do requests. If a nil client is given, the http.DefaultClient will be used.
func (*Sling) Context ¶
Context method returns the Context if its already set in request otherwise it creates new one using `context.Background()`.
func (*Sling) Do ¶
Do sends an HTTP request and returns the response. Success responses (2XX) are JSON decoded into the value pointed to by successV and other responses are JSON decoded into the value pointed to by failureV. If the status code of response is 204(no content) or the Content-Length is 0, decoding is skipped. Any error sending the request or decoding the response is returned.
func (*Sling) Doer ¶
Doer sets the custom Doer implementation used to do requests. If a nil client is given, the http.DefaultClient will be used.
func (*Sling) New ¶
New returns a copy of a Sling for creating a new Sling with properties from a parent Sling. For example,
parentSling := sling.New().Client(client).Base("https://api.io/") fooSling := parentSling.New().Get("foo/") barSling := parentSling.New().Get("bar/")
fooSling and barSling will both use the same client, but send requests to https://api.io/foo/ and https://api.io/bar/ respectively.
Note that query and body values are copied so if pointer values are used, mutating the original value will mutate the value within the child Sling.
func (*Sling) Path ¶
Path extends the rawURL with the given path by resolving the reference to an absolute URL. If parsing errors occur, the rawURL is left unmodified.
func (*Sling) QueryStruct ¶
QueryStruct appends the queryStruct to the Sling's queryStructs. The value pointed to by each queryStruct will be encoded as url query parameters on new requests (see Request()). The queryStruct argument should be a pointer to a url tagged struct. See https://godoc.org/github.com/google/go-querystring/query for details.
func (*Sling) Receive ¶
Receive creates a new HTTP request and returns the response. Success responses (2XX) are JSON decoded into the value pointed to by successV and other responses are JSON decoded into the value pointed to by failureV. If the status code of response is 204(no content) or the Content-Lenght is 0, decoding is skipped. Any error creating the request, sending it, or decoding the response is returned. Receive is shorthand for calling Request and Do.
func (*Sling) ReceiveSuccess ¶
ReceiveSuccess creates a new HTTP request and returns the response. Success responses (2XX) are JSON decoded into the value pointed to by successV. Any error creating the request, sending it, or decoding a 2XX response is returned.
func (*Sling) Request ¶
Request returns a new http.Request created with the Sling properties. Returns any errors parsing the rawURL, encoding query structs, encoding the body, or creating the http.Request.
func (*Sling) ResponseDecoder ¶
func (s *Sling) ResponseDecoder(decoder ResponseDecoder) *Sling
ResponseDecoder sets the Sling's response decoder.
func (*Sling) SetBasicAuth ¶
SetBasicAuth sets the Authorization header to use HTTP Basic Authentication with the provided username and password. With HTTP Basic Authentication the provided username and password are not encrypted.
func (*Sling) SetBearerAuth ¶
SetBearerAuth sets the Authorization header to use HTTP Bearer Authentication with the provided token.
func (*Sling) SetContext ¶
SetContext method sets the context.Context for current Request. It allows to interrupt the request execution if ctx.Done() channel is closed. See https://blog.golang.org/context article and the "context" package documentation.
func (*Sling) SetHeader ¶
Set sets the key, value pair in Headers, replacing existing values associated with key. Header keys are canonicalized.
func (*Sling) WithSuccessDecider ¶
func (s *Sling) WithSuccessDecider(isSuccess SuccessDecider) *Sling
type SuccessDecider ¶
SuccessDecider decide should we decode the response or not