mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-22 01:12:16 +08:00
render: add sync.Pool to manager JSON,XML,YAML,ProtoBuf,MsgPack Render object
This commit is contained in:
parent
85b92cdf4b
commit
f7f798f408
2
.gitignore
vendored
2
.gitignore
vendored
@ -5,3 +5,5 @@ count.out
|
||||
test
|
||||
profile.out
|
||||
tmp.out
|
||||
.idea/
|
||||
*.iml
|
||||
|
35
context.go
35
context.go
@ -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.
|
||||
|
@ -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
39
render/empty.go
Normal 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
|
||||
}
|
130
render/json.go
130
render/json.go
@ -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{}
|
||||
}
|
||||
|
@ -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{}
|
||||
}
|
||||
|
@ -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{}
|
||||
}
|
||||
|
@ -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{}
|
||||
}
|
||||
|
139
render/render.go
139
render/render.go
@ -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) {
|
||||
|
@ -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"))
|
||||
|
@ -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())
|
||||
|
@ -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{}
|
||||
}
|
||||
|
@ -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{}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user