mirror of
https://github.com/labstack/echo.git
synced 2024-12-24 20:14:31 +02:00
* echo.context.cjson should encode the JSON before writing the status code #1334 : `response.Write` automatically sets status to `200` if a response code wasn't committed yet. This is convenient, but it ignores the fact that `response.Status` is a public field that may be set separately/before `response.Write` has been called A `response.Status` is by default `0`, or `200` if the response was reset, so `response.Write` should fallback to `200` only if a code wasn't set yet. * echo.context.cjson should encode the JSON before writing the status code #1334 : Writing the response code before encoding the payload is prone to error. If JSON encoding fails, the response code is already committed, the server is able to only modify the response body to reflect the error and the user receives an awkward response where the status is successful but the body reports an error. Instead - set the desired code on `c.response.Status`. If writing eventually takes place, the desired code is committed. If an error occurs, the server can still change the response.
This commit is contained in:
parent
e2671fe963
commit
fbb72869b3
@ -437,7 +437,7 @@ func (c *context) json(code int, i interface{}, indent string) error {
|
|||||||
enc.SetIndent("", indent)
|
enc.SetIndent("", indent)
|
||||||
}
|
}
|
||||||
c.writeContentType(MIMEApplicationJSONCharsetUTF8)
|
c.writeContentType(MIMEApplicationJSONCharsetUTF8)
|
||||||
c.response.WriteHeader(code)
|
c.response.Status = code
|
||||||
return enc.Encode(i)
|
return enc.Encode(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
@ -374,6 +375,34 @@ func TestContext(t *testing.T) {
|
|||||||
assert.Equal(0, len(c.QueryParams()))
|
assert.Equal(0, len(c.QueryParams()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContext_JSON_CommitsCustomResponseCode(t *testing.T) {
|
||||||
|
e := New()
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
c := e.NewContext(req, rec).(*context)
|
||||||
|
err := c.JSON(http.StatusCreated, user{1, "Jon Snow"})
|
||||||
|
|
||||||
|
assert := testify.New(t)
|
||||||
|
if assert.NoError(err) {
|
||||||
|
assert.Equal(http.StatusCreated, rec.Code)
|
||||||
|
assert.Equal(MIMEApplicationJSONCharsetUTF8, rec.Header().Get(HeaderContentType))
|
||||||
|
assert.Equal(userJSON+"\n", rec.Body.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContext_JSON_DoesntCommitResponseCodePrematurely(t *testing.T) {
|
||||||
|
e := New()
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
c := e.NewContext(req, rec).(*context)
|
||||||
|
err := c.JSON(http.StatusCreated, map[string]float64{"a": math.NaN()})
|
||||||
|
|
||||||
|
assert := testify.New(t)
|
||||||
|
if assert.Error(err) {
|
||||||
|
assert.False(c.response.Committed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestContextCookie(t *testing.T) {
|
func TestContextCookie(t *testing.T) {
|
||||||
e := New()
|
e := New()
|
||||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||||
|
@ -67,7 +67,10 @@ func (r *Response) WriteHeader(code int) {
|
|||||||
// Write writes the data to the connection as part of an HTTP reply.
|
// Write writes the data to the connection as part of an HTTP reply.
|
||||||
func (r *Response) Write(b []byte) (n int, err error) {
|
func (r *Response) Write(b []byte) (n int, err error) {
|
||||||
if !r.Committed {
|
if !r.Committed {
|
||||||
r.WriteHeader(http.StatusOK)
|
if r.Status == 0 {
|
||||||
|
r.Status = http.StatusOK
|
||||||
|
}
|
||||||
|
r.WriteHeader(r.Status)
|
||||||
}
|
}
|
||||||
n, err = r.Writer.Write(b)
|
n, err = r.Writer.Write(b)
|
||||||
r.Size += int64(n)
|
r.Size += int64(n)
|
||||||
|
@ -22,3 +22,22 @@ func TestResponse(t *testing.T) {
|
|||||||
res.Write([]byte("test"))
|
res.Write([]byte("test"))
|
||||||
assert.Equal(t, "echo", rec.Header().Get(HeaderServer))
|
assert.Equal(t, "echo", rec.Header().Get(HeaderServer))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResponse_Write_FallsBackToDefaultStatus(t *testing.T) {
|
||||||
|
e := New()
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
res := &Response{echo: e, Writer: rec}
|
||||||
|
|
||||||
|
res.Write([]byte("test"))
|
||||||
|
assert.Equal(t, http.StatusOK, rec.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResponse_Write_UsesSetResponseCode(t *testing.T) {
|
||||||
|
e := New()
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
res := &Response{echo: e, Writer: rec}
|
||||||
|
|
||||||
|
res.Status = http.StatusBadRequest
|
||||||
|
res.Write([]byte("test"))
|
||||||
|
assert.Equal(t, http.StatusBadRequest, rec.Code)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user