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).