Просмотр исходного кода

add json ASCII string render (#1358)

add a json render that rendering json as ASCII string
Rex Lee(李俊) 7 лет назад
Родитель
Сommit
85221af84c
5 измененных файлов с 101 добавлено и 0 удалено
  1. 23 0
      README.md
  2. 6 0
      context.go
  3. 11 0
      context_test.go
  4. 32 0
      render/json.go
  5. 29 0
      render/render_test.go

+ 23 - 0
README.md

@@ -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

+ 6 - 0
context.go

@@ -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{}) {

+ 11 - 0
context_test.go

@@ -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) {

+ 32 - 0
render/json.go

@@ -6,6 +6,7 @@ package render
 
 import (
 	"bytes"
+	"fmt"
 	"html/template"
 	"net/http"
 
@@ -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 {
@@ -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 - 0
render/render_test.go

@@ -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