return err instead of panic in JSON Render

This commit is contained in:
Tevic 2022-01-26 11:16:09 +08:00
parent 580e7da6ee
commit 4cfab1be20
5 changed files with 101 additions and 117 deletions

View File

@ -195,9 +195,9 @@ func (c *Context) AbortWithStatus(code int) {
// AbortWithStatusJSON calls `Abort()` and then `JSON` internally.
// This method stops the chain, writes the status code and return a JSON body.
// It also sets the Content-Type as "application/json".
func (c *Context) AbortWithStatusJSON(code int, jsonObj interface{}) {
func (c *Context) AbortWithStatusJSON(code int, jsonObj interface{}) error {
c.Abort()
c.JSON(code, jsonObj)
return c.JSON(code, jsonObj)
}
// AbortWithError calls `AbortWithStatus()` and `Error()` internally.
@ -884,97 +884,94 @@ func (c *Context) Cookie(name string) (string, error) {
}
// Render writes the response headers and calls render.Render to render data.
func (c *Context) Render(code int, r render.Render) {
func (c *Context) Render(code int, r render.Render) error {
c.Status(code)
if !bodyAllowedForStatus(code) {
r.WriteContentType(c.Writer)
c.Writer.WriteHeaderNow()
return
return nil
}
if err := r.Render(c.Writer); err != nil {
panic(err)
}
return r.Render(c.Writer)
}
// HTML renders the HTTP template specified by its file name.
// It also updates the HTTP code and sets the Content-Type as "text/html".
// See http://golang.org/doc/articles/wiki/
func (c *Context) HTML(code int, name string, obj interface{}) {
func (c *Context) HTML(code int, name string, obj interface{}) error {
instance := c.engine.HTMLRender.Instance(name, obj)
c.Render(code, instance)
return c.Render(code, instance)
}
// IndentedJSON serializes the given struct as pretty JSON (indented + endlines) into the response body.
// It also sets the Content-Type as "application/json".
// WARNING: we recommend using this only for development purposes since printing pretty JSON is
// more CPU and bandwidth consuming. Use Context.JSON() instead.
func (c *Context) IndentedJSON(code int, obj interface{}) {
c.Render(code, render.IndentedJSON{Data: obj})
func (c *Context) IndentedJSON(code int, obj interface{}) error {
return c.Render(code, render.IndentedJSON{Data: obj})
}
// SecureJSON serializes the given struct as Secure JSON into the response body.
// Default prepends "while(1)," to response body if the given struct is array values.
// It also sets the Content-Type as "application/json".
func (c *Context) SecureJSON(code int, obj interface{}) {
c.Render(code, render.SecureJSON{Prefix: c.engine.secureJSONPrefix, Data: obj})
func (c *Context) SecureJSON(code int, obj interface{}) error {
return c.Render(code, render.SecureJSON{Prefix: c.engine.secureJSONPrefix, Data: obj})
}
// JSONP serializes the given struct as JSON into the response body.
// It adds padding to response body to request data from a server residing in a different domain than the client.
// It also sets the Content-Type as "application/javascript".
func (c *Context) JSONP(code int, obj interface{}) {
func (c *Context) JSONP(code int, obj interface{}) error {
callback := c.DefaultQuery("callback", "")
if callback == "" {
c.Render(code, render.JSON{Data: obj})
return
return c.Render(code, render.JSON{Data: obj})
}
c.Render(code, render.JsonpJSON{Callback: callback, Data: obj})
return c.Render(code, render.JsonpJSON{Callback: callback, Data: obj})
}
// 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.Render(code, render.JSON{Data: obj})
func (c *Context) JSON(code int, obj interface{}) error {
return c.Render(code, render.JSON{Data: obj})
}
// AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string.
// It also sets the Content-Type as "application/json".
func (c *Context) AsciiJSON(code int, obj interface{}) {
c.Render(code, render.AsciiJSON{Data: obj})
func (c *Context) AsciiJSON(code int, obj interface{}) error {
return c.Render(code, render.AsciiJSON{Data: obj})
}
// PureJSON serializes the given struct as JSON into the response body.
// PureJSON, unlike JSON, does not replace special html characters with their unicode entities.
func (c *Context) PureJSON(code int, obj interface{}) {
c.Render(code, render.PureJSON{Data: obj})
func (c *Context) PureJSON(code int, obj interface{}) error {
return c.Render(code, render.PureJSON{Data: obj})
}
// XML serializes the given struct as XML into the response body.
// It also sets the Content-Type as "application/xml".
func (c *Context) XML(code int, obj interface{}) {
c.Render(code, render.XML{Data: obj})
func (c *Context) XML(code int, obj interface{}) error {
return c.Render(code, render.XML{Data: obj})
}
// YAML serializes the given struct as YAML into the response body.
func (c *Context) YAML(code int, obj interface{}) {
c.Render(code, render.YAML{Data: obj})
func (c *Context) YAML(code int, obj interface{}) error {
return c.Render(code, render.YAML{Data: obj})
}
// ProtoBuf serializes the given struct as ProtoBuf into the response body.
func (c *Context) ProtoBuf(code int, obj interface{}) {
c.Render(code, render.ProtoBuf{Data: obj})
func (c *Context) ProtoBuf(code int, obj interface{}) error {
return c.Render(code, render.ProtoBuf{Data: obj})
}
// String writes the given string into the response body.
func (c *Context) String(code int, format string, values ...interface{}) {
c.Render(code, render.String{Format: format, Data: values})
func (c *Context) String(code int, format string, values ...interface{}) error {
return c.Render(code, render.String{Format: format, Data: values})
}
// Redirect returns an HTTP redirect to the specific location.
func (c *Context) Redirect(code int, location string) {
c.Render(-1, render.Redirect{
func (c *Context) Redirect(code int, location string) error {
return c.Render(-1, render.Redirect{
Code: code,
Location: location,
Request: c.Request,
@ -982,16 +979,16 @@ func (c *Context) Redirect(code int, location string) {
}
// Data writes some data into the body stream and updates the HTTP code.
func (c *Context) Data(code int, contentType string, data []byte) {
c.Render(code, render.Data{
func (c *Context) Data(code int, contentType string, data []byte) error {
return c.Render(code, render.Data{
ContentType: contentType,
Data: data,
})
}
// DataFromReader writes the specified reader into the body stream and updates the HTTP code.
func (c *Context) DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string) {
c.Render(code, render.Reader{
func (c *Context) DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string) error {
return c.Render(code, render.Reader{
Headers: extraHeaders,
ContentType: contentType,
ContentLength: contentLength,
@ -1023,8 +1020,8 @@ func (c *Context) FileAttachment(filepath, filename string) {
}
// SSEvent writes a Server-Sent Event into the body stream.
func (c *Context) SSEvent(name string, message interface{}) {
c.Render(-1, sse.Event{
func (c *Context) SSEvent(name string, message interface{}) error {
return c.Render(-1, sse.Event{
Event: name,
Data: message,
})
@ -1065,26 +1062,27 @@ type Negotiate struct {
}
// Negotiate calls different Render according to acceptable Accept format.
func (c *Context) Negotiate(code int, config Negotiate) {
func (c *Context) Negotiate(code int, config Negotiate) error {
switch c.NegotiateFormat(config.Offered...) {
case binding.MIMEJSON:
data := chooseData(config.JSONData, config.Data)
c.JSON(code, data)
return c.JSON(code, data)
case binding.MIMEHTML:
data := chooseData(config.HTMLData, config.Data)
c.HTML(code, config.HTMLName, data)
return c.HTML(code, config.HTMLName, data)
case binding.MIMEXML:
data := chooseData(config.XMLData, config.Data)
c.XML(code, data)
return c.XML(code, data)
case binding.MIMEYAML:
data := chooseData(config.YAMLData, config.Data)
c.YAML(code, data)
return c.YAML(code, data)
default:
c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server")) // nolint: errcheck
return nil
}
}

View File

@ -642,25 +642,18 @@ func TestContextBodyAllowedForStatus(t *testing.T) {
assert.True(t, true, bodyAllowedForStatus(http.StatusInternalServerError))
}
type TestPanicRender struct{}
type TestErrorRender struct{}
func (*TestPanicRender) Render(http.ResponseWriter) error {
return errors.New("TestPanicRender")
func (*TestErrorRender) Render(http.ResponseWriter) error {
return errors.New("TestErrorRender")
}
func (*TestPanicRender) WriteContentType(http.ResponseWriter) {}
func (*TestErrorRender) WriteContentType(http.ResponseWriter) {}
func TestContextRenderPanicIfErr(t *testing.T) {
defer func() {
r := recover()
assert.Equal(t, "TestPanicRender", fmt.Sprint(r))
}()
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
c.Render(http.StatusOK, &TestPanicRender{})
assert.Fail(t, "Panic not detected")
assert.Error(t, c.Render(http.StatusOK, &TestErrorRender{}))
}
// Tests that the response is serialized as JSON
@ -670,7 +663,7 @@ func TestContextRenderJSON(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
c.JSON(http.StatusCreated, H{"foo": "bar", "html": "<b>"})
assert.NoError(t, c.JSON(http.StatusCreated, H{"foo": "bar", "html": "<b>"}))
assert.Equal(t, http.StatusCreated, w.Code)
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String())
@ -684,7 +677,7 @@ func TestContextRenderJSONP(t *testing.T) {
c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("GET", "http://example.com/?callback=x", nil)
c.JSONP(http.StatusCreated, H{"foo": "bar"})
assert.NoError(t, c.JSONP(http.StatusCreated, H{"foo": "bar"}))
assert.Equal(t, http.StatusCreated, w.Code)
assert.Equal(t, "x({\"foo\":\"bar\"});", w.Body.String())
@ -698,7 +691,7 @@ func TestContextRenderJSONPWithoutCallback(t *testing.T) {
c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("GET", "http://example.com", nil)
c.JSONP(http.StatusCreated, H{"foo": "bar"})
assert.NoError(t, c.JSONP(http.StatusCreated, H{"foo": "bar"}))
assert.Equal(t, http.StatusCreated, w.Code)
assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String())
@ -750,8 +743,7 @@ func TestContextRenderIndentedJSON(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
c.IndentedJSON(http.StatusCreated, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}})
assert.NoError(t, c.IndentedJSON(http.StatusCreated, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}}))
assert.Equal(t, http.StatusCreated, w.Code)
assert.Equal(t, "{\n \"bar\": \"foo\",\n \"foo\": \"bar\",\n \"nested\": {\n \"foo\": \"bar\"\n }\n}", w.Body.String())
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
@ -762,7 +754,7 @@ func TestContextRenderNoContentIndentedJSON(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
c.IndentedJSON(http.StatusNoContent, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}})
assert.NoError(t, c.IndentedJSON(http.StatusNoContent, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}}))
assert.Equal(t, http.StatusNoContent, w.Code)
assert.Empty(t, w.Body.String())
@ -776,7 +768,7 @@ func TestContextRenderSecureJSON(t *testing.T) {
c, router := CreateTestContext(w)
router.SecureJsonPrefix("&&&START&&&")
c.SecureJSON(http.StatusCreated, []string{"foo", "bar"})
assert.NoError(t, c.SecureJSON(http.StatusCreated, []string{"foo", "bar"}))
assert.Equal(t, http.StatusCreated, w.Code)
assert.Equal(t, "&&&START&&&[\"foo\",\"bar\"]", w.Body.String())
@ -788,7 +780,7 @@ func TestContextRenderNoContentSecureJSON(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
c.SecureJSON(http.StatusNoContent, []string{"foo", "bar"})
assert.NoError(t, c.SecureJSON(http.StatusNoContent, []string{"foo", "bar"}))
assert.Equal(t, http.StatusNoContent, w.Code)
assert.Empty(t, w.Body.String())
@ -799,7 +791,7 @@ func TestContextRenderNoContentAsciiJSON(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
c.AsciiJSON(http.StatusNoContent, []string{"lang", "Go语言"})
assert.NoError(t, c.AsciiJSON(http.StatusNoContent, []string{"lang", "Go语言"}))
assert.Equal(t, http.StatusNoContent, w.Code)
assert.Empty(t, w.Body.String())
@ -812,7 +804,7 @@ func TestContextRenderNoContentAsciiJSON(t *testing.T) {
func TestContextRenderPureJSON(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
c.PureJSON(http.StatusCreated, H{"foo": "bar", "html": "<b>"})
assert.NoError(t, c.PureJSON(http.StatusCreated, H{"foo": "bar", "html": "<b>"}))
assert.Equal(t, http.StatusCreated, w.Code)
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"<b>\"}\n", w.Body.String())
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
@ -827,7 +819,7 @@ func TestContextRenderHTML(t *testing.T) {
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
router.SetHTMLTemplate(templ)
c.HTML(http.StatusCreated, "t", H{"name": "alexandernyquist"})
assert.NoError(t, c.HTML(http.StatusCreated, "t", H{"name": "alexandernyquist"}))
assert.Equal(t, http.StatusCreated, w.Code)
assert.Equal(t, "Hello alexandernyquist", w.Body.String())
@ -851,7 +843,7 @@ func TestContextRenderHTML2(t *testing.T) {
assert.Equal(t, "[GIN-debug] [WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called\nat initialization. ie. before any route is registered or the router is listening in a socket:\n\n\trouter := gin.Default()\n\trouter.SetHTMLTemplate(template) // << good place\n\n", re)
c.HTML(http.StatusCreated, "t", H{"name": "alexandernyquist"})
assert.NoError(t, c.HTML(http.StatusCreated, "t", H{"name": "alexandernyquist"}))
assert.Equal(t, http.StatusCreated, w.Code)
assert.Equal(t, "Hello alexandernyquist", w.Body.String())
@ -865,7 +857,7 @@ func TestContextRenderNoContentHTML(t *testing.T) {
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
router.SetHTMLTemplate(templ)
c.HTML(http.StatusNoContent, "t", H{"name": "alexandernyquist"})
assert.NoError(t, c.HTML(http.StatusNoContent, "t", H{"name": "alexandernyquist"}))
assert.Equal(t, http.StatusNoContent, w.Code)
assert.Empty(t, w.Body.String())
@ -878,7 +870,7 @@ func TestContextRenderXML(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
c.XML(http.StatusCreated, H{"foo": "bar"})
assert.NoError(t, c.XML(http.StatusCreated, H{"foo": "bar"}))
assert.Equal(t, http.StatusCreated, w.Code)
assert.Equal(t, "<map><foo>bar</foo></map>", w.Body.String())
@ -890,7 +882,7 @@ func TestContextRenderNoContentXML(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
c.XML(http.StatusNoContent, H{"foo": "bar"})
assert.NoError(t, c.XML(http.StatusNoContent, H{"foo": "bar"}))
assert.Equal(t, http.StatusNoContent, w.Code)
assert.Empty(t, w.Body.String())
@ -903,7 +895,7 @@ func TestContextRenderString(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
c.String(http.StatusCreated, "test %s %d", "string", 2)
assert.NoError(t, c.String(http.StatusCreated, "test %s %d", "string", 2))
assert.Equal(t, http.StatusCreated, w.Code)
assert.Equal(t, "test string 2", w.Body.String())
@ -915,7 +907,7 @@ func TestContextRenderNoContentString(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
c.String(http.StatusNoContent, "test %s %d", "string", 2)
assert.NoError(t, c.String(http.StatusNoContent, "test %s %d", "string", 2))
assert.Equal(t, http.StatusNoContent, w.Code)
assert.Empty(t, w.Body.String())
@ -929,7 +921,7 @@ func TestContextRenderHTMLString(t *testing.T) {
c, _ := CreateTestContext(w)
c.Header("Content-Type", "text/html; charset=utf-8")
c.String(http.StatusCreated, "<html>%s %d</html>", "string", 3)
assert.NoError(t, c.String(http.StatusCreated, "<html>%s %d</html>", "string", 3))
assert.Equal(t, http.StatusCreated, w.Code)
assert.Equal(t, "<html>string 3</html>", w.Body.String())
@ -942,7 +934,7 @@ func TestContextRenderNoContentHTMLString(t *testing.T) {
c, _ := CreateTestContext(w)
c.Header("Content-Type", "text/html; charset=utf-8")
c.String(http.StatusNoContent, "<html>%s %d</html>", "string", 3)
assert.NoError(t, c.String(http.StatusNoContent, "<html>%s %d</html>", "string", 3))
assert.Equal(t, http.StatusNoContent, w.Code)
assert.Empty(t, w.Body.String())
@ -955,7 +947,7 @@ func TestContextRenderData(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
c.Data(http.StatusCreated, "text/csv", []byte(`foo,bar`))
assert.NoError(t, c.Data(http.StatusCreated, "text/csv", []byte(`foo,bar`)))
assert.Equal(t, http.StatusCreated, w.Code)
assert.Equal(t, "foo,bar", w.Body.String())
@ -967,7 +959,7 @@ func TestContextRenderNoContentData(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
c.Data(http.StatusNoContent, "text/csv", []byte(`foo,bar`))
assert.NoError(t, c.Data(http.StatusNoContent, "text/csv", []byte(`foo,bar`)))
assert.Equal(t, http.StatusNoContent, w.Code)
assert.Empty(t, w.Body.String())
@ -978,15 +970,15 @@ func TestContextRenderSSE(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
c.SSEvent("float", 1.5)
c.Render(-1, sse.Event{
assert.NoError(t, c.SSEvent("float", 1.5))
assert.NoError(t, c.Render(-1, sse.Event{
Id: "123",
Data: "text",
})
c.SSEvent("chat", H{
}))
assert.NoError(t, c.SSEvent("chat", H{
"foo": "bar",
"bar": "foo",
})
}))
assert.Equal(t, strings.Replace(w.Body.String(), " ", "", -1), strings.Replace("event:float\ndata:1.5\n\nid:123\ndata:text\n\nevent:chat\ndata:{\"bar\":\"foo\",\"foo\":\"bar\"}\n\n", " ", "", -1))
}
@ -1039,7 +1031,7 @@ func TestContextRenderYAML(t *testing.T) {
w := httptest.NewRecorder()
c, _ := CreateTestContext(w)
c.YAML(http.StatusCreated, H{"foo": "bar"})
assert.NoError(t, c.YAML(http.StatusCreated, H{"foo": "bar"}))
assert.Equal(t, http.StatusCreated, w.Code)
assert.Equal(t, "foo: bar\n", w.Body.String())
@ -1060,7 +1052,7 @@ func TestContextRenderProtoBuf(t *testing.T) {
Reps: reps,
}
c.ProtoBuf(http.StatusCreated, data)
assert.NoError(t, c.ProtoBuf(http.StatusCreated, data))
protoData, err := proto.Marshal(data)
assert.NoError(t, err)
@ -1092,10 +1084,10 @@ func TestContextRenderRedirectWithRelativePath(t *testing.T) {
c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
assert.Panics(t, func() { c.Redirect(299, "/new_path") })
assert.Panics(t, func() { c.Redirect(309, "/new_path") })
assert.Error(t, c.Redirect(299, "/new_path"))
assert.Error(t, c.Redirect(309, "/new_path"))
c.Redirect(http.StatusMovedPermanently, "/path")
assert.NoError(t, c.Redirect(http.StatusMovedPermanently, "/path"))
c.Writer.WriteHeaderNow()
assert.Equal(t, http.StatusMovedPermanently, w.Code)
assert.Equal(t, "/path", w.Header().Get("Location"))
@ -1106,7 +1098,7 @@ func TestContextRenderRedirectWithAbsolutePath(t *testing.T) {
c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
c.Redirect(http.StatusFound, "http://google.com")
assert.NoError(t, c.Redirect(http.StatusFound, "http://google.com"))
c.Writer.WriteHeaderNow()
assert.Equal(t, http.StatusFound, w.Code)
@ -1118,7 +1110,7 @@ func TestContextRenderRedirectWith201(t *testing.T) {
c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
c.Redirect(http.StatusCreated, "/resource")
assert.NoError(t, c.Redirect(http.StatusCreated, "/resource"))
c.Writer.WriteHeaderNow()
assert.Equal(t, http.StatusCreated, w.Code)
@ -1128,12 +1120,12 @@ func TestContextRenderRedirectWith201(t *testing.T) {
func TestContextRenderRedirectAll(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
assert.Panics(t, func() { c.Redirect(http.StatusOK, "/resource") })
assert.Panics(t, func() { c.Redirect(http.StatusAccepted, "/resource") })
assert.Panics(t, func() { c.Redirect(299, "/resource") })
assert.Panics(t, func() { c.Redirect(309, "/resource") })
assert.NotPanics(t, func() { c.Redirect(http.StatusMultipleChoices, "/resource") })
assert.NotPanics(t, func() { c.Redirect(http.StatusPermanentRedirect, "/resource") })
assert.Error(t, c.Redirect(http.StatusOK, "/resource"))
assert.Error(t, c.Redirect(http.StatusAccepted, "/resource"))
assert.Error(t, c.Redirect(299, "/resource"))
assert.Error(t, c.Redirect(309, "/resource"))
assert.NoError(t, c.Redirect(http.StatusMultipleChoices, "/resource"))
assert.NoError(t, c.Redirect(http.StatusPermanentRedirect, "/resource"))
}
func TestContextNegotiationWithJSON(t *testing.T) {
@ -1141,10 +1133,10 @@ func TestContextNegotiationWithJSON(t *testing.T) {
c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "", nil)
c.Negotiate(http.StatusOK, Negotiate{
assert.NoError(t, c.Negotiate(http.StatusOK, Negotiate{
Offered: []string{MIMEJSON, MIMEXML, MIMEYAML},
Data: H{"foo": "bar"},
})
}))
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String())
@ -1156,10 +1148,10 @@ func TestContextNegotiationWithXML(t *testing.T) {
c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "", nil)
c.Negotiate(http.StatusOK, Negotiate{
assert.NoError(t, c.Negotiate(http.StatusOK, Negotiate{
Offered: []string{MIMEXML, MIMEJSON, MIMEYAML},
Data: H{"foo": "bar"},
})
}))
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "<map><foo>bar</foo></map>", w.Body.String())
@ -1173,11 +1165,11 @@ func TestContextNegotiationWithHTML(t *testing.T) {
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
router.SetHTMLTemplate(templ)
c.Negotiate(http.StatusOK, Negotiate{
assert.NoError(t, c.Negotiate(http.StatusOK, Negotiate{
Offered: []string{MIMEHTML},
Data: H{"name": "gin"},
HTMLName: "t",
})
}))
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "Hello gin", w.Body.String())
@ -1189,9 +1181,9 @@ func TestContextNegotiationNotSupport(t *testing.T) {
c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "", nil)
c.Negotiate(http.StatusOK, Negotiate{
assert.NoError(t, c.Negotiate(http.StatusOK, Negotiate{
Offered: []string{MIMEPOSTForm},
})
}))
assert.Equal(t, http.StatusNotAcceptable, w.Code)
assert.Equal(t, c.index, abortIndex)
@ -1297,7 +1289,7 @@ func TestContextAbortWithStatusJSON(t *testing.T) {
in.Bar = "barValue"
in.Foo = "fooValue"
c.AbortWithStatusJSON(http.StatusUnsupportedMediaType, in)
assert.NoError(t, c.AbortWithStatusJSON(http.StatusUnsupportedMediaType, in))
assert.Equal(t, abortIndex, c.index)
assert.Equal(t, http.StatusUnsupportedMediaType, c.Writer.Status())
@ -1925,7 +1917,7 @@ func TestContextRenderDataFromReader(t *testing.T) {
contentType := "image/png"
extraHeaders := map[string]string{"Content-Disposition": `attachment; filename="gopher.png"`}
c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)
assert.NoError(t, c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders))
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, body, w.Body.String())
@ -1943,7 +1935,7 @@ func TestContextRenderDataFromReaderNoHeaders(t *testing.T) {
contentLength := int64(len(body))
contentType := "image/png"
c.DataFromReader(http.StatusOK, contentLength, contentType, reader, nil)
assert.NoError(t, c.DataFromReader(http.StatusOK, contentLength, contentType, reader, nil))
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, body, w.Body.String())

View File

@ -54,10 +54,7 @@ var (
// Render (JSON) writes data with custom ContentType.
func (r JSON) Render(w http.ResponseWriter) (err error) {
if err = WriteJSON(w, r.Data); err != nil {
panic(err)
}
return
return WriteJSON(w, r.Data)
}
// WriteContentType (JSON) writes JSON ContentType.

View File

@ -19,7 +19,7 @@ type Redirect struct {
// Render (Redirect) redirects the http request to new location and writes redirect response.
func (r Redirect) Render(w http.ResponseWriter) error {
if (r.Code < http.StatusMultipleChoices || r.Code > http.StatusPermanentRedirect) && r.Code != http.StatusCreated {
panic(fmt.Sprintf("Cannot redirect with status code %d", r.Code))
return fmt.Errorf("Cannot redirect with status code %d", r.Code)
}
http.Redirect(w, r.Request, r.Location, r.Code)
return nil

View File

@ -39,12 +39,12 @@ func TestRenderJSON(t *testing.T) {
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
}
func TestRenderJSONPanics(t *testing.T) {
func TestRenderJSONError(t *testing.T) {
w := httptest.NewRecorder()
data := make(chan int)
// json: unsupported type: chan int
assert.Panics(t, func() { assert.NoError(t, (JSON{data}).Render(w)) })
assert.Error(t, (JSON{data}).Render(w))
}
func TestRenderIndentedJSON(t *testing.T) {
@ -320,10 +320,7 @@ func TestRenderRedirect(t *testing.T) {
}
w = httptest.NewRecorder()
assert.PanicsWithValue(t, "Cannot redirect with status code 200", func() {
err := data2.Render(w)
assert.NoError(t, err)
})
assert.EqualError(t, data2.Render(w), "Cannot redirect with status code 200")
data3 := Redirect{
Code: http.StatusCreated,