requests

package module
v0.0.0-...-01100c3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 22, 2018 License: MIT Imports: 17 Imported by: 0

README

Requests Build Status GoDoc Go Report Card

A.K.A "Yet Another Golang Requests Package"

EXPERIMENTAL: Until the version reaches 1.0, the API may change.

Requests makes it a bit simpler to use Go's http package as a client. As an example, take a simple request, with the http package:

req, err := http.NewRequest("GET", "http://www.google.com", nil)
if err != nil { return err }

resp, err := http.DefaultClient.Do(req)
if err != nil { return err }

defer resp.Body.Close()
if resp.StatusCode == 200 {
    respBody, _ := ioutil.ReadAll(resp.Body)
    fmt.Println(string(respBody))
}

With requests:

resp, body, err := requests.Receive(nil, requests.Get("http://www.google.com"))
if err != nil { return err }

fmt.Printf("%d %s", resp.StatusCode, body)

For a more complex use case, take an API call, which sends and receives JSON.
With http:

bodyBytes, err := json.Marshal(reqBodyStruct)
if err != nil { return err }

req, err := http.NewRequest("POST", "http://api.com/resources/", bytes.NewReader(bodyBytes))
if err != nil { return err }
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json"

resp, err := http.DefaultClient.Do(req)
if err != nil { return err }

if resp.StatusCode == 201 {
    respBody, _ := ioutil.ReadAll(resp.Body)
    var respStruct Resource
    err := json.Unmarshal(respBody, &respStruct)
    if err != nil { return err }
    
    fmt.Println(respBody)
}

With requests:

var respStruct Resource

resp, body, err := requests.Receive(&respStruct, 
    requests.JSON(),
    requests.Get("http://www.google.com")
)
if err != nil { return err }
    
fmt.Printf("%d %s %v", resp.StatusCode, body, respStruct)

Requests revolves around the use of Options, which are arguments to the functions which create and/or send requests. Options can be be used to set headers, query parameters, compose the URL, set the method, install middleware, configure or replace the HTTP client used to send the request, etc.

The package-level Request(...Option) function creates an (unsent) *http.Request:

req, err := requests.Request(Get("http://www.google.com"))

The Do(...Option) function both creates the request and sends it, using the http.DefaultClient:

resp, err := requests.Do(Get("http://www.google.com"))

A raw *http.Response is returned. It is the caller's responsibility to close the response body, just as with http.Client's Do() method.

The package also has Receive(interface{}, ...Option) and ReceiveFull(interface{},interface{},...Option) functions which handle reading the response and optionally unmarshaling the body into a struct:

var user User
resp, body, err := requests.Receive(&user, requests.Get("http://api.com/users/bob"))

The Receive*() functions read and close the response body for you, return the entire response body as a string, and optionally unmarshal the response body into a struct. If you only want the body back as a string, pass nil as the first argument. The string body is returned either way. Requests can handle JSON and XML response bodies, as determined by the response's Content-Type header. Other types of bodies can be handled by using a custom Unmarshaler.

If you have an API which returns structured non-2XX responses (like an error response JSON body), you can use the ReceiveFull() function to pass an alternate struct value to unmarhal the error response body into:

 var user User
 var apiError APIError
 resp, body, err := requests.ReceiveFull(&user, &apiError, requests.Get("http://api.com/users/bob"))

All these functions have *Context() variants, which add a context.Context to the request. This is particularly useful for setting a request timeout:

ctx = context.WithTimeout(ctx, 10 * time.Second)
requests.RequestContext(ctx, Get("http://www.google.com"))
requests.DoContext(ctx, Get("http://www.google.com"))
requests.ReceiveContext(ctx, &into, Get("http://www.google.com"))
requests.ReceiveFullContext(ctx, &into, &apierr, Get("http://www.google.com"))

Requests Instance

The package level functions just delegate to the DefaultRequests variable, which holds a Requests instance. An instance of Requests is useful for building a re-usable, composable HTTP client.

A new Requests can be constructed with New(...Options):

reqs, err := requests.New(
    requests.Get("http://api.server/resources/1"), 
    requests.JSON(), 
    requests.Accept(requests.ContentTypeJSON)
)

...or can be created with a literal:

u, err := url.Parse("http://api.server/resources/1")
if err != nil { return err }

reqs := &Requests{
    URL: u,
    Method: "GET",
    Header: http.Header{
        requests.HeaderContentType: []string{requests.ContentTypeJSON),
        requests.HeaderAccept: []string{requests.ContentTypeJSON),
    },
}

Additional options can be applied with Apply():

err := reqs.Apply(requests.Method("POST"), requests.Body(bodyStruct))
if err != nil { return err }

...or can just be set directly:

reqs.Method = "POST"
reqs.Body = bodyStruct

Requests can be cloned, creating a copy which can be further configured:

base, _ := requests.New(
    requests.URL("https://api.com"), 
    requests.JSON(),
    requests.Accept(requests.ContentTypeJSON),
    requests.BearerAuth(token),
)
    
getResource = base.Clone()
getResource.Apply(requests.RelativeURL("resources/1"))

