Add Jsonp Support to Context (#1333)
This commit is contained in:
parent
41f951e0cd
commit
8c24018290
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)
|
- [Bind HTML checkboxes](#bind-html-checkboxes)
|
||||||
- [Multipart/Urlencoded binding](#multiparturlencoded-binding)
|
- [Multipart/Urlencoded binding](#multiparturlencoded-binding)
|
||||||
- [XML, JSON and YAML rendering](#xml-json-and-yaml-rendering)
|
- [XML, JSON and YAML rendering](#xml-json-and-yaml-rendering)
|
||||||
|
- [JSONP rendering](#jsonp)
|
||||||
- [Serving static files](#serving-static-files)
|
- [Serving static files](#serving-static-files)
|
||||||
- [HTML rendering](#html-rendering)
|
- [HTML rendering](#html-rendering)
|
||||||
- [Multitemplate](#multitemplate)
|
- [Multitemplate](#multitemplate)
|
||||||
@ -861,6 +862,28 @@ func main() {
|
|||||||
r.Run(":8080")
|
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
|
### 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})
|
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.
|
// JSON serializes the given struct as JSON into the response body.
|
||||||
// It also sets the Content-Type as "application/json".
|
// It also sets the Content-Type as "application/json".
|
||||||
func (c *Context) JSON(code int, obj interface{}) {
|
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"))
|
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
|
// Tests that no JSON is rendered if code is 204
|
||||||
func TestContextRenderNoContentJSON(t *testing.T) {
|
func TestContextRenderNoContentJSON(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
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 (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin/json"
|
"github.com/gin-gonic/gin/json"
|
||||||
@ -24,9 +25,15 @@ type SecureJSON struct {
|
|||||||
Data interface{}
|
Data interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type JsonpJSON struct {
|
||||||
|
Callback string
|
||||||
|
Data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
type SecureJSONPrefix string
|
type SecureJSONPrefix string
|
||||||
|
|
||||||
var jsonContentType = []string{"application/json; charset=utf-8"}
|
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) {
|
func (r JSON) Render(w http.ResponseWriter) (err error) {
|
||||||
if err = WriteJSON(w, r.Data); err != nil {
|
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) {
|
func (r SecureJSON) WriteContentType(w http.ResponseWriter) {
|
||||||
writeContentType(w, jsonContentType)
|
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 = JSON{}
|
||||||
_ Render = IndentedJSON{}
|
_ Render = IndentedJSON{}
|
||||||
_ Render = SecureJSON{}
|
_ Render = SecureJSON{}
|
||||||
|
_ Render = JsonpJSON{}
|
||||||
_ Render = XML{}
|
_ Render = XML{}
|
||||||
_ Render = String{}
|
_ Render = String{}
|
||||||
_ Render = Redirect{}
|
_ 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)
|
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{}
|
type xmlmap map[string]interface{}
|
||||||
|
|
||||||
// Allows type H to be used with xml.Marshal
|
// Allows type H to be used with xml.Marshal
|
||||||
|
Loading…
Reference in New Issue
Block a user