From c6110f970ca5af365283bd4083b8a105e974e92c Mon Sep 17 00:00:00 2001 From: Filip Figiel Date: Mon, 20 Aug 2018 09:15:31 +0200 Subject: [PATCH] Add PureJSON renderer (#694) Closes #693 --- README.md | 28 ++++++++++++++++++++++++++++ context_17.go | 17 +++++++++++++++++ context_17_test.go | 27 +++++++++++++++++++++++++++ context_test.go | 5 +++-- json/json.go | 1 + render/json_17.go | 28 ++++++++++++++++++++++++++++ render/render_17_test.go | 26 ++++++++++++++++++++++++++ render/render_test.go | 5 +++-- 8 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 context_17.go create mode 100644 context_17_test.go create mode 100644 render/json_17.go create mode 100644 render/render_17_test.go diff --git a/README.md b/README.md index ae183f92c8..053750bb5b 100644 --- a/README.md +++ b/README.md @@ -991,6 +991,34 @@ func main() { } ``` +#### PureJSON + +Normally, JSON replaces special HTML characters with their unicode entities, e.g. `<` becomes `\u003c`. If you want to encode such characters literally, you can use PureJSON instead. +This feature is unavailable in Go 1.6 and lower. + +```go +func main() { + r := gin.Default() + + // Serves unicode entities + r.GET("/json", func(c *gin.Context) { + c.JSON(200, gin.H{ + "html": "Hello, world!", + }) + }) + + // Serves literal characters + r.GET("/purejson", func(c *gin.Context) { + c.PureJSON(200, gin.H{ + "html": "Hello, world!", + }) + }) + + // listen and serve on 0.0.0.0:8080 + r.Run(":8080) +} +``` + ### Serving static files ```go diff --git a/context_17.go b/context_17.go new file mode 100644 index 0000000000..8e9f75ad7b --- /dev/null +++ b/context_17.go @@ -0,0 +1,17 @@ +// Copyright 2018 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +// +build go1.7 + +package gin + +import ( + "github.com/gin-gonic/gin/render" +) + +// PureJSON serializes the given struct as JSON into the response body. +// PureJSON, unlike JSON, does not replace special html characters with their unicode entities. +func (c *Context) PureJSON(code int, obj interface{}) { + c.Render(code, render.PureJSON{Data: obj}) +} diff --git a/context_17_test.go b/context_17_test.go new file mode 100644 index 0000000000..d2251904f2 --- /dev/null +++ b/context_17_test.go @@ -0,0 +1,27 @@ +// Copyright 2018 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +// +build go1.7 + +package gin + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +// Tests that the response is serialized as JSON +// and Content-Type is set to application/json +// and special HTML characters are preserved +func TestContextRenderPureJSON(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + c.PureJSON(http.StatusCreated, H{"foo": "bar", "html": ""}) + assert.Equal(t, http.StatusCreated, w.Code) + assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\"}\n", w.Body.String()) + assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type")) +} diff --git a/context_test.go b/context_test.go index 675c2a4911..782f7bed59 100644 --- a/context_test.go +++ b/context_test.go @@ -619,14 +619,15 @@ func TestContextRenderPanicIfErr(t *testing.T) { // Tests that the response is serialized as JSON // and Content-Type is set to application/json +// and special HTML characters are escaped func TestContextRenderJSON(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.JSON(http.StatusCreated, H{"foo": "bar"}) + c.JSON(http.StatusCreated, H{"foo": "bar", "html": ""}) assert.Equal(t, http.StatusCreated, w.Code) - assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String()) + assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String()) assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type")) } diff --git a/json/json.go b/json/json.go index aa76aa3098..4f643c5617 100644 --- a/json/json.go +++ b/json/json.go @@ -12,4 +12,5 @@ var ( Marshal = json.Marshal MarshalIndent = json.MarshalIndent NewDecoder = json.NewDecoder + NewEncoder = json.NewEncoder ) diff --git a/render/json_17.go b/render/json_17.go new file mode 100644 index 0000000000..df0dc1458f --- /dev/null +++ b/render/json_17.go @@ -0,0 +1,28 @@ +// Copyright 2018 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +// +build go1.7 + +package render + +import ( + "net/http" + + "github.com/gin-gonic/gin/json" +) + +type PureJSON struct { + Data interface{} +} + +func (r PureJSON) Render(w http.ResponseWriter) error { + r.WriteContentType(w) + encoder := json.NewEncoder(w) + encoder.SetEscapeHTML(false) + return encoder.Encode(r.Data) +} + +func (r PureJSON) WriteContentType(w http.ResponseWriter) { + writeContentType(w, jsonContentType) +} diff --git a/render/render_17_test.go b/render/render_17_test.go new file mode 100644 index 0000000000..68330090f6 --- /dev/null +++ b/render/render_17_test.go @@ -0,0 +1,26 @@ +// Copyright 2018 Gin Core Team. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +// +build go1.7 + +package render + +import ( + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestRenderPureJSON(t *testing.T) { + w := httptest.NewRecorder() + data := map[string]interface{}{ + "foo": "bar", + "html": "", + } + err := (PureJSON{data}).Render(w) + assert.NoError(t, err) + assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\"}\n", w.Body.String()) + assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) +} diff --git a/render/render_test.go b/render/render_test.go index cd20d01d37..fe9228e985 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -52,7 +52,8 @@ func TestRenderMsgPack(t *testing.T) { func TestRenderJSON(t *testing.T) { w := httptest.NewRecorder() data := map[string]interface{}{ - "foo": "bar", + "foo": "bar", + "html": "", } (JSON{data}).WriteContentType(w) @@ -61,7 +62,7 @@ func TestRenderJSON(t *testing.T) { err := (JSON{data}).Render(w) assert.NoError(t, err) - assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String()) + assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String()) assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) }