With(...Option) combines Clone() and Apply(...Option):

getResource, _ := base.With(requests.RelativeURL("resources/1"))

Options can also be passed to the Request/Do/Receive* methods. These Options will only be applied to the particular request, not the Requests instance:

resp, body, err := base.Receive(nil, requests.Get("resources", "1")  // path elements
                                                                     // will be joined.

Request Options

The Requests struct has attributes mirror counterparts on *http.Request:

Method string
URL    *url.URL
Header http.Header
GetBody          func() (io.ReadCloser, error)
ContentLength    int64
TransferEncoding []string
Close            bool
Host             string
Trailer          http.Header

If not set, the constructed *http.Requests will have the normal default values these attributes have after calling http.NewRequest() (some attributes will be initialized, some remained zeroed).

If set, then the Requests' values will overwrite the values of these attributes in the *http.Request.

Functional Options are defined which set most of these attributes. You can configure Requests either by applying Options, or by simply setting the attributes directly.

Client Options

The HTTP client used to execute requests can also be customized through options:

requests.Do(requests.Get("https://api.com"), requests.Client(clients.SkipVerify()))

github.com/gemalto/requests/clients is a standalone package for constructing and configuring http.Clients. The requests.Client(...clients.Option) option constructs a new HTTP client and installs it into Requests.Doer.

Query Params

The QueryParams attribute will be merged into any query parameters encoded into the URL. For example:

reqs, _ := requests.New(requests.URL("http://test.com?color=red"))
reqs.QueryParams = url.Values("flavor":[]string{"vanilla"})
r, _ := reqs.Request()
r.URL.String()             // http://test.com?color=red&flavor=vanilla

The QueryParams() option can take a map[string]interface{} or url.Values, or accepts a struct value, which is marshaled into a url.Values using github.com/google/go-querystring:

type Params struct {
    Color string `url:"color"`
}

reqs, _ := requests.New(
    requests.URL("http://test.com"),
    requests.QueryParams(Params{Color:"blue"}),
    requests.QueryParams(map[string][]string{"flavor":[]string{"vanilla"}}),
)
r, _ := reqs.Request()
r.URL.String()             // http://test.com?color=blue,flavor=vanilla

Body

If Requests.Body is set to a string, []byte, or io.Reader, the value will be used directly as the request body:

req, _ := requests.Request(
    requests.Post("http://api.com"),
    requests.ContentType(requests.ContentTypeJSON),
    requests.Body(`{"color":"red"}`),
)
httputil.DumpRequest(req, true)

// POST / HTTP/1.1
// Host: api.com
// Content-Type: application/json
// 
// {"color":"red"}

If Body is any other value, it will be marshaled into the body, using the Marshaler:

type Resource struct {
    Color string `json:"color"`
}

req, _ := requests.Request(
    requests.Post("http://api.com"),
    requests.Body(Resource{Color:"red"}),
)
httputil.DumpRequest(req, true)

// POST / HTTP/1.1
// Host: api.com
// Content-Type: application/json
// 
// {"color":"red"}

Note the default marshaler is JSON, and sets the Content-Type header.

Receive

Receive() handles the response as well:

type Resource struct {
    Color string `json:"color"`
}

var res Resource

resp, body, err := requests.Receive(&res, requests.Get("http://api.com/resources/1")
if err != nil { return err }

fmt.Println(body)     // {"color":"red"}

The body of the response is returned as a string. If the first argument is not nil, the body will also be unmarshaled into that value.

By default, the unmarshaler will use the response's Content-Type header to determine how to unmarshal the response body into a struct. This can be customized by setting Requests.Unmarshaler:

reqs.Unmarshaler = &requests.XML(true)                  // via assignment
reqs.Apply(requests.Unmarshaler(&requests.XML(true)))   // or via an Option

Doer and Middleware

Requests uses an implementation of Doer to execute requests. By default, http.DefaultClient is used, but this can be replaced by a customize client, or a mock Doer:

reqs.Doer = requests.DoerFunc(func(req *http.Request) (*http.Response, error) {
    return &http.Response{}
})

You can also install middleware into Requests, which can intercept the request and response:

mw := func(next requests.Doer) requests.Doer {
    return requests.DoerFunc(func(req *http.Request) (*http.Response, error) {
        fmt.Println(httputil.DumpRequest(req, true))
        resp, err := next(req)
        if err == nil { 
            fmt.Println(httputil.DumpResponse(resp, true))
        }
        return resp, err
    })
}
reqs.Middleware = append(reqs.Middleware, mw)   // via assignment
reqs.Apply(requests.Use(mw))                    // or via option

FAQ

  • Why, when there are like, 50 other packages that do the exact same thing?

Yeah, good question. This library started as a few tweaks to https://github.com/dghubble/sling. Then it became more of a fork, then a complete rewrite, inspiration from a bunch of other similar libraries.

A few things bugged me about other libraries:

  1. Some didn't offer enough control over the base http primitives, like the underlying http.Client, and all the esoteric attributes of http.Request.

  2. I wanted more control over marshaling and unmarshaling bodies, without sacrificing access to the raw body.

  3. Some libraries which offer lots more control or options also seemed to be much more complicated, or less idiomatic.

  4. Most libraries don't handle context.Contexts at all.

  5. The main thing: most other libraries use a "fluent" API, where you call methods on a builder instance to configure the request, and these methods each return the builder, making it simple to call the next method, something like this:

     req.Get("http://api.com").Header("Content-Type", "application/json").Body(reqBody)
    

    I used to like fluent APIs in other languages, but they don't feel right in Go. You typically end up deferring errors until later, so the error doesn't surface near the code that caused the error. Its difficult to mix fluent APIs with interfaces, because the concrete types tend to have lots of methods, and they all have to return the same concrete type. For the same reason, it's awkward to embed types with fluent APIs. Fluent APIs also make it hard to extend the library with additional, external options.

Requests swaps a fluent API for the functional option pattern. This hopefully keeps a fluent-like coding style, while being more idiomatic Go. Since Options are just a simple interface, it's easy to bring your own options, or contribute new options back to this library.

Also, making the options into objects improved ergonomics in a few places, like mirroring the main functions (Request(), Do(), Receive()) on the struct and the package. Options can be passed around as arguments or accumulated in slices.

Documentation

Overview

Package requests is Go library for HTTP clients. It's a thin wrapper around the `http` package, which makes it a little easier to create and send requests.

Examples:

```go resp, body, err := requests.Receive(nil, requests.Get("http://www.google.com")) if err != nil { return err }

fmt.Printf("%d %s", resp.StatusCode, body) ```

```go var respStruct Resource

resp, body, err := requests.Receive(&respStruct,

requests.JSON(),
requests.Get("http://www.google.com")

) if err != nil { return err }

fmt.Printf("%d %s %v", resp.StatusCode, body, respStruct) ```

Requests revolves around the use of `Option`s, which are arguments to the functions which create and/or send requests. Options can be be used to set headers, query parameters, compose the URL, set the method, install middleware, configure or replace the HTTP client used to send the request, etc.

The package-level `Request(...Option)` function creates an (unsent) `*http.Request`:

```go req, err := requests.Request(Get("http://www.google.com")) ```

The `Send(...Option)` function both creates the request and sends it, using the `http.DefaultClient`:

```go resp, err := requests.Send(Get("http://www.google.com")) ```

A raw `*http.Response` is returned. It is the caller's responsibility to close the response body, just as with `http.Client`'s `Do()` method.

The package also has `Receive(interface{}, ...Option)` and `ReceiveFull(interface{},interface{},...Option)` functions which handle reading the response and optionally unmarshaling the body into a struct:

```go var user User resp, body, err := requests.Receive(&user, requests.Get("http://api.com/users/bob")) ```

The `Receive*()` functions read and close the response body for you, return the entire response body as a string, and optionally unmarshal the response body into a struct. If you only want the body back as a string, pass nil as the first argument. The string body is returned either way. Requests can handle JSON and XML response bodies, as determined by the response's `Content-Type` header. Other types of bodies can be handled by using a custom `Unmarshaler`.

If you have an API which returns structured non-2XX responses (like an error response JSON body), you can use the `ReceiveFull()` function to pass an alternate struct value to unmarhal the error response body into:

```go

var user User
var apiError APIError
resp, body, err := requests.ReceiveFull(&user, &apiError, requests.Get("http://api.com/users/bob"))

```

All these functions have `*Context()` variants, which add a `context.Context` to the request. This is particularly useful for setting a request timeout:

```go ctx = context.WithTimeout(ctx, 10 * time.Second) requests.RequestContext(ctx, Get("http://www.google.com")) requests.SendContext(ctx, Get("http://www.google.com")) requests.ReceiveContext(ctx, &into, Get("http://www.google.com")) requests.ReceiveFullContext(ctx, &into, &apierr, Get("http://www.google.com")) ```

Requests Instance

The package level functions just delegate to the `DefaultRequests` variable, which holds a `Requests` instance. An instance of `Requests` is useful for building a re-usable, composable HTTP client.

A new `Requests` can be constructed with `New(...Options)`:

```go reqs, err := requests.New(

requests.Get("http://api.server/resources/1"),
requests.JSON(),
requests.Accept(requests.ContentTypeJSON)

) ```

...or can be created with a literal:

```go u, err := url.Parse("http://api.server/resources/1") if err != nil { return err }

reqs := &Requests{
    URL: u,
    Method: "GET",
    Header: http.Header{
        requests.HeaderContentType: []string{requests.ContentTypeJSON),
        requests.HeaderAccept: []string{requests.ContentTypeJSON),
    },
}

```

Additional options can be applied with Apply():

```go err := reqs.Apply(requests.Method("POST"), requests.Body(bodyStruct)) if err != nil { return err } ```

...or can just be set directly:

```go reqs.Method = "POST" reqs.Body = bodyStruct ```

`Requests` can be cloned, creating a copy which can be further configured:

```go base, _ := requests.New(

requests.URL("https://api.com"),
requests.JSON(),
requests.Accept(requests.ContentTypeJSON),
requests.BearerAuth(token),

)

getResource = base.Clone() getResource.Apply(requests.RelativeURL("resources/1")) ```

`With(...Option)` combines `Clone()` and `Apply(...Option)`:

```go getResource, _ := base.With(requests.RelativeURL("resources/1")) ```

Options can also be passed to the Request/Send/Receive methods. These Options will only be applied to the particular request, not the `Requests` instance:

```go resp, body, err := base.Receive(nil, requests.Get("resources", "1") // path elements

// will be joined.

```

Request Options

The `Requests` struct has attributes mirror counterparts on `*http.Request`:

```go Method string URL *url.URL Header http.Header GetBody func() (io.ReadCloser, error) ContentLength int64 TransferEncoding []string Close bool Host string Trailer http.Header ```

If not set, the constructed `*http.Request`s will have the normal default values these attributes have after calling `http.NewRequest()` (some attributes will be initialized, some remained zeroed).

If set, then the `Requests`' values will overwrite the values of these attributes in the `*http.Request`.

Functional `Options` are defined which set most of these attributes. You can configure `Requests` either by applying `Option`s, or by simply setting the attributes directly.

Client Options

The HTTP client used to execute requests can also be customized through options:

```go requests.Send(requests.Get("https://api.com"), requests.Client(clients.SkipVerify())) ```

`github.com/gemalto/requests/clients` is a standalone package for constructing and configuring `http.Client`s. The `requests.Client(...clients.Option)` option constructs a new HTTP client and installs it into `Requests.Doer`.

Query Params

The `QueryParams` attribute will be merged into any query parameters encoded into the URL. For example:

```go reqs, _ := requests.New(requests.URL("http://test.com?color=red")) reqs.QueryParams = url.Values("flavor":[]string{"vanilla"}) r, _ := reqs.Request() r.URL.String() // http://test.com?color=red&flavor=vanilla ```

The `QueryParams()` option can take a `map[string]interface{}` or `url.Values`, or accepts a struct value, which is marshaled into a `url.Values` using `github.com/google/go-querystring`:

```go

type Params struct {
    Color string `url:"color"`
}

reqs, _ := requests.New(

requests.URL("http://test.com"),
requests.QueryParams(Params{Color:"blue"}),
requests.QueryParams(map[string][]string{"flavor":[]string{"vanilla"}}),

) r, _ := reqs.Request() r.URL.String() // http://test.com?color=blue,flavor=vanilla ```

Body

If `Requests.Body` is set to a `string`, `[]byte`, or `io.Reader`, the value will be used directly as the request body:

```go req, _ := requests.Request(

requests.Post("http://api.com"),
requests.ContentType(requests.ContentTypeJSON),
requests.Body(`{"color":"red"}`),

) httputil.DumpRequest(req, true)

// POST / HTTP/1.1 // Host: api.com // Content-Type: application/json // // {"color":"red"} ```

If `Body` is any other value, it will be marshaled into the body, using the `Marshaler`:

```go

type Resource struct {
    Color string `json:"color"`
}

req, _ := requests.Request(

requests.Post("http://api.com"),
requests.Body(Resource{Color:"red"}),

) httputil.DumpRequest(req, true)

// POST / HTTP/1.1 // Host: api.com // Content-Type: application/json // // {"color":"red"} ```

Note the default marshaler is JSON, and sets the `Content-Type` header.

Receive

`Receive()` handles the response as well:

```go

type Resource struct {
    Color string `json:"color"`
}

var res Resource

resp, body, err := requests.Receive(&res, requests.Get("http://api.com/resources/1") if err != nil { return err }

fmt.Println(body) // {"color":"red"} ```

The body of the response is returned as a string. If the first argument is not nil, the body will also be unmarshaled into that value.

By default, the unmarshaler will use the response's `Content-Type` header to determine how to unmarshal the response body into a struct. This can be customized by setting `Requests.Unmarshaler`:

```go reqs.Unmarshaler = &requests.XML(true) // via assignment reqs.Apply(requests.Unmarshaler(&requests.XML(true))) // or via an Option ```

Doer and Middleware

`Requests` uses an implementation of `Doer` to execute requests. By default, `http.DefaultClient` is used, but this can be replaced by a customize client, or a mock `Doer`:

```go

reqs.Doer = requests.DoerFunc(func(req *http.Request) (*http.Response, error) {
    return &http.Response{}
})

```

You can also install middleware into `Requests`, which can intercept the request and response:

```go

mw := func(next requests.Doer) requests.Doer {
    return requests.DoerFunc(func(req *http.Request) (*http.Response, error) {
        fmt.Println(httputil.DumpRequest(req, true))
        resp, err := next(req)
        if err == nil {
            fmt.Println(httputil.DumpResponse(resp, true))
        }
        return resp, err
    })
}

reqs.Middleware = append(reqs.Middleware, mw) // via assignment reqs.Apply(requests.Use(mw)) // or via option ```

Index

Constants

View Source
const (
	HeaderAccept        = "Accept"
	HeaderContentType   = "Content-Type"
	HeaderAuthorization = "Authorization"

	ContentTypeJSON = "application/json"
	ContentTypeXML  = "application/xml"
	ContentTypeForm = "application/x-www-form-urlencoded"
)

HTTP constants.

Variables

View Source
var DefaultRequests = Requests{}

DefaultRequests is the singleton used by the package-level Request/Send/Receive functions.

Functions

func Receive

func Receive(successV interface{}, opts ...Option) (*http.Response, string, error)

Receive does the same as Requests.Receive(), using the DefaultRequests.

func ReceiveContext

func ReceiveContext(ctx context.Context, successV interface{}, opts ...Option) (*http.Response, string, error)

ReceiveContext does the same as Requests.ReceiveContext(), using the DefaultRequests.

func ReceiveFull

func ReceiveFull(successV, failureV interface{}, opts ...Option) (*http.Response, string, error)

ReceiveFull does the same as Requests.ReceiveFull(), using the DefaultRequests.

func ReceiveFullContext

func ReceiveFullContext(ctx context.Context, successV, failureV interface{}, opts ...Option) (*http.Response, string, error)

ReceiveFullContext does the same as Requests.ReceiveFullContext(), using the DefaultRequests.

func Request

func Request(opts ...Option) (*http.Request, error)

Request does the same as Requests.Request(), using the DefaultRequests.

func RequestContext

func RequestContext(ctx context.Context, opts ...Option) (*http.Request, error)

RequestContext does the same as Requests.RequestContext(), using the DefaultRequests.

func Send

func Send(opts ...Option) (*http.Response, error)

Send does the same as Requests.Send(), using the DefaultRequests.

func SendContext

func SendContext(ctx context.Context, opts ...Option) (*http.Response, error)

SendContext does the same as Requests.SendContext(), using the DefaultRequests.

Types

type BodyMarshaler

type BodyMarshaler interface {
	Marshal(v interface{}) (data []byte, contentType string, err error)
}

BodyMarshaler marshals structs into a []byte, and supplies a matching Content-Type header.

var DefaultMarshaler BodyMarshaler = &JSONMarshaler{}

DefaultMarshaler is used by Requests if Requests.Marshaler is nil.

type BodyUnmarshaler

type BodyUnmarshaler interface {
	Unmarshal(data []byte, contentType string, v interface{}) error
}

BodyUnmarshaler unmarshals a []byte response body into a value. It is provided the value of the Content-Type header from the response.

var DefaultUnmarshaler BodyUnmarshaler = &MultiUnmarshaler{}

DefaultUnmarshaler is used by Requests if Requests.Unmarshaler is nil.

type Doer

type Doer interface {
	Do(req *http.Request) (*http.Response, error)
}

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.

func Wrap

func Wrap(d Doer, m ...Middleware) Doer

Wrap applies a set of middleware to a Doer. The returned Doer will invoke the middleware in the order of the arguments.

type DoerFunc

type DoerFunc func(req *http.Request) (*http.Response, error)

DoerFunc adapts a function to implement Doer

func (DoerFunc) Do

func (f DoerFunc) Do(req *http.Request) (*http.Response, error)

Do implements the Doer interface

type FormMarshaler

type FormMarshaler struct{}

FormMarshaler implements BodyMarshaler. It marshals values into URL-Encoded form data.

The value can be either a map[string][]string, map[string]string, url.Values, or a struct with `url` tags.

func (*FormMarshaler) Marshal

func (*FormMarshaler) Marshal(v interface{}) (data []byte, contentType string, err error)

Marshal implements BodyMarshaler.

type JSONMarshaler

type JSONMarshaler struct {
	Indent bool
}

JSONMarshaler implement BodyMarshaler and BodyUnmarshaler. It marshals values to and from JSON. If Indent is true, marshaled JSON will be indented.

r := requests.Requests{
    Body: &JSONMarshaler{},
}

func (*JSONMarshaler) Marshal

func (m *JSONMarshaler) Marshal(v interface{}) (data []byte, contentType string, err error)

Marshal implements BodyMarshaler.

func (*JSONMarshaler) Unmarshal

func (m *JSONMarshaler) Unmarshal(data []byte, contentType string, v interface{}) error

Unmarshal implements BodyUnmarshaler.

type MarshalFunc

type MarshalFunc func(v interface{}) ([]byte, string, error)

MarshalFunc adapts a function to the BodyMarshaler interface.

func (MarshalFunc) Marshal

func (f MarshalFunc) Marshal(v interface{}) ([]byte, string, error)

Marshal implements the BodyMarshaler interface.

type Middleware

type Middleware func(Doer) Doer

Middleware can be used to wrap Doers with additional functionality:

loggingMiddleware := func(next Doer) Doer {
    return func(req *http.Request) (*http.Response, error) {
        logRequest(req)
        return next(req)
    }
}

Middleware can be applied to a Requests object with the Use() option:

reqs.Apply(requests.Use(loggingMiddleware))

Middleware itself is an Option, so it can also be applied directly:

reqs.Apply(Middleware(loggingMiddleware))

func Dump

func Dump(w io.Writer) Middleware

Dump dumps requests and responses to a writer. Just intended for debugging.

func DumpToStandardOut

func DumpToStandardOut() Middleware

DumpToStandardOut dumps requests to os.Stdout.

func Non2XXResponseAsError

func Non2XXResponseAsError() Middleware

Non2XXResponseAsError converts error responses from the server into an `error`. For simple code, this removes the need to check both `err` and `resp.StatusCode`.

The body of the response is dumped into the error message, for example:

fmt.Println(err)

...might output:

    server returned non-2XX status code:
    HTTP/1.1 407 Proxy Authentication Required
	   Content-Length: 5
	   Content-Type: text/plain; charset=utf-8
	   Date: Mon, 22 Jan 2018 18:55:18 GMT

	   boom!

This probably isn't appropriate for production code, where the full response might be sensitive, or too long, or binary, but it should be OK for tests or sample code.

For production code, consider using this as an example for your own error handler.

func (Middleware) Apply

func (m Middleware) Apply(r *Requests) error

Apply implements Option

type MultiUnmarshaler

type MultiUnmarshaler struct {
	// contains filtered or unexported fields
}

MultiUnmarshaler implements BodyUnmarshaler. It uses the value of the Content-Type header in the response to choose between the JSON and XML unmarshalers. If Content-Type is something else, an error is returned.

func (*MultiUnmarshaler) Unmarshal

func (m *MultiUnmarshaler) Unmarshal(data []byte, contentType string, v interface{}) error

Unmarshal implements BodyUnmarshaler.

type Option

type Option interface {

	// Apply modifies the Requests argument.  The Requests pointer will never be nil.
	// Returning an error will stop applying the request of the Options, and the error
	// will float up to the original caller.
	Apply(*Requests) error
}

Option applies some setting to a Requests object. Options can be passed as arguments to most of Requests' methods.

func Accept

func Accept(accept string) Option

Accept sets the Accept header.

func AddHeader

func AddHeader(key, value string) Option

AddHeader adds a header value, using Header.Add()

func BasicAuth

func BasicAuth(username, password string) Option

BasicAuth sets the Authorization header to "Basic <encoded username and password>". If username and password are empty, it deletes the Authorization header.

func BearerAuth

func BearerAuth(token string) Option

BearerAuth sets the Authorization header to "Bearer <token>". If the token is empty, it deletes the Authorization header.

func Body

func Body(body interface{}) Option

Body sets Requests.Body

func Client

func Client(opts ...clients.Option) Option

Client replaces Requests.Doer with an *http.Client. The client will be created and configured using the `clients` package.

func ContentType

func ContentType(contentType string) Option

ContentType sets the Content-Type header.

func Delete

func Delete(paths ...string) Option

Delete sets the HTTP method to "DELETE". Optional path arguments will be applied via the RelativeURL option.

func DeleteHeader

func DeleteHeader(key string) Option

DeleteHeader deletes a header key, using Header.Del()

func Form

func Form() Option

Form sets Requests.Marshaler to the FormMarshaler, which marshals the body into form-urlencoded. The FormMarshaler will set the Content-Type header to "application/x-www-form-urlencoded" unless explicitly overwritten.

func Get

func Get(paths ...string) Option

Get sets the HTTP method to "GET". Optional path arguments will be applied via the RelativeURL option.

func Head(paths ...string) Option

Head sets the HTTP method to "HEAD". Optional path arguments will be applied via the RelativeURL option.

func Header(key, value string) Option

Header sets a header value, using Header.Set()

func Host

func Host(host string) Option

Host sets Requests.Host

func JSON

func JSON(indent bool) Option

JSON sets Requests.Marshaler to the JSONMarshaler. If the arg is true, the generated JSON will be indented. The JSONMarshaler will set the Content-Type header to "application/json" unless explicitly overwritten.

func Marshaler

func Marshaler(m BodyMarshaler) Option

Marshaler sets Requests.Marshaler

func Method

func Method(m string, paths ...string) Option

Method sets the HTTP method (e.g. GET/DELETE/etc). If path arguments are passed, they will be applied via the RelativeURL option.

func Patch

func Patch(paths ...string) Option

Patch sets the HTTP method to "PATCH". Optional path arguments will be applied via the RelativeURL option.

func Post

func Post(paths ...string) Option

Post sets the HTTP method to "POST". Optional path arguments will be applied via the RelativeURL option.

func Put

func Put(paths ...string) Option

Put sets the HTTP method to "PUT". Optional path arguments will be applied via the RelativeURL option.

func QueryParam

func QueryParam(k, v string) Option

QueryParam adds a query parameter.

func QueryParams

func QueryParams(queryStructs ...interface{}) Option

QueryParams adds params to the Requests.QueryParams member. The arguments may be either map[string][]string, map[string]string, url.Values, or a struct. The argument values are merged into Requests.QueryParams, overriding existing values.

If the arg is a struct, the struct is marshaled into a url.Values object using the github.com/google/go-querystring/query package. Structs should tag their members with the "url" tag, e.g.:

type ReqParams struct {
    Color string `url:"color"`
}

An error will be returned if marshaling the struct fails.

func RelativeURL

func RelativeURL(paths ...string) Option

RelativeURL resolves the arg as a relative URL references against the current URL, using the standard lib's url.URL.ResolveReference() method. For example:

r, _ := requests.New(Get("http://test.com"), RelativeURL("red"))
fmt.Println(r.URL.String())  // http://test.com/red

Multiple arguments will be resolved in order:

r, _ := requests.New(Get("http://test.com"), RelativeURL("red", "blue"))
fmt.Println(r.URL.String())  // http://test.com/red/blue

func URL

func URL(rawurl string) Option

URL sets the request URL. Returns an error if arg is not a valid URL.

func Unmarshaler

func Unmarshaler(m BodyUnmarshaler) Option

Unmarshaler sets Requests.Unmarshaler

func Use

func Use(m ...Middleware) Option

Use appends middlware to Requests.Middleware. Middleware is invoked in the order added.

func WithDoer

func WithDoer(d Doer) Option

WithDoer replaces Requests.Doer. If nil, Requests will revert to using the http.DefaultClient.

func XML

func XML(indent bool) Option

XML sets Requests.Marshaler to the XMLMarshaler. If the arg is true, the generated XML will be indented. The XMLMarshaler will set the Content-Type header to "application/xml" unless explicitly overwritten.

type OptionFunc

type OptionFunc func(*Requests) error

OptionFunc adapts a function to the Option interface.

func (OptionFunc) Apply

func (f OptionFunc) Apply(r *Requests) error

Apply implements Option.

type Requests

type Requests struct {

	// Method defaults to "GET".
	Method string
	URL    *url.URL

	// Header supplies the request headers.  If the Content-Type header
	// is explicitly set here, it will override the Content-Type header
	// supplied by the Marshaler.
	Header http.Header

	// advanced options, not typically used.  If not sure, leave them
	// blank.
	// Most of these settings are set automatically by the http package.
	// Setting them here will override the automatic values.
	GetBody          func() (io.ReadCloser, error)
	ContentLength    int64
	TransferEncoding []string
	Close            bool
	Host             string
	Trailer          http.Header

	// QueryParams are added to the request, in addition to any
	// query params already encoded in the URL
	QueryParams url.Values

	// Body can be set to a string, []byte, io.Reader, or a struct.
	// If set to a string, []byte, or io.Reader,
	// the value will be used as the body of the request.
	// If set to a struct, the Marshaler
	// will be used to marshal the value into the request body.
	Body interface{}

	// Marshaler will be used to marshal the Body value into the body
	// of requests.  It is only used if Body is a struct value.
	// Defaults to the DefaultMarshaler, which marshals to JSON.
	//
	// If no Content-Type header has been explicitly set in Requests.Header, the
	// Marshaler will supply an appropriate one.
	Marshaler BodyMarshaler

	// Doer holds the HTTP client for used to execute requests.
	// Defaults to http.DefaultClient.
	Doer Doer

	// Middleware wraps the Doer.  Middleware will be invoked in the order
	// it is in this slice.
	Middleware []Middleware

	// Unmarshaler will be used by the Receive methods to unmarshal
	// the response body.  Defaults to DefaultUnmarshaler, which unmarshals
	// multiple content types based on the Content-Type response header.
	Unmarshaler BodyUnmarshaler
}

Requests is an HTTP Request builder and sender.

A Requests object can be used to construct *http.Requests, send requests via a configurable HTTP client, and unmarshal the response. A Requests object is configured by setting its members, which in most cases mirror the members of *http.Request, or by applying Options, using the Apply() and With() methods.

Once configured, you can use Requests solely as a *http.Request factory, by calling Request() or RequestContext().

Or you can use the Requests to construct and send requests (via a configurable Doer) and get back the raw *http.Response, with the Send() and SendContext() methods.

Or you can have Requests also read the response body and unmarshal it into a struct, with Receive(), ReceiveContext(), ReceiveFull(), and ReceiveFullContext().

A Requests object can be constructed as a literal:

r := requests.Requests{
         URL:    u,
         Method: "POST",
         Body:   b,
     }

...or via the New() constructor:

reqs, err := requests.New(requests.Post("http://test.com/red"), requests.Body(b))

Additional options can be applied with Apply():

err := reqs.Apply(Accept("application/json"))

Requests can be cloned, to create an identically configured Requests object, which can then be further configured without affecting the parent:

reqs2 := reqs.Clone()
err := reqs2.Apply(Header("X-Frame","1"))

With() is equivalent to Clone() and Apply():

reqs2, err := reqs.With(Header("X-Frame","1"))

The remaining methods of Requests are for creating HTTP requests, sending them, and handling the responses: Request, Send, Receive, and ReceiveFull.

req, err        := reqs.Request()           // create a requests
resp, err       := reqs.Send()                // create and send a request

var m Resource
resp, body, err := reqs.Receive(&m)         // create and send request, read and unmarshal response

var e ErrorResponse
resp, body, err := reqs.ReceiveFull(&m, &e) // create and send request, read response, unmarshal 2XX responses
                                            // into m, and other responses in e

Request, Send, Receive, and ReceiveFull all accept a varargs of Options, which will be applied only to a single request, not to the Requests object.

req, err 	   := reqs.Request(
                   	Put("users/bob"),
                     Body(bob),
                   )

RequestContext, SendContext, ReceiveContext, and ReceiveFullContext variants accept a context, which is attached to the constructed request:

req, err        := reqs.RequestContext(ctx)

func New

func New(options ...Option) (*Requests, error)

New returns a new Requests.

func (*Requests) Apply

func (r *Requests) Apply(opts ...Option) error

Apply applies the options to the receiver.

func (*Requests) Clone

func (r *Requests) Clone() *Requests

Clone returns a deep copy of a Requests. Useful inheriting and adding settings from a parent Requests without modifying the parent. For example,

    parent, _ := requests.New(Get("https://api.io/"))
    foo := parent.Clone()
    foo.Apply(Get("foo/"))
	   bar := parent.Clone()
    bar.Apply(Post("bar/"))

foo and bar will both use the same client, but send requests to https://api.io/foo/ and https://api.io/bar/ respectively.

func (*Requests) Do

func (r *Requests) Do(req *http.Request) (*http.Response, error)

Do implements Doer. Executes the request using the configured Doer and Middleware.

func (*Requests) Receive

func (r *Requests) Receive(successV interface{}, opts ...Option) (resp *http.Response, body string, err error)

Receive creates a new HTTP request and returns the response. Success responses (2XX) are unmarshaled into successV. Any error creating the request, sending it, or decoding a 2XX response is returned.

If option arguments are passed, they are applied to this single request only.

func (*Requests) ReceiveContext

func (r *Requests) ReceiveContext(ctx context.Context, successV interface{}, opts ...Option) (resp *http.Response, body string, err error)

ReceiveContext does the same as Receive, but requires a context.

func (*Requests) ReceiveFull

func (r *Requests) ReceiveFull(successV, failureV interface{}, opts ...Option) (resp *http.Response, body string, err error)

ReceiveFull creates a new HTTP request and returns the response. Success responses (2XX) are unmarshaled into successV and other responses are unmarshaled into failureV. Any error creating the request, sending it, or decoding the response is returned.

func (*Requests) ReceiveFullContext

func (r *Requests) ReceiveFullContext(ctx context.Context, successV, failureV interface{}, opts ...Option) (resp *http.Response, body string, err error)

ReceiveFullContext does the same as ReceiveFull

func (*Requests) Request

func (r *Requests) Request(opts ...Option) (*http.Request, error)

Request returns a new http.Request.

If Options are passed, they will only by applied to this single request.

If r.Body is a struct, it will be marshaled into the request body using r.Marshaler. The Marshaler will also set the Content-Type header, unless this header is already explicitly set in r.Header.

If r.Body is an io.Reader, string, or []byte, it is set as the request body directly, and no default Content-Type is set.

func (*Requests) RequestContext

func (r *Requests) RequestContext(ctx context.Context, opts ...Option) (*http.Request, error)

RequestContext does the same as Request, but requires a context. Use this to set a request timeout:

req, err := r.RequestContext(context.WithTimeout(context.Background(), 10 * time.Seconds))

func (*Requests) Send

func (r *Requests) Send(opts ...Option) (*http.Response, error)

Send executes a request with the Doer. The response body is not closed: it is the caller's responsibility to close the response body. If the caller prefers the body as a byte slice, or prefers the body unmarshaled into a struct, see the Receive methods below.

Additional options arguments can be passed. They will be applied to this request only.

func (*Requests) SendContext

func (r *Requests) SendContext(ctx context.Context, opts ...Option) (*http.Response, error)

SendContext does the same as Request, but requires a context.

func (*Requests) With

func (r *Requests) With(opts ...Option) (*Requests, error)

With clones the Requests object, then applies the options to the clone.

type UnmarshalFunc

type UnmarshalFunc func(data []byte, contentType string, v interface{}) error

UnmarshalFunc adapts a function to the BodyUnmarshaler interface.

func (UnmarshalFunc) Unmarshal

func (f UnmarshalFunc) Unmarshal(data []byte, contentType string, v interface{}) error

Unmarshal implements the BodyUnmarshaler interface.

type XMLMarshaler

type XMLMarshaler struct {
	Indent bool
}

XMLMarshaler implements BodyMarshaler and BodyUnmarshaler. It marshals values to and from XML. If Indent is true, marshaled XML will be indented.

r := requests.Requests{
    Marshaler: &XMLMarshaler{},
}

func (*XMLMarshaler) Marshal

func (m *XMLMarshaler) Marshal(v interface{}) (data []byte, contentType string, err error)

Marshal implements BodyMarshaler.

func (*XMLMarshaler) Unmarshal

func (*XMLMarshaler) Unmarshal(data []byte, contentType string, v interface{}) error

Unmarshal implements BodyUnmarshaler.

Directories

Path Synopsis
Package clients is a set of utilities for creating and configuring instances of http.Client.
Package clients is a set of utilities for creating and configuring instances of http.Client.
Package clientserver is a utility for writing HTTP tests.
Package clientserver is a utility for writing HTTP tests.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL