mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-15 21:06:39 +08:00
Added protobuf support for negotiation
This commit is contained in:
parent
4a6bc4aac4
commit
743cdd1a39
12
context.go
12
context.go
@ -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"))
|
||||||
}
|
}
|
||||||
|
@ -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
24
render/protobuf.go
Normal 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
|
||||||
|
}
|
@ -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
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user