Merge e7e1996f4689d3d2f8d361d97e1ca175984be15b into 82b284fd774d6bf68c934a3f335b816c61edf66f

This commit is contained in:
bestgopher 2020-05-08 13:51:56 +08:00 committed by GitHub
commit b5646bd60b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 64 additions and 15 deletions

View File

@ -858,7 +858,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.

33
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 = " "
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")
@ -103,16 +107,17 @@ type Engine struct {
// See the PR #1817 and issue #1644 // See the PR #1817 and issue #1644
RemoveExtraSlash bool RemoveExtraSlash bool
delims render.Delims delims render.Delims
secureJSONPrefix string secureJSONPrefix string
HTMLRender render.HTMLRender HTMLRender render.HTMLRender
FuncMap template.FuncMap FuncMap template.FuncMap
allNoRoute HandlersChain allNoRoute HandlersChain
allNoMethod HandlersChain allNoMethod HandlersChain
noRoute HandlersChain noRoute HandlersChain
noMethod HandlersChain noMethod HandlersChain
pool sync.Pool pool sync.Pool
trees methodTrees trees methodTrees
indentJsonIndentString string
} }
var _ IRouter = &Engine{} var _ IRouter = &Engine{}
@ -146,6 +151,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{} {
@ -178,6 +184,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,7 +21,8 @@ type JSON struct {
// IndentedJSON contains the given interface object. // IndentedJSON contains the given interface object.
type IndentedJSON struct { type IndentedJSON struct {
Data interface{} IndentString string
Data interface{}
} }
// SecureJSON contains the given interface object and its prefix. // SecureJSON contains the given interface object and its prefix.
@ -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)
} }