Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 46 additions & 14 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -1264,32 +1264,64 @@ type Negotiate struct {
TOMLData any
}

// Negotiate calls different Render according to acceptable Accept format.
func (c *Context) Negotiate(code int, config Negotiate) {
switch c.NegotiateFormat(config.Offered...) {
case binding.MIMEJSON:
func NewNegotiate(offered []string) Negotiate {
return Negotiate{Offered: offered}
}
func (receiver Negotiate) WithData(data any) Negotiate {
receiver.Data = data
return receiver
}

// NegotiationRenderFunc is responsible for rendering data in a specific format.
type NegotiationRenderFunc func(status int, config Negotiate, c *Context)

func AddNegotiationRenderMapping(mimeType string, binding NegotiationRenderFunc) {
negotiationRenderMappings[mimeType] = binding
}

// All predefined negotiationRenderMappings - associate a content type
// with a NegotiationRenderFunc, which is responsible for rendering
// data in a specific format.
var negotiationRenderMappings = map[string]NegotiationRenderFunc{
binding.MIMEJSON: func(code int, config Negotiate, c *Context) {
data := chooseData(config.JSONData, config.Data)
c.JSON(code, data)

case binding.MIMEHTML:
},
binding.MIMEHTML: func(code int, config Negotiate, c *Context) {
data := chooseData(config.HTMLData, config.Data)
c.HTML(code, config.HTMLName, data)

case binding.MIMEXML:
},
binding.MIMEXML: func(code int, config Negotiate, c *Context) {
data := chooseData(config.XMLData, config.Data)
c.XML(code, data)

case binding.MIMEYAML, binding.MIMEYAML2:
},
binding.MIMEYAML: func(code int, config Negotiate, c *Context) {
data := chooseData(config.YAMLData, config.Data)
c.YAML(code, data)

case binding.MIMETOML:
},
binding.MIMEYAML2: func(code int, config Negotiate, c *Context) {
data := chooseData(config.YAMLData, config.Data)
c.YAML(code, data)
},
binding.MIMETOML: func(code int, config Negotiate, c *Context) {
data := chooseData(config.TOMLData, config.Data)
c.TOML(code, data)
},
binding.MIMEPROTOBUF: func(code int, config Negotiate, c *Context) {
c.ProtoBuf(code, config.Data)
},
}

default:
c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server")) //nolint: errcheck
// Negotiate calls different Render according to acceptable Accept format.
func (c *Context) Negotiate(code int, config Negotiate) {

accepted := c.NegotiateFormat(config.Offered...)
if fn, ok := negotiationRenderMappings[accepted]; ok {
fn(code, config, c)
return
}
c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server")) //nolint: errcheck

}

// NegotiateFormat returns an acceptable Accept format.
Expand Down
38 changes: 38 additions & 0 deletions context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1542,6 +1542,44 @@ func TestContextNegotiationWithHTML(t *testing.T) {
assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
}

func TestContextNegotiationWithProto(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest(http.MethodPost, "", nil)

c.Negotiate(http.StatusOK, Negotiate{
Offered: []string{binding.MIMEPROTOBUF},
Data: &testdata.Test{},
})

assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, 0, w.Body.Len())
assert.Equal(t, "application/x-protobuf", w.Header().Get("Content-Type"))
}

func TestContextNegotiationWithCustom(t *testing.T) {

contentType := "application/whatever"

w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest(http.MethodPost, "", nil)

AddNegotiationRenderMapping(contentType, func(status int, config Negotiate, c *Context) {
data := config.Data.([]byte)
c.Data(status, contentType, data)
})

c.Negotiate(http.StatusOK, Negotiate{
Offered: []string{contentType},
Data: []byte("Hello World"),
})

assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "Hello World", w.Body.String())
assert.Equal(t, contentType, w.Header().Get("Content-Type"))
}

func TestContextNegotiationNotSupport(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
Expand Down