Merge 743cdd1a39fefa28e4e6f86d81a02b82512b433d into 72ffff6e442e76b3109322d71a2b64a1ba0fcf74

This commit is contained in:
Riku Ayanokoji 2016-04-12 07:17:15 +00:00
commit c9fa87b1be
4 changed files with 66 additions and 1 deletions

View File

@ -26,6 +26,7 @@ const (
MIMEHTML = binding.MIMEHTML MIMEHTML = binding.MIMEHTML
MIMEXML = binding.MIMEXML MIMEXML = binding.MIMEXML
MIMEXML2 = binding.MIMEXML2 MIMEXML2 = binding.MIMEXML2
MIMEPROTOBUF = binding.MIMEPROTOBUF
MIMEPlain = binding.MIMEPlain MIMEPlain = binding.MIMEPlain
MIMEPOSTForm = binding.MIMEPOSTForm MIMEPOSTForm = binding.MIMEPOSTForm
MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm
@ -426,6 +427,12 @@ func (c *Context) XML(code int, obj interface{}) {
c.Render(code, render.XML{Data: obj}) c.Render(code, render.XML{Data: obj})
} }
// Protobuf serializes the given struct as PB binary-stream into response body
// It also sets the Content-Type as "application/x-protobuf"
func (c *Context) Protobuf(code int, obj interface{}) {
c.Render(code, render.Protobuf{Data: obj})
}
// String writes the given string into the response body. // String writes the given string into the response body.
func (c *Context) String(code int, format string, values ...interface{}) { func (c *Context) String(code int, format string, values ...interface{}) {
c.Status(code) c.Status(code)
@ -489,6 +496,7 @@ type Negotiate struct {
HTMLData interface{} HTMLData interface{}
JSONData interface{} JSONData interface{}
XMLData interface{} XMLData interface{}
PBData interface{}
Data interface{} Data interface{}
} }
@ -506,6 +514,10 @@ func (c *Context) Negotiate(code int, config Negotiate) {
data := chooseData(config.XMLData, config.Data) data := chooseData(config.XMLData, config.Data)
c.XML(code, data) c.XML(code, data)
case binding.MIMEPROTOBUF:
data := chooseData(config.PBData, config.Data)
c.Protobuf(code, data)
default: default:
c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server")) c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server"))
} }

View File

@ -15,6 +15,8 @@ import (
"testing" "testing"
"time" "time"
"github.com/gin-gonic/gin/binding/example"
"github.com/golang/protobuf/proto"
"github.com/manucorporat/sse" "github.com/manucorporat/sse"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -374,6 +376,16 @@ func TestContextRenderXML(t *testing.T) {
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/xml; charset=utf-8") assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/xml; charset=utf-8")
} }
// TestContextRenderProtobuf tests that the response is serialized as Protobuf binary-stream
// and Content-Type is set to application/x-protobuf
func TestContextRenderProtobuf(t *testing.T) {
c, w, _ := CreateTestContext()
c.Protobuf(201, &example.Test{Label: proto.String("test")})
assert.Equal(t, w.Code, 201)
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/x-protobuf")
}
// TestContextString tests that the response is returned // TestContextString tests that the response is returned
// with Content-Type set to text/plain // with Content-Type set to text/plain
func TestContextRenderString(t *testing.T) { func TestContextRenderString(t *testing.T) {
@ -500,15 +512,17 @@ func TestContextNegotiationFormat(t *testing.T) {
assert.Panics(t, func() { c.NegotiateFormat() }) assert.Panics(t, func() { c.NegotiateFormat() })
assert.Equal(t, c.NegotiateFormat(MIMEJSON, MIMEXML), MIMEJSON) assert.Equal(t, c.NegotiateFormat(MIMEJSON, MIMEXML), MIMEJSON)
assert.Equal(t, c.NegotiateFormat(MIMEHTML, MIMEJSON), MIMEHTML) assert.Equal(t, c.NegotiateFormat(MIMEHTML, MIMEJSON), MIMEHTML)
assert.Equal(t, c.NegotiateFormat(MIMEPROTOBUF, MIMEPROTOBUF), MIMEPROTOBUF)
} }
func TestContextNegotiationFormatWithAccept(t *testing.T) { func TestContextNegotiationFormatWithAccept(t *testing.T) {
c, _, _ := CreateTestContext() c, _, _ := CreateTestContext()
c.Request, _ = http.NewRequest("POST", "/", nil) c.Request, _ = http.NewRequest("POST", "/", nil)
c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") c.Request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml,application/x-protobuf;q=0.9,*/*;q=0.8")
assert.Equal(t, c.NegotiateFormat(MIMEJSON, MIMEXML), MIMEXML) assert.Equal(t, c.NegotiateFormat(MIMEJSON, MIMEXML), MIMEXML)
assert.Equal(t, c.NegotiateFormat(MIMEXML, MIMEHTML), MIMEHTML) assert.Equal(t, c.NegotiateFormat(MIMEXML, MIMEHTML), MIMEHTML)
assert.Equal(t, c.NegotiateFormat(MIMEPROTOBUF, MIMEPROTOBUF), MIMEPROTOBUF)
assert.Equal(t, c.NegotiateFormat(MIMEJSON), "") assert.Equal(t, c.NegotiateFormat(MIMEJSON), "")
} }

24
render/protobuf.go Normal file
View File

@ -0,0 +1,24 @@
package render
import (
"github.com/golang/protobuf/proto"
"net/http"
)
type Protobuf struct {
Data interface{}
}
var pbContentType = []string{"application/x-protobuf"}
func (r Protobuf) Render(w http.ResponseWriter) error {
writeContentType(w, pbContentType)
encoded, err := proto.Marshal(r.Data.(proto.Message))
if err != nil {
return err
}
if _, err = w.Write(encoded); err != nil {
return err
}
return nil
}

View File

@ -10,6 +10,8 @@ import (
"net/http/httptest" "net/http/httptest"
"testing" "testing"
"github.com/gin-gonic/gin/binding/example"
"github.com/golang/protobuf/proto"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -82,6 +84,19 @@ func TestRenderXML(t *testing.T) {
assert.Equal(t, w.Header().Get("Content-Type"), "application/xml; charset=utf-8") assert.Equal(t, w.Header().Get("Content-Type"), "application/xml; charset=utf-8")
} }
func TestRenderProtobuf(t *testing.T) {
w := httptest.NewRecorder()
data := &example.Test{
Label: proto.String("test"),
}
err := (Protobuf{data}).Render(w)
assert.NoError(t, err)
// assert.Equal(t, w.Body.String(), "<map><foo>bar</foo></map>")
// assert.Equal(t, w.Header().Get("Content-Type"), "application/xml; charset=utf-8")
}
func TestRenderRedirect(t *testing.T) { func TestRenderRedirect(t *testing.T) {
// TODO // TODO
} }