Documentation ¶
Overview ¶
Package json adds composition friendly JSON support to HTTP clients
A HTTP client request can be constructed using NewRequest which takes fluent functional args to specify the Body and Query parameters:
// queryArgs can be any type that can be serialized by // github.com/google/go-querystring/query" queryArgs := struct { Foo int Hoo string }{ 42, "something" } // body can be any type that can be serialied to JSON body := struct { Hey string }{ "Hey!" } // make a HTTP request with these req, err := json.NewRequest( "GET", url, json.Query(queryArgs), json.Body(body), )
The response can then be sent using standard http.Client mechanisms.
This package also exposes a `Transport` type for parsing `application/json` content-types. The middleware pattern is built on top of the standard http.RoundTripper interface which allows other processing activity to be properly chained. For instance, it is possible to properly chain Retries (assuming that was also implemented as a http.RoundTripper):
// output can be any JSON decodable type var output struct { ... } client := &http.Client{ Transport: json.Transport{ Result: &output, // Transports can be chained! Transport: retry.Transport{ // See github.com/tvastar/http/retry for details // of how to use the retry transport Transport: http.DefaultTransport, } }, } res, err := client.Do(req) // if there was no error, output will be filled in!
Example ¶
package main import ( "bytes" gojson "encoding/json" "fmt" "io/ioutil" "net/http" "net/http/httptest" "github.com/tvastar/http/json" ) func main() { // input and output can be strongly typed below makeCall := func(url string, query, input, output interface{}) (*http.Response, error) { req, err := json.NewRequest( "POST", url, json.Query(query), json.Body(input), ) if err != nil { return nil, err } client := &http.Client{ Transport: json.Transport{ Result: output, Transport: http.DefaultTransport, }, } return client.Do(req) } // actual test server := simpleTestServer(func(query string, jsonBody interface{}) interface{} { return map[string]interface{}{ "Query": query, "Body": jsonBody, } }) defer server.Close() input := map[string]interface{}{"hello": 42} var output interface{} res, err := makeCall(server.URL, sampleQuery(), input, &output) if err != nil { panic(err) } defer res.Body.Close() greeting, err := ioutil.ReadAll(res.Body) if err != nil { panic(err) } // both of these should agree fmt.Printf("%s", greeting) fmt.Println(output) } func sampleQuery() interface{} { return struct { Foo int `url:"foo"` Heya bool `url:"heya"` }{42, true} } func simpleTestServer(fn func(query string, body interface{}) interface{}) *httptest.Server { return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var body interface{} err := gojson.NewDecoder(r.Body).Decode(&body) if err != nil { panic(err) } var buf bytes.Buffer enc := gojson.NewEncoder(&buf) enc.SetEscapeHTML(false) err = enc.Encode(fn(r.URL.Query().Encode(), body)) if err != nil { panic(err) } w.Header().Add("Content-Type", "application/json") _, err = w.Write(buf.Bytes()) if err != nil { panic(err) } })) }
Output: {"Body":{"hello":42},"Query":"foo=42&heya=true"} map[Body:map[hello:42] Query:foo=42&heya=true]
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NewRequest ¶
NewRequest creates a new http Request with the provided options.
See Body and Query for interesting JSON options.
Example ¶
package main import ( "bytes" "fmt" "strings" "github.com/tvastar/http/json" ) func main() { r, err := json.NewRequest( "POST", "http://localhost:99/boo?x=1", json.Query(sampleQuery()), json.Body(map[string]interface{}{"hello": 42}), ) var buf bytes.Buffer err2 := r.Write(&buf) fmt.Println("Error:", err, err2) fmt.Println(strings.Replace(buf.String(), "\r", "", 100)) } func sampleQuery() interface{} { return struct { Foo int `url:"foo"` Heya bool `url:"heya"` }{42, true} }
Output: Error: <nil> <nil> POST /boo?foo=42&heya=true&x=1 HTTP/1.1 Host: localhost:99 User-Agent: Go-http-client/1.1 Content-Length: 13 Content-Type: application/json {"hello":42}
Types ¶
type Option ¶
Option is an option to pass to NewRequest.
Custom options can either mutate the request or create a new request and return that
type Transport ¶
type Transport struct { Result interface{} // where the result is stored Transport http.RoundTripper // the base transport }
Transport implements a JSON-decoder HTTP transport.
This wraps another transport and only parses the response. If the response has the content type `application/json`, then the response body is decoded into `Result`. Note that `Result` must be a reference type (i.e. something that can be passed to json.Unmarshal).