Modified from: https://github.com/dghubble/sling
Each time, you want to conduct a HTTP request, you must implement like
func postHTTPSample(ctx context.Context, payload request) (*response, error) {
body, err := json.Marshal(payload)
if err != nil {
return nil, err
}
requestBody := bytes.NewBuffer(body)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, "http://localhost:8080/hello", requestBody)
if err != nil {
return nil, err
}
resp, err := http.defaultClient.Do(req)
if err != nil {
return nil, err
}
defer func() {
err := resp.Body.Close()
if err != nil {
fmt.Printf("%v\n", err)
}
}()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed")
}
var respStruct response
err = json.NewDecoder(resp.Body).Decode(&respStruct)
if err != nil {
return nil, err
}
return &respStruct, nil
}
There are some downsides that we can quickly point out from this code.
- We have to write too much boiler plate verbose code from time to time.
- That make refactoring changes like adding an tracing event will make a enormous effect to whole codebase.
- Too much verbose things may leads to mini crackles on details from copy - pasting
- Harder to maintain such a big code
- Many library need to combine to achieve simple things: "bytes", "context", "fmt", "net/http", "encoding/json". New developer can't get a fast acquainting curve.
With sling, we can quickly implement the same feature with significantly less verbosity than ever before
func postSlingSample(ctx context.Context, payload request) (*response, error) {
var respStruct response
_, err := sling.New().Base("http://localhost:8080").
Get("/hello").
SetContext(ctx).
BodyJSON(payload).
WithSuccessDecider(func(h *http.Response) bool {
return h.StatusCode == http.StatusOK
}).
ReceiveSuccess(&respStruct)
return &respStruct, err
}
- What will you receive back?
- Write less code, do more work, be more productive
- An optimized HTTP dealing experience with best practice included
- OpenTelemetry tracing & metrics default integrated
- Extra valuable extension: auto retry, authorization...
Function | Feature |
---|---|
New | Create new sling client |
Doer | Set a new Doer (replacing http lib client default client with Doer, an interface provide Do function) |
Function | Feature |
---|---|
Context | Get the current request context |
SetContext | Do the request with current context |
AddHeader | Add value to current header key |
SetHeader | Replace value for current header key |
SetHeaders | Replace current headers |
SetBasicAuth | Set up the Basic authorization header |
SetAuthToken | Set up standard Teko Bearer token |
Function | Feature |
---|---|
Base | Set up base host (use for all request use the same client instance) |
Path | Extend the URL by the given path |
QueryStruct | Extend the URL by the provided query parameter |
Function | Feature |
---|---|
Body | Provide request raw body |
BodyProvider | Provide request raw body with custom content type |
BodyJSON | Provide request body as content type "application/json" |
BodyForm | Provide request body as content type "application/x-www-form-urlencoded" |
Function | Feature |
---|---|
ResponseDecoder | Setup response decoder (JSON, XML, raw, etc...) |
WithSuccessDecider | Change the condition that differentiate if the request is success or not |
Function | Feature |
---|---|
Request | Build request based on provided data |
ReceiveSuccess | Receive and parse the response body using the provided response decoder only if the request is success |
Receive | Receive and parse the response body using the provided response decoder if the request is success or failed |
Do | Do with custom HTTP request, receive and parse the response body using the provided response decoder if the request is success or failed |
- min retry wait: 1 sec
- max retry wait: 30 sec
- max retry times: 4
- retry policy:
- not retry if one of these errors occur:
- too much redirects
- invalid scheme
- TLS certs invalid.
- Otherwise, retry if
- status code 429 (server is busy)
- status code invalid
- status code over 500 (may related to server outage)
- not retry if one of these errors occur:
- backoff algorithm: Exponential Backoff with min and max wait time range
Function | Feature |
---|---|
WithRetryTimes | Set up the maximum retry times |
WithRetryMaxWait | Set up the maximum wait time before retry the request again |
WithRetryMinWait | Set up the minimum wait time before retry the request again |
WithRetryPolicy | Provide alternative retry policy |
WithBackoff | Provide alternative backoff calculate algorithm, Jitter backoff is available for swapping |
-
The success response have reliable struct, but the failed ones are not following any rules. We should handle it case by case. How do sling supports it?
sling supports a raw body response struct for dealing with this problem. Example:
var rawBody sling.Raw _, err := sling.New().AutoRetry().Base("http://localhost:8080").Get("/hello").ReceiveSuccess(&rawBody)
rawBody
is a go byte slice wrapped all the response body. We can parse, log, do whatever we want with it now. -
The JSON parser work not as expected. Some fields cannot be parsed.
We use the standard go
encoding/json
library for encoding and decoding JSON payload. Please recheck if the payload is strictly conform the JSON - go struct type conversion. For loosely type conversion, you can do some trick with other external lib likemitchellh/mapstructure
, implement it as a custom decoder and register it. We should be good to go now.