add a method to set the indentJsonIndentString used in Context.IndentedJSON

This commit is contained in:
bestgopher 2020-05-01 18:17:29 +08:00
parent 4427ca4a60
commit 2fc888a61d
5 changed files with 63 additions and 14 deletions

View File

@ -859,7 +859,7 @@ func (c *Context) HTML(code int, name string, obj interface{}) {
// WARNING: we recommend to use this only for development purposes since printing pretty JSON is // WARNING: we recommend to use this only for development purposes since printing pretty JSON is
// more CPU and bandwidth consuming. Use Context.JSON() instead. // more CPU and bandwidth consuming. Use Context.JSON() instead.
func (c *Context) IndentedJSON(code int, obj interface{}) { func (c *Context) IndentedJSON(code int, obj interface{}) {
c.Render(code, render.IndentedJSON{Data: obj}) c.Render(code, render.IndentedJSON{IndentString: c.engine.indentJsonIndentString, Data: obj})
} }
// SecureJSON serializes the given struct as Secure JSON into the response body. // SecureJSON serializes the given struct as Secure JSON into the response body.

13
gin.go
View File

@ -11,6 +11,7 @@ import (
"net/http" "net/http"
"os" "os"
"path" "path"
"strings"
"sync" "sync"
"github.com/gin-gonic/gin/internal/bytesconv" "github.com/gin-gonic/gin/internal/bytesconv"
@ -19,6 +20,9 @@ import (
const defaultMultipartMemory = 32 << 20 // 32 MB const defaultMultipartMemory = 32 << 20 // 32 MB
// A space string
var spaceString = string(32)
var ( var (
default404Body = []byte("404 page not found") default404Body = []byte("404 page not found")
default405Body = []byte("405 method not allowed") default405Body = []byte("405 method not allowed")
@ -112,6 +116,7 @@ type Engine struct {
noMethod HandlersChain noMethod HandlersChain
pool sync.Pool pool sync.Pool
trees methodTrees trees methodTrees
indentJsonIndentString string
} }
var _ IRouter = &Engine{} var _ IRouter = &Engine{}
@ -145,6 +150,7 @@ func New() *Engine {
trees: make(methodTrees, 0, 9), trees: make(methodTrees, 0, 9),
delims: render.Delims{Left: "{{", Right: "}}"}, delims: render.Delims{Left: "{{", Right: "}}"},
secureJsonPrefix: "while(1);", secureJsonPrefix: "while(1);",
indentJsonIndentString: strings.Repeat(spaceString, 4),
} }
engine.RouterGroup.engine = engine engine.RouterGroup.engine = engine
engine.pool.New = func() interface{} { engine.pool.New = func() interface{} {
@ -177,6 +183,13 @@ func (engine *Engine) SecureJsonPrefix(prefix string) *Engine {
return engine return engine
} }
// IndentJsonIndentSpaceNum sets the indentJsonIndentString used in Context.IndentedJSON.
// When we use Context.IndentedJSON, we can use custom indentation to render the response.
func (engine *Engine) IndentJsonIndentSpaceNum(spaceNum int) *Engine {
engine.indentJsonIndentString = strings.Repeat(spaceString, spaceNum)
return engine
}
// LoadHTMLGlob loads HTML files identified by glob pattern // LoadHTMLGlob loads HTML files identified by glob pattern
// and associates the result with HTML renderer. // and associates the result with HTML renderer.
func (engine *Engine) LoadHTMLGlob(pattern string) { func (engine *Engine) LoadHTMLGlob(pattern string) {

View File

@ -6,13 +6,16 @@ package gin
import ( import (
"crypto/tls" "crypto/tls"
"encoding/json"
"fmt" "fmt"
"html/template" "html/template"
"io/ioutil" "io/ioutil"
"math/rand"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"reflect" "reflect"
"strconv" "strconv"
"strings"
"sync/atomic" "sync/atomic"
"testing" "testing"
"time" "time"
@ -544,3 +547,35 @@ func assertRoutePresent(t *testing.T, gotRoutes RoutesInfo, wantRoute RouteInfo)
func handlerTest1(c *Context) {} func handlerTest1(c *Context) {}
func handlerTest2(c *Context) {} func handlerTest2(c *Context) {}
// Engine.IndentJsonIndentSpaceNum
func TestEngine_IndentJsonIndentSpaceNum(t *testing.T) {
rand.Seed(time.Now().UnixNano())
spaceNum := rand.Intn(10)
router := Default()
defaultResponse := H{"name": "test", "age": 20}
router.IndentJsonIndentSpaceNum(spaceNum)
router.GET("/test", func(c *Context) {
c.IndentedJSON(200, defaultResponse)
})
s := httptest.NewServer(router)
defer s.Close()
req, err := http.NewRequest("GET", "/test", nil)
if err != nil {
t.Fatal(err)
}
rr := httptest.NewRecorder()
router.ServeHTTP(rr, req)
if rr.Code != http.StatusOK {
t.Fatal("error code: ", rr.Code)
}
except, _ := json.MarshalIndent(defaultResponse, "", strings.Repeat(spaceString, spaceNum))
assert.Equal(t, rr.Body.Bytes(), except)
t.Log(rr.Body.String())
}

View File

@ -21,6 +21,7 @@ type JSON struct {
// IndentedJSON contains the given interface object. // IndentedJSON contains the given interface object.
type IndentedJSON struct { type IndentedJSON struct {
IndentString string
Data interface{} Data interface{}
} }
@ -80,7 +81,7 @@ func WriteJSON(w http.ResponseWriter, obj interface{}) error {
// Render (IndentedJSON) marshals the given interface object and writes it with custom ContentType. // Render (IndentedJSON) marshals the given interface object and writes it with custom ContentType.
func (r IndentedJSON) Render(w http.ResponseWriter) error { func (r IndentedJSON) Render(w http.ResponseWriter) error {
r.WriteContentType(w) r.WriteContentType(w)
jsonBytes, err := json.MarshalIndent(r.Data, "", " ") jsonBytes, err := json.MarshalIndent(r.Data, "", r.IndentString)
if err != nil { if err != nil {
return err return err
} }

View File

@ -55,7 +55,7 @@ func TestRenderIndentedJSON(t *testing.T) {
"bar": "foo", "bar": "foo",
} }
err := (IndentedJSON{data}).Render(w) err := (IndentedJSON{" ", data}).Render(w)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "{\n \"bar\": \"foo\",\n \"foo\": \"bar\"\n}", w.Body.String()) assert.Equal(t, "{\n \"bar\": \"foo\",\n \"foo\": \"bar\"\n}", w.Body.String())
@ -67,7 +67,7 @@ func TestRenderIndentedJSONPanics(t *testing.T) {
data := make(chan int) data := make(chan int)
// json: unsupported type: chan int // json: unsupported type: chan int
err := (IndentedJSON{data}).Render(w) err := (IndentedJSON{"", data}).Render(w)
assert.Error(t, err) assert.Error(t, err)
} }