Skip to content

Commit

Permalink
add json ASCII string render (#1358)
Browse files Browse the repository at this point in the history
add a json render that rendering json as ASCII string
  • Loading branch information
duguying authored and appleboy committed Jul 3, 2018
1 parent d17a125 commit 85221af
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 0 deletions.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,29 @@ func main() {
}
```

#### AsciiJSON

Using AsciiJSON to Generates ASCII-only JSON with escaped non-ASCII chracters.

```go
func main() {
r := gin.Default()

r.GET("/someJSON", func(c *gin.Context) {
data := map[string]interface{}{
"lang": "GO语言",
"tag": "<br>",
}

// will output : {"lang":"GO\u8bed\u8a00","tag":"\u003cbr\u003e"}
c.AsciiJSON(http.StatusOK, data)
})

// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
```

### Serving static files

```go
Expand Down
6 changes: 6 additions & 0 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,12 @@ func (c *Context) JSON(code int, obj interface{}) {
c.Render(code, render.JSON{Data: obj})
}

// AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string.
// It also sets the Content-Type as "application/json".
func (c *Context) AsciiJSON(code int, obj interface{}) {
c.Render(code, render.AsciiJSON{Data: obj})
}

// XML serializes the given struct as XML into the response body.
// It also sets the Content-Type as "application/xml".
func (c *Context) XML(code int, obj interface{}) {
Expand Down
11 changes: 11 additions & 0 deletions context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,17 @@ func TestContextRenderNoContentSecureJSON(t *testing.T) {
assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type"))
}

func TestContextRenderNoContentAsciiJSON(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)

c.AsciiJSON(http.StatusNoContent, []string{"lang", "Go语言"})

assert.Equal(t, http.StatusNoContent, w.Code)
assert.Empty(t, w.Body.String())
assert.Equal(t, "application/json", w.HeaderMap.Get("Content-Type"))
}

// Tests that the response executes the templates
// and responds with Content-Type set to text/html
func TestContextRenderHTML(t *testing.T) {
Expand Down
32 changes: 32 additions & 0 deletions render/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package render

import (
"bytes"
"fmt"
"html/template"
"net/http"

Expand All @@ -30,10 +31,15 @@ type JsonpJSON struct {
Data interface{}
}

type AsciiJSON struct {
Data interface{}
}

type SecureJSONPrefix string

var jsonContentType = []string{"application/json; charset=utf-8"}
var jsonpContentType = []string{"application/javascript; charset=utf-8"}
var jsonAsciiContentType = []string{"application/json"}

func (r JSON) Render(w http.ResponseWriter) (err error) {
if err = WriteJSON(w, r.Data); err != nil {
Expand Down Expand Up @@ -112,3 +118,29 @@ func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
func (r JsonpJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonpContentType)
}

func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
r.WriteContentType(w)
ret, err := json.Marshal(r.Data)
if err != nil {
return err
}

var buffer bytes.Buffer
for _, r := range string(ret) {
cvt := ""
if r < 128 {
cvt = string(r)
} else {
cvt = fmt.Sprintf("\\u%04x", int64(r))
}
buffer.WriteString(cvt)
}

w.Write(buffer.Bytes())
return nil
}

func (r AsciiJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonAsciiContentType)
}
29 changes: 29 additions & 0 deletions render/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,35 @@ func TestRenderJsonpJSONFail(t *testing.T) {
assert.Error(t, err)
}

func TestRenderAsciiJSON(t *testing.T) {
w1 := httptest.NewRecorder()
data1 := map[string]interface{}{
"lang": "GO语言",
"tag": "<br>",
}

err := (AsciiJSON{data1}).Render(w1)

assert.NoError(t, err)
assert.Equal(t, "{\"lang\":\"GO\\u8bed\\u8a00\",\"tag\":\"\\u003cbr\\u003e\"}", w1.Body.String())
assert.Equal(t, "application/json", w1.Header().Get("Content-Type"))

w2 := httptest.NewRecorder()
data2 := float64(3.1415926)

err = (AsciiJSON{data2}).Render(w2)
assert.NoError(t, err)
assert.Equal(t, "3.1415926", w2.Body.String())
}

func TestRenderAsciiJSONFail(t *testing.T) {
w := httptest.NewRecorder()
data := make(chan int)

// json: unsupported type: chan int
assert.Error(t, (AsciiJSON{data}).Render(w))
}

type xmlmap map[string]interface{}

// Allows type H to be used with xml.Marshal
Expand Down

0 comments on commit 85221af

Please sign in to comment.