Skip to content

Commit

Permalink
add json and xml encoder setting
Browse files Browse the repository at this point in the history
  • Loading branch information
imroc committed May 31, 2017
1 parent 4637953 commit 00fd6f4
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 63 deletions.
131 changes: 68 additions & 63 deletions req.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,23 @@ type FileUpload struct {
File io.ReadCloser
}

// BodyJSON make the object be encoded in json format and set it to the request body
type BodyJSON interface{}

// BodyXML make the object be encoded in xml format and set it to the request body
type BodyXML interface{}

type jsonEncOpts struct {
indentPrefix string
indentValue string
escapeHTML bool
}

type xmlEncOpts struct {
prefix string
indent string
}

// Debug enable debug mode if set to true
var Debug bool

Expand Down Expand Up @@ -98,8 +115,10 @@ const (
)

type Req struct {
client *http.Client
flag int
client *http.Client
jsonEncOpts *jsonEncOpts
xmlEncOpts *xmlEncOpts
flag int
}

func newClient() *http.Client {
Expand Down Expand Up @@ -140,15 +159,12 @@ func (r *Req) Do(method, rawurl string, v ...interface{}) (resp *Resp, err error
ProtoMinor: 1,
}
resp = &Resp{req: req, r: r}
handleBody := func(b *body) {
if b == nil {
return
}
resp.reqBody = b.Data
handleBody := func(data []byte, contentType string) {
resp.reqBody = data
req.Body = resp.getReqBody()
req.ContentLength = int64(len(resp.reqBody))
if b.ContentType != "" && req.Header.Get("Content-Type") == "" {
req.Header.Set("Content-Type", b.ContentType)
if contentType != "" && req.Header.Get("Content-Type") == "" {
req.Header.Set("Content-Type", contentType)
}
}

Expand Down Expand Up @@ -178,12 +194,49 @@ func (r *Req) Do(method, rawurl string, v ...interface{}) (resp *Resp, err error
if err != nil {
return nil, err
}
handleBody(&body{Data: bs})
handleBody(bs, "")
if rc, ok := t.(io.ReadCloser); ok {
rc.Close()
}
case *body:
handleBody(t)
case BodyJSON:
var data []byte
if r.jsonEncOpts != nil {
opts := r.jsonEncOpts
var buf bytes.Buffer
enc := json.NewEncoder(&buf)
enc.SetIndent(opts.indentPrefix, opts.indentValue)
enc.SetEscapeHTML(opts.escapeHTML)
err = enc.Encode(p)
if err != nil {
return nil, err
}
data = buf.Bytes()
} else {
data, err = json.Marshal(p)
if err != nil {
return nil, err
}
}
handleBody(data, "application/json; charset=UTF-8")
case BodyXML:
var data []byte
if r.xmlEncOpts != nil {
opts := r.xmlEncOpts
var buf bytes.Buffer
enc := xml.NewEncoder(&buf)
enc.Indent(opts.prefix, opts.indent)
err = enc.Encode(p)
if err != nil {
return nil, err
}
data = buf.Bytes()
} else {
data, err = xml.Marshal(p)
if err != nil {
return nil, err
}
}
handleBody(data, "application/xml; charset=UTF-8")
case Param:
if method == "GET" {
queryParam = append(queryParam, QueryParam(t))
Expand All @@ -193,9 +246,9 @@ func (r *Req) Do(method, rawurl string, v ...interface{}) (resp *Resp, err error
case QueryParam:
queryParam = append(queryParam, t)
case string:
handleBody(&body{Data: []byte(t)})
handleBody([]byte(t), "")
case []byte:
handleBody(&body{Data: []byte(t)})
handleBody(t, "")
case *http.Client:
resp.client = t
case FileUpload:
Expand Down Expand Up @@ -228,11 +281,7 @@ func (r *Req) Do(method, rawurl string, v ...interface{}) (resp *Resp, err error
}
}
paramStr := params.Encode()
body := &body{
ContentType: "application/x-www-form-urlencoded; charset=UTF-8",
Data: []byte(paramStr),
}
handleBody(body)
handleBody([]byte(paramStr), "application/x-www-form-urlencoded; charset=UTF-8")
}

if len(queryParam) > 0 {
Expand Down Expand Up @@ -371,50 +420,6 @@ func (r *Resp) Cost() time.Duration {
return r.cost
}

// Body represents request's body
type body struct {
ContentType string
Data []byte
}

// BodyXML get request's body as xml
func BodyXML(v interface{}) interface{} {
b := new(body)
switch t := v.(type) {
case string:
b.Data = []byte(t)
case []byte:
b.Data = t
default:
bs, err := xml.Marshal(v)
if err != nil {
return err
}
b.Data = bs
}
b.ContentType = "application/xml; charset=UTF-8"
return b
}

// BodyJSON get request's body as json
func BodyJSON(v interface{}) interface{} {
b := new(body)
switch t := v.(type) {
case string:
b.Data = []byte(t)
case []byte:
b.Data = t
default:
bs, err := json.Marshal(v)
if err != nil {
return err
}
b.Data = bs
}
b.ContentType = "application/json; charset=UTF-8"
return b
}

// File upload files matching the name pattern such as
// /usr/*/bin/go* (assuming the Separator is '/')
func File(patterns ...string) interface{} {
Expand Down
69 changes: 69 additions & 0 deletions setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,72 @@ func (r *Req) SetProxy(proxy func(*http.Request) (*url.URL, error)) error {
func SetProxy(proxy func(*http.Request) (*url.URL, error)) error {
return std.SetProxy(proxy)
}

// SetJSONEscapeHTML specifies whether problematic HTML characters
// should be escaped inside JSON quoted strings.
// The default behavior is to escape &, <, and > to \u0026, \u003c, and \u003e
// to avoid certain safety problems that can arise when embedding JSON in HTML.
//
// In non-HTML settings where the escaping interferes with the readability
// of the output, SetEscapeHTML(false) disables this behavior.
func (r *Req) SetJSONEscapeHTML(escape bool) {
opts := r.getJSONEncOpts()
opts.escapeHTML = escape
}

// SetJSONEscapeHTML specifies whether problematic HTML characters
// should be escaped inside JSON quoted strings.
// The default behavior is to escape &, <, and > to \u0026, \u003c, and \u003e
// to avoid certain safety problems that can arise when embedding JSON in HTML.
//
// In non-HTML settings where the escaping interferes with the readability
// of the output, SetEscapeHTML(false) disables this behavior.
func SetJSONEscapeHTML(escape bool) {
std.SetJSONEscapeHTML(escape)
}

// SetJSONIndent instructs the encoder to format each subsequent encoded
// value as if indented by the package-level function Indent(dst, src, prefix, indent).
// Calling SetIndent("", "") disables indentation.
func (r *Req) SetJSONIndent(prefix, indent string) {
opts := r.getJSONEncOpts()
opts.indentPrefix = prefix
opts.indentValue = indent
}

// SetJSONIndent instructs the encoder to format each subsequent encoded
// value as if indented by the package-level function Indent(dst, src, prefix, indent).
// Calling SetIndent("", "") disables indentation.
func SetJSONIndent(prefix, indent string) {
std.SetJSONIndent(prefix, indent)
}

// SetXMLIndent sets the encoder to generate XML in which each element
// begins on a new indented line that starts with prefix and is followed by
// one or more copies of indent according to the nesting depth.
func (r *Req) SetXMLIndent(prefix, indent string) {
opts := r.getXMLEncOpts()
opts.prefix = prefix
opts.indent = indent
}

// SetXMLIndent sets the encoder to generate XML in which each element
// begins on a new indented line that starts with prefix and is followed by
// one or more copies of indent according to the nesting depth.
func SetXMLIndent(prefix, indent string) {
std.SetXMLIndent(prefix, indent)
}

func (r *Req) getJSONEncOpts() *jsonEncOpts {
if r.jsonEncOpts == nil {
r.jsonEncOpts = &jsonEncOpts{escapeHTML: true}
}
return r.jsonEncOpts
}

func (r *Req) getXMLEncOpts() *xmlEncOpts {
if r.xmlEncOpts == nil {
r.xmlEncOpts = &xmlEncOpts{}
}
return r.xmlEncOpts
}

0 comments on commit 00fd6f4

Please sign in to comment.