From 2f166fe9cc3ecb9586c7eb8c759eec2a65d89669 Mon Sep 17 00:00:00 2001 From: Javier Provecho Fernandez Date: Mon, 2 Jan 2017 13:25:25 +0100 Subject: [PATCH] Add support for the new function in Render interface for writing content-type only --- context.go | 30 +++++++++---- context_test.go | 102 ++++++++++++++++++++++++++++++++++++++++++++- middleware_test.go | 2 +- 3 files changed, 125 insertions(+), 9 deletions(-) diff --git a/context.go b/context.go index 01c7cb4f..65819c6c 100644 --- a/context.go +++ b/context.go @@ -17,7 +17,7 @@ import ( "github.com/gin-gonic/gin/binding" "github.com/gin-gonic/gin/render" - "github.com/manucorporat/sse" + "gopkg.in/gin-contrib/sse.v0" ) // Content-Type MIME of the most common data formats @@ -404,6 +404,19 @@ func (c *Context) requestHeader(key string) string { /******** RESPONSE RENDERING ********/ /************************************/ +// bodyAllowedForStatus is a copy of http.bodyAllowedForStatus non-exported function +func bodyAllowedForStatus(status int) bool { + switch { + case status >= 100 && status <= 199: + return false + case status == 204: + return false + case status == 304: + return false + } + return true +} + func (c *Context) Status(code int) { c.writermem.WriteHeader(code) } @@ -453,6 +466,13 @@ func (c *Context) Cookie(name string) (string, error) { func (c *Context) Render(code int, r render.Render) { c.Status(code) + + if !bodyAllowedForStatus(code) { + r.WriteContentType(c.Writer) + c.Writer.WriteHeaderNow() + return + } + if err := r.Render(c.Writer); err != nil { panic(err) } @@ -477,10 +497,7 @@ func (c *Context) IndentedJSON(code int, obj interface{}) { // 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.Status(code) - if err := render.WriteJSON(c.Writer, obj); err != nil { - panic(err) - } + c.Render(code, render.JSON{Data: obj}) } // XML serializes the given struct as XML into the response body. @@ -496,8 +513,7 @@ func (c *Context) YAML(code int, obj interface{}) { // String writes the given string into the response body. func (c *Context) String(code int, format string, values ...interface{}) { - c.Status(code) - render.WriteString(c.Writer, format, values) + c.Render(code, render.String{Format: format, Data: values}) } // Redirect returns a HTTP redirect to the specific location. diff --git a/context_test.go b/context_test.go index fe22c492..96e4d04b 100644 --- a/context_test.go +++ b/context_test.go @@ -15,9 +15,9 @@ import ( "testing" "time" - "github.com/manucorporat/sse" "github.com/stretchr/testify/assert" "golang.org/x/net/context" + "gopkg.in/gin-contrib/sse.v0" ) var _ context.Context = &Context{} @@ -394,6 +394,18 @@ func TestContextRenderJSON(t *testing.T) { assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type")) } +// Tests that no JSON is rendered if code is 204 +func TestContextRenderNoContentJSON(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + c.JSON(204, H{"foo": "bar"}) + + assert.Equal(t, 204, w.Code) + assert.Equal(t, "", w.Body.String()) + assert.Equal(t, "application/json; charset=utf-8", w.HeaderMap.Get("Content-Type")) +} + // Tests that the response is serialized as JSON // we change the content-type before func TestContextRenderAPIJSON(t *testing.T) { @@ -408,6 +420,19 @@ func TestContextRenderAPIJSON(t *testing.T) { assert.Equal(t, "application/vnd.api+json", w.HeaderMap.Get("Content-Type")) } +// Tests that no Custom JSON is rendered if code is 204 +func TestContextRenderNoContentAPIJSON(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + c.Header("Content-Type", "application/vnd.api+json") + c.JSON(204, H{"foo": "bar"}) + + assert.Equal(t, 204, w.Code) + assert.Equal(t, "", w.Body.String()) + assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/vnd.api+json") +} + // Tests that the response is serialized as JSON // and Content-Type is set to application/json func TestContextRenderIndentedJSON(t *testing.T) { @@ -421,6 +446,18 @@ func TestContextRenderIndentedJSON(t *testing.T) { assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/json; charset=utf-8") } +// Tests that no Custom JSON is rendered if code is 204 +func TestContextRenderNoContentIndentedJSON(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + c.IndentedJSON(204, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}}) + + assert.Equal(t, 204, w.Code) + assert.Equal(t, "", w.Body.String()) + assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/json; charset=utf-8") +} + // Tests that the response executes the templates // and responds with Content-Type set to text/html func TestContextRenderHTML(t *testing.T) { @@ -436,6 +473,20 @@ func TestContextRenderHTML(t *testing.T) { assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/html; charset=utf-8") } +// Tests that no HTML is rendered if code is 204 +func TestContextRenderNoContentHTML(t *testing.T) { + w := httptest.NewRecorder() + c, router := CreateTestContext(w) + templ := template.Must(template.New("t").Parse(`Hello {{.name}}`)) + router.SetHTMLTemplate(templ) + + c.HTML(204, "t", H{"name": "alexandernyquist"}) + + assert.Equal(t, 204, w.Code) + assert.Equal(t, "", w.Body.String()) + assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/html; charset=utf-8") +} + // TestContextXML tests that the response is serialized as XML // and Content-Type is set to application/xml func TestContextRenderXML(t *testing.T) { @@ -449,6 +500,18 @@ func TestContextRenderXML(t *testing.T) { assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/xml; charset=utf-8") } +// Tests that no XML is rendered if code is 204 +func TestContextRenderNoContentXML(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + c.XML(204, H{"foo": "bar"}) + + assert.Equal(t, 204, w.Code) + assert.Equal(t, "", w.Body.String()) + assert.Equal(t, w.HeaderMap.Get("Content-Type"), "application/xml; charset=utf-8") +} + // TestContextString tests that the response is returned // with Content-Type set to text/plain func TestContextRenderString(t *testing.T) { @@ -462,6 +525,18 @@ func TestContextRenderString(t *testing.T) { assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/plain; charset=utf-8") } +// Tests that no String is rendered if code is 204 +func TestContextRenderNoContentString(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + c.String(204, "test %s %d", "string", 2) + + assert.Equal(t, 204, w.Code) + assert.Equal(t, "", w.Body.String()) + assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/plain; charset=utf-8") +} + // TestContextString tests that the response is returned // with Content-Type set to text/html func TestContextRenderHTMLString(t *testing.T) { @@ -476,6 +551,19 @@ func TestContextRenderHTMLString(t *testing.T) { assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/html; charset=utf-8") } +// Tests that no HTML String is rendered if code is 204 +func TestContextRenderNoContentHTMLString(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + c.Header("Content-Type", "text/html; charset=utf-8") + c.String(204, "%s %d", "string", 3) + + assert.Equal(t, 204, w.Code) + assert.Equal(t, "", w.Body.String()) + assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/html; charset=utf-8") +} + // TestContextData tests that the response can be written from `bytesting` // with specified MIME type func TestContextRenderData(t *testing.T) { @@ -489,6 +577,18 @@ func TestContextRenderData(t *testing.T) { assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/csv") } +// Tests that no Custom Data is rendered if code is 204 +func TestContextRenderNoContentData(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + c.Data(204, "text/csv", []byte(`foo,bar`)) + + assert.Equal(t, 204, w.Code) + assert.Equal(t, "", w.Body.String()) + assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/csv") +} + func TestContextRenderSSE(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) diff --git a/middleware_test.go b/middleware_test.go index 273d003d..c77f8276 100644 --- a/middleware_test.go +++ b/middleware_test.go @@ -10,8 +10,8 @@ import ( "testing" - "github.com/manucorporat/sse" "github.com/stretchr/testify/assert" + "gopkg.in/gin-contrib/sse.v0" ) func TestMiddlewareGeneralCase(t *testing.T) {