From 156b250b71f71ad3e63ab18373d2a6d2b22b9973 Mon Sep 17 00:00:00 2001 From: tpkeeper Date: Sun, 5 May 2019 10:42:51 +0800 Subject: [PATCH] optimize json format --- README.md | 48 ++++++++++--------- gindump.go | 78 ++++++++++++------------------- gindump_test.go | 13 ++++-- go.mod | 6 +++ go.sum | 22 +++++++++ parse.go | 120 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 214 insertions(+), 73 deletions(-) create mode 100644 parse.go diff --git a/README.md b/README.md index 8a23b6f..1ef5c8b 100644 --- a/README.md +++ b/README.md @@ -53,31 +53,37 @@ func main() { ### Output is as follows ```sh +=== RUN TestMIMEPOSTFORM [GIN-dump]: Request-Header: - { - Content-Length : [66] - Content-Type : [application/json;charset=utf-8] - Accept-Encoding : [gzip] - User-Agent : [Go-http-client/1.1] - } - +{ + "Content-Type": [ + "application/x-www-form-urlencoded" + ] +} Request-Body: - { - sms_code : 1111 - telephone : 18322889845 - password : lfajkdffsefadfare - } - +{ + "bar": [ + "baz" + ], + "foo": [ + "bar", + "bar2" + ] +} Response-Header: - { - Content-Type : [application/json; charset=utf-8] - } - +{ + "Content-Type": [ + "application/json; charset=utf-8" + ] +} Response-Body: - { - data : sms_code error - ok : %!s(bool=false) - } +{ + "data": { + "addr": "tpkeeper@qq.com", + "name": "jfise" + }, + "ok": true +} ``` diff --git a/gindump.go b/gindump.go index 3b62eb4..b6e78a6 100644 --- a/gindump.go +++ b/gindump.go @@ -2,7 +2,6 @@ package gindump import ( "bytes" - "encoding/json" "fmt" "github.com/gin-gonic/gin" "io/ioutil" @@ -17,8 +16,14 @@ func Dump(cb func(dumpStr string)) gin.HandlerFunc { return func(ctx *gin.Context) { //dump req header var strB strings.Builder - strB.WriteString("[GIN-dump]:\nRequest-Header:\n") - strB.WriteString(strHeader(ctx.Request.Header)) + s, err := FormatToJson(ctx.Request.Header) + + if err != nil { + strB.WriteString(fmt.Sprintf("\nparse req header err \n" + err.Error())) + } else { + strB.WriteString("[GIN-dump]:\nRequest-Header:\n") + strB.WriteString(string(s)) + } //dump req body if ctx.Request.ContentLength > 0 { @@ -38,19 +43,20 @@ func Dump(cb func(dumpStr string)) gin.HandlerFunc { switch ct { case gin.MIMEJSON: - var mapReq map[string]interface{} bts, err := ioutil.ReadAll(rdr) if err != nil { strB.WriteString(fmt.Sprintf("\nread rdr err \n %s", err.Error())) goto DumpRes } - if err := json.Unmarshal(bts, &mapReq); err != nil { - strB.WriteString(fmt.Sprintf("\nparse bodyCache err \n" + err.Error())) + + s, err := FormatJsonBytes(bts) + if err != nil { + strB.WriteString(fmt.Sprintf("\nparse req body err \n" + err.Error())) goto DumpRes } strB.WriteString("\nRequest-Body:\n") - strB.WriteString(strMap(mapReq)) + strB.WriteString(string(s)) case gin.MIMEPOSTForm: bts, err := ioutil.ReadAll(rdr) if err != nil { @@ -58,16 +64,16 @@ func Dump(cb func(dumpStr string)) gin.HandlerFunc { goto DumpRes } val, err := url.ParseQuery(string(bts)) - valMap := (map[string][]string)(val) + + s, err := FormatToJson(val) if err != nil { - strB.WriteString(fmt.Sprintf("\nparse formdata err \n %s", err.Error())) + strB.WriteString(fmt.Sprintf("\nparse req body err \n" + err.Error())) goto DumpRes } strB.WriteString("\nRequest-Body:\n") - strB.WriteString(strMap(valMap)) + strB.WriteString(string(s)) case gin.MIMEMultipartPOSTForm: - case gin.MIMEHTML: default: } } @@ -77,8 +83,13 @@ func Dump(cb func(dumpStr string)) gin.HandlerFunc { ctx.Next() //dump res header - strB.WriteString("\nResponse-Header:\n") - strB.WriteString(strHeader(ctx.Writer.Header())) + sHeader, err := FormatToJson(ctx.Writer.Header()) + if err != nil { + strB.WriteString(fmt.Sprintf("\nparse res header err \n" + err.Error())) + } else { + strB.WriteString("\nResponse-Header:\n") + strB.WriteString(string(sHeader)) + } bw, ok := ctx.Writer.(*bodyWriter) if !ok { @@ -96,18 +107,20 @@ func Dump(cb func(dumpStr string)) gin.HandlerFunc { } switch ct { case gin.MIMEJSON: - var mapRes map[string]interface{} - if err := json.Unmarshal(bw.bodyCache.Bytes(), &mapRes); err != nil { + + s, err := FormatJsonBytes(bw.bodyCache.Bytes()) + if err != nil { strB.WriteString(fmt.Sprintf("\nparse bodyCache err \n" + err.Error())) goto End } strB.WriteString("\nResponse-Body:\n") - strB.WriteString(strMap(mapRes)) + strB.WriteString(string(s)) case gin.MIMEHTML: default: } } + End: if cb != nil { cb(strB.String()) @@ -117,39 +130,6 @@ func Dump(cb func(dumpStr string)) gin.HandlerFunc { } } -func strHeader(header http.Header) string { - var strB strings.Builder - strB.WriteString(" {\n") - for key, value := range header { - strB.WriteString(fmt.Sprintf(" %s : %s\n", key, value)) - } - strB.WriteString(" }\n") - return strB.String() -} - -func strMap(m interface{}) string { - var strB strings.Builder - switch m.(type) { - case map[string]interface{}: - mInter := m.(map[string]interface{}) - strB.WriteString(" {\n") - for key, value := range mInter { - strB.WriteString(fmt.Sprintf(" %s : %s\n", key, value)) - } - strB.WriteString(" }\n") - break - case map[string][]string: - mSl := m.(map[string][]string) - strB.WriteString(" {\n") - for key, value := range mSl { - strB.WriteString(fmt.Sprintf(" %s : %s\n", key, value)) - } - strB.WriteString(" }\n") - break - } - return strB.String() -} - type bodyWriter struct { gin.ResponseWriter bodyCache *bytes.Buffer diff --git a/gindump_test.go b/gindump_test.go index 2d0c7c0..c8f026a 100644 --- a/gindump_test.go +++ b/gindump_test.go @@ -27,7 +27,9 @@ func performRequest(r http.Handler, method,contentType string ,path string,body func TestMIMEJSON(t *testing.T) { router := gin.New() - router.Use(Dump()) + router.Use(Dump(func(dumpStr string) { + fmt.Println(dumpStr) + })) router.POST("/dump", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ @@ -59,12 +61,17 @@ func TestMIMEJSON(t *testing.T) { func TestMIMEPOSTFORM(t *testing.T) { router := gin.New() - router.Use(Dump()) + router.Use(Dump(func(dumpStr string) { + fmt.Println(dumpStr) + })) router.POST("/dump", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "ok":true, - "data":"gin-dump", + "data":map[string]interface{}{ + "name":"jfise" , + "addr":"tpkeeper@qq.com", + }, }) }) diff --git a/go.mod b/go.mod index b78cbbc..94fc707 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,14 @@ require ( github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 // indirect github.com/gin-gonic/gin v1.3.0 github.com/golang/protobuf v1.3.1 // indirect + github.com/json-iterator/go v1.1.6 // indirect github.com/mattn/go-isatty v0.0.7 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/stretchr/testify v1.3.0 // indirect github.com/ugorji/go v1.1.4 // indirect + golang.org/x/net v0.0.0-20190419010253-1f3472d942ba // indirect + gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/go-playground/validator.v8 v8.18.2 // indirect gopkg.in/yaml.v2 v2.2.2 // indirect ) diff --git a/go.sum b/go.sum index f6a8179..a1946bd 100644 --- a/go.sum +++ b/go.sum @@ -1,15 +1,37 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-gonic/gin v1.3.0 h1:kCmZyPklC0gVdL728E6Aj20uYBJV93nj/TkwBTKhFbs= github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190419010253-1f3472d942ba h1:h0zCzEL5UW1mERvwTN6AXcc75PpLkY6OcReia6Dq1BM= +golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= diff --git a/parse.go b/parse.go new file mode 100644 index 0000000..e9dc695 --- /dev/null +++ b/parse.go @@ -0,0 +1,120 @@ +package gindump + +import ( + "bytes" + "encoding/json" + "fmt" + "sort" + "strconv" + "strings" +) + +var StringMaxLength = 0 +var Newline = "\n" +var Indent = 4 + +func FormatJsonBytes(data []byte) ([]byte, error) { + var v interface{} + if err := json.Unmarshal(data, &v); err != nil { + return nil, err + } + + return []byte(format(v, 1)), nil +} + +//support +func FormatToJson(v interface{}) ([]byte, error) { + data, err := json.Marshal(v) + if err != nil { + return nil, err + } + return FormatJsonBytes(data) +} + +func format(v interface{}, depth int) string { + switch val := v.(type) { + case string: + return formatString(val) + case float64: + return fmt.Sprint(strconv.FormatFloat(val, 'f', -1, 64)) + case bool: + return fmt.Sprint(strconv.FormatBool(val)) + case nil: + return fmt.Sprint("null") + case map[string]interface{}: + return formatMap(val, depth) + case []interface{}: + return formatArray(val, depth) + } + + return "" +} + +func formatString(s string) string { + r := []rune(s) + if StringMaxLength != 0 && len(r) >= StringMaxLength { + s = string(r[0:StringMaxLength]) + "..." + } + + buf := &bytes.Buffer{} + encoder := json.NewEncoder(buf) + encoder.SetEscapeHTML(false) + encoder.Encode(s) + s = string(buf.Bytes()) + s = strings.TrimSuffix(s, "\n") + + return fmt.Sprint(s) +} + +func formatMap(m map[string]interface{}, depth int) string { + if len(m) == 0 { + return "{}" + } + + currentIndent := generateIndent(depth - 1) + nextIndent := generateIndent(depth) + rows := []string{} + keys := []string{} + + for key := range m { + keys = append(keys, key) + } + + sort.Strings(keys) + + for _, key := range keys { + val := m[key] + k := fmt.Sprintf(`"%s"`, key) + v := format(val, depth+1) + + valueIndent := " " + if Newline == "" { + valueIndent = "" + } + row := fmt.Sprintf("%s%s:%s%s", nextIndent, k, valueIndent, v) + rows = append(rows, row) + } + + return fmt.Sprintf("{%s%s%s%s}", Newline, strings.Join(rows, ","+Newline), Newline, currentIndent) +} + +func formatArray(a []interface{}, depth int) string { + if len(a) == 0 { + return "[]" + } + + currentIndent := generateIndent(depth - 1) + nextIndent := generateIndent(depth) + rows := []string{} + + for _, val := range a { + c := format(val, depth+1) + row := nextIndent + c + rows = append(rows, row) + } + return fmt.Sprintf("[%s%s%s%s]", Newline, strings.Join(rows, ","+Newline), Newline, currentIndent) +} + +func generateIndent(depth int) string { + return strings.Repeat(" ", Indent*depth) +}