render: add sync.Pool to manager JSON,XML,YAML,ProtoBuf,MsgPack Render object

This commit is contained in:
Michael Li 2018-12-29 23:51:47 +08:00
parent 85b92cdf4b
commit f7f798f408
13 changed files with 546 additions and 81 deletions

2
.gitignore vendored
View File

@ -5,3 +5,5 @@ count.out
test
profile.out
tmp.out
.idea/
*.iml

View File

@ -757,6 +757,14 @@ func (c *Context) Render(code int, r render.Render) {
}
}
// RenderWith writes the response headers and calls render.Render to render data and opts
func (c *Context) RenderWith(name int, code int, data interface{}, opts ...interface{}) {
r := render.Default(name)
r.Setup(data, opts...)
c.Render(code, r)
render.Recycle(name, r)
}
// HTML renders the HTTP template specified by its file name.
// It also updates the HTTP code and sets the Content-Type as "text/html".
// See http://golang.org/doc/articles/wiki/
@ -770,14 +778,14 @@ func (c *Context) HTML(code int, name string, obj interface{}) {
// WARNING: we recommend to use this only for development purposes since printing pretty JSON is
// more CPU and bandwidth consuming. Use Context.JSON() instead.
func (c *Context) IndentedJSON(code int, obj interface{}) {
c.Render(code, render.IndentedJSON{Data: obj})
c.RenderWith(render.IntendedJSONRenderType, code, obj)
}
// SecureJSON serializes the given struct as Secure JSON into the response body.
// Default prepends "while(1)," to response body if the given struct is array values.
// It also sets the Content-Type as "application/json".
func (c *Context) SecureJSON(code int, obj interface{}) {
c.Render(code, render.SecureJSON{Prefix: c.engine.secureJsonPrefix, Data: obj})
c.RenderWith(render.SecureJSONRenderType, code, obj, c.engine.secureJsonPrefix)
}
// JSONP serializes the given struct as JSON into the response body.
@ -785,39 +793,44 @@ func (c *Context) SecureJSON(code int, obj interface{}) {
// It also sets the Content-Type as "application/javascript".
func (c *Context) JSONP(code int, obj interface{}) {
callback := c.DefaultQuery("callback", "")
if callback == "" {
c.Render(code, render.JSON{Data: obj})
return
if callback != "" {
c.RenderWith(render.JsonpJSONRenderType, code, obj, callback)
} else {
c.RenderWith(render.JSONRenderType, code, obj)
}
c.Render(code, render.JsonpJSON{Callback: 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{}) {
c.Render(code, render.JSON{Data: obj})
c.RenderWith(render.JSONRenderType, code, 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})
c.RenderWith(render.AsciiJSONRenderType, code, 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{}) {
c.Render(code, render.XML{Data: obj})
c.RenderWith(render.XMLRenderType, code, obj)
}
// YAML serializes the given struct as YAML into the response body.
func (c *Context) YAML(code int, obj interface{}) {
c.Render(code, render.YAML{Data: obj})
c.RenderWith(render.YAMLRenderType, code, obj)
}
// ProtoBuf serializes the given struct as ProtoBuf into the response body.
func (c *Context) ProtoBuf(code int, obj interface{}) {
c.Render(code, render.ProtoBuf{Data: obj})
c.RenderWith(render.ProtoBufRenderType, code, obj)
}
// MsgPack serializes the given struct as ProtoBuf into the response body.
func (c *Context) MsgPack(code int, obj interface{}) {
c.RenderWith(render.MsgPackRenderType, code, obj)
}
// String writes the given string into the response body.

View File

@ -15,7 +15,7 @@ import (
// 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})
c.RenderWith(render.PureJSONRenderType, code, obj)
}
/************************************/

39
render/empty.go Normal file
View File

@ -0,0 +1,39 @@
package render
import (
"fmt"
"net/http"
)
type EmptyRenderFactory struct{}
type EmptyRender struct{}
func init() {
Register(EmptyRenderType, EmptyRenderFactory{})
}
// Instance apply opts to build a new EmptyRender instance
func (EmptyRenderFactory) Instance() RenderRecycler {
return &EmptyRender{}
}
// Render writes data with custom ContentType.
func (*EmptyRender) Render(http.ResponseWriter) error {
return fmt.Errorf("empty render,you need register one first")
}
// WriteContentType writes custom ContentType.
func (*EmptyRender) WriteContentType(w http.ResponseWriter) {
// Empty
}
// Setup set data and opts
func (*EmptyRender) Setup(data interface{}, opts ...interface{}) {
// Empty
}
// Reset clean data and opts
func (*EmptyRender) Reset() {
// Empty
}

View File

@ -13,6 +13,14 @@ import (
"github.com/gin-gonic/gin/internal/json"
)
func init() {
Register(JSONRenderType, JSONFactory{})
Register(IntendedJSONRenderType, IndentedJSONFactory{})
Register(JsonpJSONRenderType, JsonpJSONFactory{})
Register(SecureJSONRenderType, SecureJSONFactory{})
Register(AsciiJSONRenderType, AsciiJSONFactory{})
}
// JSON contains the given interface object.
type JSON struct {
Data interface{}
@ -40,6 +48,21 @@ type AsciiJSON struct {
Data interface{}
}
// JSONFactory instance the JSON object.
type JSONFactory struct{}
// IndentedJSONFactory instance the IndentedJSON object.
type IndentedJSONFactory struct{}
// SecureJSONFactory instance the SecureJSON object.
type SecureJSONFactory struct{}
// JsonpJSONFactory instance the JsonpJSON object.
type JsonpJSONFactory struct{}
// AsciiJSONFactory instance the AsciiJSON object.
type AsciiJSONFactory struct{}
// SecureJSONPrefix is a string which represents SecureJSON prefix.
type SecureJSONPrefix string
@ -47,8 +70,18 @@ var jsonContentType = []string{"application/json; charset=utf-8"}
var jsonpContentType = []string{"application/javascript; charset=utf-8"}
var jsonAsciiContentType = []string{"application/json"}
// Setup set data and opts
func (r *JSON) Setup(data interface{}, opts ...interface{}) {
r.Data = data
}
// Reset clean data and opts
func (r *JSON) Reset() {
r.Data = nil
}
// Render (JSON) writes data with custom ContentType.
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 {
panic(err)
}
@ -56,7 +89,7 @@ func (r JSON) Render(w http.ResponseWriter) (err error) {
}
// WriteContentType (JSON) writes JSON ContentType.
func (r JSON) WriteContentType(w http.ResponseWriter) {
func (r *JSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonContentType)
}
@ -71,8 +104,18 @@ func WriteJSON(w http.ResponseWriter, obj interface{}) error {
return nil
}
// Setup set data and opts
func (r *IndentedJSON) Setup(data interface{}, opts ...interface{}) {
r.Data = data
}
// Reset clean data and opts
func (r *IndentedJSON) Reset() {
r.Data = nil
}
// Render (IndentedJSON) marshals the given interface object and writes it with custom ContentType.
func (r IndentedJSON) Render(w http.ResponseWriter) error {
func (r *IndentedJSON) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
jsonBytes, err := json.MarshalIndent(r.Data, "", " ")
if err != nil {
@ -83,12 +126,26 @@ func (r IndentedJSON) Render(w http.ResponseWriter) error {
}
// WriteContentType (IndentedJSON) writes JSON ContentType.
func (r IndentedJSON) WriteContentType(w http.ResponseWriter) {
func (r *IndentedJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonContentType)
}
// Setup set data and opts
func (r *SecureJSON) Setup(data interface{}, opts ...interface{}) {
r.Data = data
if len(opts) == 1 {
r.Prefix, _ = opts[0].(string)
}
}
// Reset clean data and opts
func (r *SecureJSON) Reset() {
r.Data = nil
r.Prefix = ""
}
// Render (SecureJSON) marshals the given interface object and writes it with custom ContentType.
func (r SecureJSON) Render(w http.ResponseWriter) error {
func (r *SecureJSON) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
jsonBytes, err := json.Marshal(r.Data)
if err != nil {
@ -103,12 +160,30 @@ func (r SecureJSON) Render(w http.ResponseWriter) error {
}
// WriteContentType (SecureJSON) writes JSON ContentType.
func (r SecureJSON) WriteContentType(w http.ResponseWriter) {
func (r *SecureJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonContentType)
}
// Setup set data and opts
func (r *JsonpJSON) Setup(data interface{}, opts ...interface{}) {
r.Data = data
if len(opts) == 1 {
if callback, ok := opts[0].(string); ok {
r.Callback = callback
} else {
r.Callback = ""
}
}
}
// Reset clean data and opts
func (r *JsonpJSON) Reset() {
r.Data = nil
r.Callback = ""
}
// Render (JsonpJSON) marshals the given interface object and writes it and its callback with custom ContentType.
func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
func (r *JsonpJSON) Render(w http.ResponseWriter) (err error) {
r.WriteContentType(w)
ret, err := json.Marshal(r.Data)
if err != nil {
@ -130,12 +205,22 @@ func (r JsonpJSON) Render(w http.ResponseWriter) (err error) {
}
// WriteContentType (JsonpJSON) writes Javascript ContentType.
func (r JsonpJSON) WriteContentType(w http.ResponseWriter) {
func (r *JsonpJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonpContentType)
}
// Setup set data and opts
func (r *AsciiJSON) Setup(data interface{}, opts ...interface{}) {
r.Data = data
}
// Reset clean data and opts
func (r *AsciiJSON) Reset() {
r.Data = nil
}
// Render (AsciiJSON) marshals the given interface object and writes it with custom ContentType.
func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
func (r *AsciiJSON) Render(w http.ResponseWriter) (err error) {
r.WriteContentType(w)
ret, err := json.Marshal(r.Data)
if err != nil {
@ -156,6 +241,31 @@ func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
}
// WriteContentType (AsciiJSON) writes JSON ContentType.
func (r AsciiJSON) WriteContentType(w http.ResponseWriter) {
func (r *AsciiJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonAsciiContentType)
}
// Instance a new JSON object.
func (JSONFactory) Instance() RenderRecycler {
return &JSON{}
}
// Instance a new IndentedJSON object.
func (IndentedJSONFactory) Instance() RenderRecycler {
return &IndentedJSON{}
}
// Instance a new SecureJSON object.
func (SecureJSONFactory) Instance() RenderRecycler {
return &SecureJSON{}
}
// Instance a new JsonpJSON object.
func (JsonpJSONFactory) Instance() RenderRecycler {
return &JsonpJSON{}
}
// Instance a new AsciiJSON object.
func (AsciiJSONFactory) Instance() RenderRecycler {
return &AsciiJSON{}
}

View File

@ -12,11 +12,28 @@ import (
"github.com/gin-gonic/gin/internal/json"
)
func init() {
Register(PureJSONRenderType, &PureJsonFactory{})
}
// PureJSON contains the given interface object.
type PureJSON struct {
Data interface{}
}
// Setup set data and opts
func (r *PureJSON) Setup(data interface{}, opts ...interface{}) {
r.Data = data
}
// Reset clean data and opts
func (r *PureJSON) Reset() {
r.Data = nil
}
// JSONFactory instance the PureJson object.
type PureJsonFactory struct{}
// Render (PureJSON) writes custom ContentType and encodes the given interface object.
func (r PureJSON) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
@ -29,3 +46,8 @@ func (r PureJSON) Render(w http.ResponseWriter) error {
func (r PureJSON) WriteContentType(w http.ResponseWriter) {
writeContentType(w, jsonContentType)
}
// Instance a new Render instance
func (PureJsonFactory) Instance() RenderRecycler {
return &PureJSON{}
}

View File

@ -10,26 +10,48 @@ import (
"github.com/ugorji/go/codec"
)
func init() {
Register(MsgPackRenderType, MsgPackFactory{})
}
// MsgPack contains the given interface object.
type MsgPack struct {
Data interface{}
}
// MsgPackFactory instance the MsgPack object.
type MsgPackFactory struct{}
var msgpackContentType = []string{"application/msgpack; charset=utf-8"}
// WriteContentType (MsgPack) writes MsgPack ContentType.
func (r MsgPack) WriteContentType(w http.ResponseWriter) {
writeContentType(w, msgpackContentType)
// Setup set data and opts
func (r *MsgPack) Setup(data interface{}, opts ...interface{}) {
r.Data = data
}
// Reset clean data and opts
func (r *MsgPack) Reset() {
r.Data = nil
}
// Render (MsgPack) encodes the given interface object and writes data with custom ContentType.
func (r MsgPack) Render(w http.ResponseWriter) error {
func (r *MsgPack) Render(w http.ResponseWriter) error {
return WriteMsgPack(w, r.Data)
}
// WriteContentType (MsgPack) writes MsgPack ContentType.
func (r *MsgPack) WriteContentType(w http.ResponseWriter) {
writeContentType(w, msgpackContentType)
}
// WriteMsgPack writes MsgPack ContentType and encodes the given interface object.
func WriteMsgPack(w http.ResponseWriter, obj interface{}) error {
writeContentType(w, msgpackContentType)
var mh codec.MsgpackHandle
return codec.NewEncoder(w, &mh).Encode(obj)
}
// Instance a new MsgPack object.
func (MsgPackFactory) Instance() RenderRecycler {
return &MsgPack{}
}

View File

@ -10,15 +10,32 @@ import (
"github.com/golang/protobuf/proto"
)
func init() {
Register(ProtoBufRenderType, ProtoBufFactory{})
}
// ProtoBuf contains the given interface object.
type ProtoBuf struct {
Data interface{}
}
// ProtoBufFactory instance the ProtoBuf object.
type ProtoBufFactory struct{}
var protobufContentType = []string{"application/x-protobuf"}
// Setup set data and opts
func (r *ProtoBuf) Setup(data interface{}, opts ...interface{}) {
r.Data = data
}
// Reset clean data and opts
func (r *ProtoBuf) Reset() {
r.Data = nil
}
// Render (ProtoBuf) marshals the given interface object and writes data with custom ContentType.
func (r ProtoBuf) Render(w http.ResponseWriter) error {
func (r *ProtoBuf) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
bytes, err := proto.Marshal(r.Data.(proto.Message))
@ -31,6 +48,11 @@ func (r ProtoBuf) Render(w http.ResponseWriter) error {
}
// WriteContentType (ProtoBuf) writes ProtoBuf ContentType.
func (r ProtoBuf) WriteContentType(w http.ResponseWriter) {
func (r *ProtoBuf) WriteContentType(w http.ResponseWriter) {
writeContentType(w, protobufContentType)
}
// Instance a new ProtoBuf object.
func (ProtoBufFactory) Instance() RenderRecycler {
return &ProtoBuf{}
}

View File

@ -4,7 +4,29 @@
package render
import "net/http"
import (
"net/http"
"sync"
)
// RenderType Names
const (
JSONRenderType = iota // JSON Render Type
IntendedJSONRenderType // IntendedJSON Render Type
PureJSONRenderType // PureJSON Render Type
AsciiJSONRenderType // AsciiJSON Render Type
JsonpJSONRenderType // JsonpJSON Render Type
SecureJSONRenderType // SecureJSON Type
XMLRenderType // XML Render Type
YAMLRenderType // YAML Render Type
MsgPackRenderType // MsgPack Render Type
ProtoBufRenderType // ProtoBuf Render Type
EmptyRenderType // Empty Render Type
)
var (
renderPool = &RenderPool{renderPools: make(map[int]sync.Pool)}
)
// Render interface is to be implemented by JSON, XML, HTML, YAML and so on.
type Render interface {
@ -14,23 +36,106 @@ type Render interface {
WriteContentType(w http.ResponseWriter)
}
// RenderRecycler interface is to be implemented by JSON, XML, HTML, YAML and so on.
type RenderRecycler interface {
Render
// Setup set data and opts
Setup(data interface{}, opts ...interface{})
// Reset clean data and opts
Reset()
}
// RenderFactory interface is to be implemented by other Render.
type RenderFactory interface {
// Instance a new RenderRecycler instance
Instance() RenderRecycler
}
// RenderPool contains Render instance
type RenderPool struct {
mu sync.RWMutex
renderPools map[int]sync.Pool
}
func (p *RenderPool) get(name int) RenderRecycler {
p.mu.RLock()
defer p.mu.RUnlock()
var render RenderRecycler
if pool, ok := p.renderPools[name]; ok {
render, _ = pool.Get().(RenderRecycler)
} else {
pool, _ = p.renderPools[EmptyRenderType]
render, _ = pool.Get().(RenderRecycler)
}
if render == nil {
render = &EmptyRender{}
}
return render
}
func (p *RenderPool) put(name int, render RenderRecycler) {
p.mu.RLock()
defer p.mu.RUnlock()
if pool, ok := p.renderPools[name]; ok {
render.Reset()
pool.Put(render)
}
}
func (p *RenderPool) register(name int, factory RenderFactory) {
p.mu.Lock()
defer p.mu.Unlock()
if factory == nil {
panic("gin: Register RenderFactory is nil")
}
if _, dup := p.renderPools[name]; dup {
panic("gin: Register called twice for RenderFactory")
}
p.renderPools[name] = sync.Pool{
New: func() interface{} {
return factory.Instance()
},
}
}
// Register makes a binding available by the provided name.
// If Register is called twice with the same name or if binding is nil,
// it panics.
func Register(name int, factory RenderFactory) {
renderPool.register(name, factory)
}
// Default returns the appropriate Render instance based on the render type.
func Default(name int) RenderRecycler {
return renderPool.get(name)
}
// Recycle put render to sync.Pool
func Recycle(name int, render RenderRecycler) {
renderPool.put(name, render)
}
var (
_ Render = JSON{}
_ Render = IndentedJSON{}
_ Render = SecureJSON{}
_ Render = JsonpJSON{}
_ Render = XML{}
_ Render = String{}
_ Render = Redirect{}
_ Render = Data{}
_ Render = HTML{}
_ HTMLRender = HTMLDebug{}
_ HTMLRender = HTMLProduction{}
_ Render = YAML{}
_ Render = MsgPack{}
_ Render = Reader{}
_ Render = AsciiJSON{}
_ Render = ProtoBuf{}
_ RenderRecycler = &JSON{}
_ RenderRecycler = &IndentedJSON{}
_ RenderRecycler = &SecureJSON{}
_ RenderRecycler = &JsonpJSON{}
_ RenderRecycler = &XML{}
_ Render = String{}
_ Render = Redirect{}
_ Render = Data{}
_ Render = HTML{}
_ HTMLRender = HTMLDebug{}
_ HTMLRender = HTMLProduction{}
_ RenderRecycler = &YAML{}
_ RenderRecycler = &MsgPack{}
_ Render = Reader{}
_ RenderRecycler = &AsciiJSON{}
_ RenderRecycler = &ProtoBuf{}
)
func writeContentType(w http.ResponseWriter, value []string) {

View File

@ -19,7 +19,10 @@ func TestRenderPureJSON(t *testing.T) {
"foo": "bar",
"html": "<b>",
}
err := (PureJSON{data}).Render(w)
r := Default(PureJSONRenderType)
r.Setup(data)
err := r.Render(w)
Recycle(PureJSONRenderType, r)
assert.NoError(t, err)
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"<b>\"}\n", w.Body.String())
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))

View File

@ -31,11 +31,15 @@ func TestRenderMsgPack(t *testing.T) {
"foo": "bar",
}
(MsgPack{data}).WriteContentType(w)
r := Default(MsgPackRenderType)
r.Setup(data)
r.WriteContentType(w)
assert.Equal(t, "application/msgpack; charset=utf-8", w.Header().Get("Content-Type"))
err := (MsgPack{data}).Render(w)
r.Reset()
r.Setup(data)
err := r.Render(w)
Recycle(MsgPackRenderType, r)
assert.NoError(t, err)
h := new(codec.MsgpackHandle)
@ -56,10 +60,15 @@ func TestRenderJSON(t *testing.T) {
"html": "<b>",
}
(JSON{data}).WriteContentType(w)
r := Default(JSONRenderType)
r.Setup(data)
r.WriteContentType(w)
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
err := (JSON{data}).Render(w)
r.Reset()
r.Setup(data)
err := r.Render(w)
Recycle(JSONRenderType, r)
assert.NoError(t, err)
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String())
@ -71,7 +80,12 @@ func TestRenderJSONPanics(t *testing.T) {
data := make(chan int)
// json: unsupported type: chan int
assert.Panics(t, func() { (JSON{data}).Render(w) })
assert.Panics(t, func() {
r := Default(JSONRenderType)
r.Setup(data)
r.Render(w)
Recycle(JSONRenderType, r)
})
}
func TestRenderIndentedJSON(t *testing.T) {
@ -81,7 +95,10 @@ func TestRenderIndentedJSON(t *testing.T) {
"bar": "foo",
}
err := (IndentedJSON{data}).Render(w)
r := Default(IntendedJSONRenderType)
r.Setup(data)
err := r.Render(w)
Recycle(IntendedJSONRenderType, r)
assert.NoError(t, err)
assert.Equal(t, "{\n \"bar\": \"foo\",\n \"foo\": \"bar\"\n}", w.Body.String())
@ -93,7 +110,11 @@ func TestRenderIndentedJSONPanics(t *testing.T) {
data := make(chan int)
// json: unsupported type: chan int
err := (IndentedJSON{data}).Render(w)
r := Default(IntendedJSONRenderType)
r.Setup(data)
err := r.Render(w)
Recycle(IntendedJSONRenderType, r)
assert.Error(t, err)
}
@ -103,10 +124,17 @@ func TestRenderSecureJSON(t *testing.T) {
"foo": "bar",
}
(SecureJSON{"while(1);", data}).WriteContentType(w1)
r := Default(SecureJSONRenderType)
r.Setup(data, "while(1);")
r.WriteContentType(w1)
Recycle(SecureJSONRenderType, r)
assert.Equal(t, "application/json; charset=utf-8", w1.Header().Get("Content-Type"))
err1 := (SecureJSON{"while(1);", data}).Render(w1)
r1 := Default(SecureJSONRenderType)
r1.Setup(data, "while(1);")
err1 := r1.Render(w1)
Recycle(SecureJSONRenderType, r1)
assert.NoError(t, err1)
assert.Equal(t, "{\"foo\":\"bar\"}", w1.Body.String())
@ -119,7 +147,11 @@ func TestRenderSecureJSON(t *testing.T) {
"bar": "foo",
}}
err2 := (SecureJSON{"while(1);", datas}).Render(w2)
r2 := Default(SecureJSONRenderType)
r2.Setup(datas, "while(1);")
err2 := r2.Render(w2)
Recycle(SecureJSONRenderType, r2)
assert.NoError(t, err2)
assert.Equal(t, "while(1);[{\"foo\":\"bar\"},{\"bar\":\"foo\"}]", w2.Body.String())
assert.Equal(t, "application/json; charset=utf-8", w2.Header().Get("Content-Type"))
@ -130,20 +162,31 @@ func TestRenderSecureJSONFail(t *testing.T) {
data := make(chan int)
// json: unsupported type: chan int
err := (SecureJSON{"while(1);", data}).Render(w)
r := Default(SecureJSONRenderType)
r.Setup(data, "while(1);")
err := r.Render(w)
Recycle(SecureJSONRenderType, r)
assert.Error(t, err)
}
func TestRenderJsonpJSON(t *testing.T) {
w1 := httptest.NewRecorder()
w := 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"))
r := Default(JsonpJSONRenderType)
r.Setup(data, "x")
r.WriteContentType(w)
Recycle(JsonpJSONRenderType, r)
assert.Equal(t, "application/javascript; charset=utf-8", w.Header().Get("Content-Type"))
err1 := (JsonpJSON{"x", data}).Render(w1)
w1 := httptest.NewRecorder()
r1 := Default(JsonpJSONRenderType)
r1.Setup(data, "x")
err1 := r1.Render(w1)
// Recycle(JsonpJSONRenderType, r1)
assert.NoError(t, err1)
assert.Equal(t, "x({\"foo\":\"bar\"})", w1.Body.String())
@ -156,7 +199,10 @@ func TestRenderJsonpJSON(t *testing.T) {
"bar": "foo",
}}
err2 := (JsonpJSON{"x", datas}).Render(w2)
r2 := Default(JsonpJSONRenderType)
r2.Setup(datas, "x")
err2 := r2.Render(w2)
Recycle(JsonpJSONRenderType, r2)
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"))
@ -167,10 +213,15 @@ func TestRenderJsonpJSONError2(t *testing.T) {
data := map[string]interface{}{
"foo": "bar",
}
(JsonpJSON{"", data}).WriteContentType(w)
r := Default(JsonpJSONRenderType)
r.Setup(data, "")
r.WriteContentType(w)
assert.Equal(t, "application/javascript; charset=utf-8", w.Header().Get("Content-Type"))
e := (JsonpJSON{"", data}).Render(w)
r.Reset()
r.Setup(data, "")
e := r.Render(w)
Recycle(JsonpJSONRenderType, r)
assert.NoError(t, e)
assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String())
@ -181,8 +232,11 @@ func TestRenderJsonpJSONFail(t *testing.T) {
w := httptest.NewRecorder()
data := make(chan int)
r := Default(JsonpJSONRenderType)
r.Setup(data, "x")
// json: unsupported type: chan int
err := (JsonpJSON{"x", data}).Render(w)
err := r.Render(w)
Recycle(JsonpJSONRenderType, r)
assert.Error(t, err)
}
@ -193,7 +247,9 @@ func TestRenderAsciiJSON(t *testing.T) {
"tag": "<br>",
}
err := (AsciiJSON{data1}).Render(w1)
r := Default(AsciiJSONRenderType)
r.Setup(data1)
err := r.Render(w1)
assert.NoError(t, err)
assert.Equal(t, "{\"lang\":\"GO\\u8bed\\u8a00\",\"tag\":\"\\u003cbr\\u003e\"}", w1.Body.String())
@ -202,7 +258,10 @@ func TestRenderAsciiJSON(t *testing.T) {
w2 := httptest.NewRecorder()
data2 := float64(3.1415926)
err = (AsciiJSON{data2}).Render(w2)
r.Reset()
r.Setup(data2)
err = r.Render(w2)
Recycle(AsciiJSONRenderType, r)
assert.NoError(t, err)
assert.Equal(t, "3.1415926", w2.Body.String())
}
@ -211,8 +270,11 @@ func TestRenderAsciiJSONFail(t *testing.T) {
w := httptest.NewRecorder()
data := make(chan int)
r := Default(AsciiJSONRenderType)
r.Setup(data)
// json: unsupported type: chan int
assert.Error(t, (AsciiJSON{data}).Render(w))
assert.Error(t, r.Render(w))
Recycle(AsciiJSONRenderType, r)
}
type xmlmap map[string]interface{}
@ -247,10 +309,15 @@ b:
c: 2
d: [3, 4]
`
(YAML{data}).WriteContentType(w)
r := Default(YAMLRenderType)
r.Setup(data)
r.WriteContentType(w)
assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type"))
err := (YAML{data}).Render(w)
r.Reset()
r.Setup(data)
err := r.Render(w)
Recycle(YAMLRenderType, r)
assert.NoError(t, err)
assert.Equal(t, "\"\\na : Easy!\\nb:\\n\\tc: 2\\n\\td: [3, 4]\\n\\t\"\n", w.Body.String())
assert.Equal(t, "application/x-yaml; charset=utf-8", w.Header().Get("Content-Type"))
@ -265,7 +332,10 @@ func (ft *fail) MarshalYAML() (interface{}, error) {
func TestRenderYAMLFail(t *testing.T) {
w := httptest.NewRecorder()
err := (YAML{&fail{}}).Render(w)
r := Default(YAMLRenderType)
r.Setup(&fail{})
err := r.Render(w)
Recycle(YAMLRenderType, r)
assert.Error(t, err)
}
@ -279,12 +349,17 @@ func TestRenderProtoBuf(t *testing.T) {
Reps: reps,
}
(ProtoBuf{data}).WriteContentType(w)
r := Default(ProtoBufRenderType)
r.Setup(data)
r.WriteContentType(w)
protoData, err := proto.Marshal(data)
assert.NoError(t, err)
assert.Equal(t, "application/x-protobuf", w.Header().Get("Content-Type"))
err = (ProtoBuf{data}).Render(w)
r.Reset()
r.Setup(data)
err = r.Render(w)
Recycle(ProtoBufRenderType, r)
assert.NoError(t, err)
assert.Equal(t, string(protoData), w.Body.String())
@ -294,7 +369,10 @@ func TestRenderProtoBuf(t *testing.T) {
func TestRenderProtoBufFail(t *testing.T) {
w := httptest.NewRecorder()
data := &testdata.Test{}
err := (ProtoBuf{data}).Render(w)
r := Default(ProtoBufRenderType)
r.Setup(data)
err := r.Render(w)
Recycle(ProtoBufRenderType, r)
assert.Error(t, err)
}
@ -304,10 +382,15 @@ func TestRenderXML(t *testing.T) {
"foo": "bar",
}
(XML{data}).WriteContentType(w)
r := Default(XMLRenderType)
r.Setup(data)
r.WriteContentType(w)
assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type"))
err := (XML{data}).Render(w)
r.Reset()
r.Setup(data)
err := r.Render(w)
Recycle(XMLRenderType, r)
assert.NoError(t, err)
assert.Equal(t, "<map><foo>bar</foo></map>", w.Body.String())

View File

@ -9,20 +9,42 @@ import (
"net/http"
)
func init() {
Register(XMLRenderType, XMLFactory{})
}
// XML contains the given interface object.
type XML struct {
Data interface{}
}
// XMLFactory instance the XML object.
type XMLFactory struct{}
var xmlContentType = []string{"application/xml; charset=utf-8"}
// Setup set data and opts
func (r *XML) Setup(data interface{}, opts ...interface{}) {
r.Data = data
}
// Reset clean data and opts
func (r *XML) Reset() {
r.Data = nil
}
// Render (XML) encodes the given interface object and writes data with custom ContentType.
func (r XML) Render(w http.ResponseWriter) error {
func (r *XML) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
return xml.NewEncoder(w).Encode(r.Data)
}
// WriteContentType (XML) writes XML ContentType for response.
func (r XML) WriteContentType(w http.ResponseWriter) {
func (r *XML) WriteContentType(w http.ResponseWriter) {
writeContentType(w, xmlContentType)
}
// Instance a new XML object.
func (XMLFactory) Instance() RenderRecycler {
return &XML{}
}

View File

@ -10,15 +10,32 @@ import (
"gopkg.in/yaml.v2"
)
func init() {
Register(YAMLRenderType, YAMLFactory{})
}
// YAML contains the given interface object.
type YAML struct {
Data interface{}
}
// YAMLFactory instance the YAML object.
type YAMLFactory struct{}
var yamlContentType = []string{"application/x-yaml; charset=utf-8"}
// Setup set data and opts
func (r *YAML) Setup(data interface{}, opts ...interface{}) {
r.Data = data
}
// Reset clean data and opts
func (r *YAML) Reset() {
r.Data = nil
}
// Render (YAML) marshals the given interface object and writes data with custom ContentType.
func (r YAML) Render(w http.ResponseWriter) error {
func (r *YAML) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
bytes, err := yaml.Marshal(r.Data)
@ -31,6 +48,11 @@ func (r YAML) Render(w http.ResponseWriter) error {
}
// WriteContentType (YAML) writes YAML ContentType for response.
func (r YAML) WriteContentType(w http.ResponseWriter) {
func (r *YAML) WriteContentType(w http.ResponseWriter) {
writeContentType(w, yamlContentType)
}
// Instance a new Render instance
func (YAMLFactory) Instance() RenderRecycler {
return &YAML{}
}