Add Jsonp Support to Context (#1333)
This commit is contained in:
		
							
								
								
									
										23
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								README.md
									
									
									
									
									
								
							| @ -38,6 +38,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi | ||||
|     - [Bind HTML checkboxes](#bind-html-checkboxes) | ||||
|     - [Multipart/Urlencoded binding](#multiparturlencoded-binding) | ||||
|     - [XML, JSON and YAML rendering](#xml-json-and-yaml-rendering) | ||||
|     - [JSONP rendering](#jsonp) | ||||
|     - [Serving static files](#serving-static-files) | ||||
|     - [HTML rendering](#html-rendering) | ||||
|     - [Multitemplate](#multitemplate) | ||||
| @ -861,6 +862,28 @@ func main() { | ||||
| 	r.Run(":8080") | ||||
| } | ||||
| ``` | ||||
| #### JSONP | ||||
|  | ||||
| Using JSONP to request data from a server  in a different domain. Add callback to response body if the query parameter callback exists. | ||||
|  | ||||
| ```go | ||||
| func main() { | ||||
| 	r := gin.Default() | ||||
|  | ||||
| 	r.GET("/JSONP?callback=x", func(c *gin.Context) { | ||||
| 		data := map[string]interface{}{ | ||||
| 			"foo": "bar", | ||||
| 		} | ||||
| 		 | ||||
| 		//callback is x | ||||
| 		// Will output  :   x({\"foo\":\"bar\"}) | ||||
| 		c.JSONP(http.StatusOK, data) | ||||
| 	}) | ||||
|  | ||||
| 	// Listen and serve on 0.0.0.0:8080 | ||||
| 	r.Run(":8080") | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ### Serving static files | ||||
|  | ||||
|  | ||||
							
								
								
									
										7
									
								
								context.go
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										7
									
								
								context.go
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @ -670,6 +670,13 @@ func (c *Context) SecureJSON(code int, obj interface{}) { | ||||
| 	c.Render(code, render.SecureJSON{Prefix: c.engine.secureJsonPrefix, Data: obj}) | ||||
| } | ||||
|  | ||||
| // JSONP serializes the given struct as JSON into the response body. | ||||
| // It add padding to response body to request data from a server residing in a different domain than the client. | ||||
| // It also sets the Content-Type as "application/javascript". | ||||
| func (c *Context) JSONP(code int, obj interface{}) { | ||||
| 	c.Render(code, render.JsonpJSON{Callback: c.DefaultQuery("callback", ""), Data: obj}) | ||||
| } | ||||
|  | ||||
| // JSON serializes the given struct as JSON into the response body. | ||||
| // It also sets the Content-Type as "application/json". | ||||
| func (c *Context) JSON(code int, obj interface{}) { | ||||
|  | ||||
| @ -581,6 +581,20 @@ func TestContextRenderJSON(t *testing.T) { | ||||
| 	assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type")) | ||||
| } | ||||
|  | ||||
| // Tests that the response is serialized as JSONP | ||||
| // and Content-Type is set to application/javascript | ||||
| func TestContextRenderJSONP(t *testing.T) { | ||||
| 	w := httptest.NewRecorder() | ||||
| 	c, _ := CreateTestContext(w) | ||||
| 	c.Request, _ = http.NewRequest("GET", "http://example.com/?callback=x", nil) | ||||
|  | ||||
| 	c.JSONP(201, H{"foo": "bar"}) | ||||
|  | ||||
| 	assert.Equal(t, 201, w.Code) | ||||
| 	assert.Equal(t, "x({\"foo\":\"bar\"})", w.Body.String()) | ||||
| 	assert.Equal(t, "application/javascript; charset=utf-8", w.HeaderMap.Get("Content-Type")) | ||||
| } | ||||
|  | ||||
| // Tests that no JSON is rendered if code is 204 | ||||
| func TestContextRenderNoContentJSON(t *testing.T) { | ||||
| 	w := httptest.NewRecorder() | ||||
|  | ||||
							
								
								
									
										32
									
								
								render/json.go
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										32
									
								
								render/json.go
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @ -6,6 +6,7 @@ package render | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"html/template" | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/gin-gonic/gin/json" | ||||
| @ -24,9 +25,15 @@ type SecureJSON struct { | ||||
| 	Data   interface{} | ||||
| } | ||||
|  | ||||
| type JsonpJSON struct { | ||||
| 	Callback string | ||||
| 	Data     interface{} | ||||
| } | ||||
|  | ||||
| type SecureJSONPrefix string | ||||
|  | ||||
| var jsonContentType = []string{"application/json; charset=utf-8"} | ||||
| var jsonpContentType = []string{"application/javascript; charset=utf-8"} | ||||
|  | ||||
| func (r JSON) Render(w http.ResponseWriter) (err error) { | ||||
| 	if err = WriteJSON(w, r.Data); err != nil { | ||||
| @ -80,3 +87,28 @@ func (r SecureJSON) Render(w http.ResponseWriter) error { | ||||
| func (r SecureJSON) WriteContentType(w http.ResponseWriter) { | ||||
| 	writeContentType(w, jsonContentType) | ||||
| } | ||||
|  | ||||
| func (r JsonpJSON) Render(w http.ResponseWriter) (err error) { | ||||
| 	r.WriteContentType(w) | ||||
| 	ret, err := json.Marshal(r.Data) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if r.Callback == "" { | ||||
| 		w.Write(ret) | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	callback := template.JSEscapeString(r.Callback) | ||||
| 	w.Write([]byte(callback)) | ||||
| 	w.Write([]byte("(")) | ||||
| 	w.Write(ret) | ||||
| 	w.Write([]byte(")")) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (r JsonpJSON) WriteContentType(w http.ResponseWriter) { | ||||
| 	writeContentType(w, jsonpContentType) | ||||
| } | ||||
|  | ||||
							
								
								
									
										1
									
								
								render/render.go
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										1
									
								
								render/render.go
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @ -15,6 +15,7 @@ var ( | ||||
| 	_ Render     = JSON{} | ||||
| 	_ Render     = IndentedJSON{} | ||||
| 	_ Render     = SecureJSON{} | ||||
| 	_ Render     = JsonpJSON{} | ||||
| 	_ Render     = XML{} | ||||
| 	_ Render     = String{} | ||||
| 	_ Render     = Redirect{} | ||||
|  | ||||
							
								
								
									
										37
									
								
								render/render_test.go
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										37
									
								
								render/render_test.go
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @ -128,6 +128,43 @@ func TestRenderSecureJSONFail(t *testing.T) { | ||||
| 	assert.Error(t, err) | ||||
| } | ||||
|  | ||||
| func TestRenderJsonpJSON(t *testing.T) { | ||||
| 	w1 := httptest.NewRecorder() | ||||
| 	data := map[string]interface{}{ | ||||
| 		"foo": "bar", | ||||
| 	} | ||||
|  | ||||
| 	(JsonpJSON{"x", data}).WriteContentType(w1) | ||||
| 	assert.Equal(t, "application/javascript; charset=utf-8", w1.Header().Get("Content-Type")) | ||||
|  | ||||
| 	err1 := (JsonpJSON{"x", data}).Render(w1) | ||||
|  | ||||
| 	assert.NoError(t, err1) | ||||
| 	assert.Equal(t, "x({\"foo\":\"bar\"})", w1.Body.String()) | ||||
| 	assert.Equal(t, "application/javascript; charset=utf-8", w1.Header().Get("Content-Type")) | ||||
|  | ||||
| 	w2 := httptest.NewRecorder() | ||||
| 	datas := []map[string]interface{}{{ | ||||
| 		"foo": "bar", | ||||
| 	}, { | ||||
| 		"bar": "foo", | ||||
| 	}} | ||||
|  | ||||
| 	err2 := (JsonpJSON{"x", datas}).Render(w2) | ||||
| 	assert.NoError(t, err2) | ||||
| 	assert.Equal(t, "x([{\"foo\":\"bar\"},{\"bar\":\"foo\"}])", w2.Body.String()) | ||||
| 	assert.Equal(t, "application/javascript; charset=utf-8", w2.Header().Get("Content-Type")) | ||||
| } | ||||
|  | ||||
| func TestRenderJsonpJSONFail(t *testing.T) { | ||||
| 	w := httptest.NewRecorder() | ||||
| 	data := make(chan int) | ||||
|  | ||||
| 	// json: unsupported type: chan int | ||||
| 	err := (JsonpJSON{"x", data}).Render(w) | ||||
| 	assert.Error(t, err) | ||||
| } | ||||
|  | ||||
| type xmlmap map[string]interface{} | ||||
|  | ||||
| // Allows type H to be used with xml.Marshal | ||||
|  | ||||
		Reference in New Issue
	
	Block a user