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. // AbortWithStatusJSON calls `Abort()` and then `JSON` internally.
// This method stops the chain, writes the status code and return a JSON body. // This method stops the chain, writes the status code and return a JSON body.
// It also sets the Content-Type as "application/json". // 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.Abort()
c.JSON(code, jsonObj) return c.JSON(code, jsonObj)
} }
// AbortWithError calls `AbortWithStatus()` and `Error()` internally. // 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. // 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) c.Status(code)
if !bodyAllowedForStatus(code) { if !bodyAllowedForStatus(code) {
r.WriteContentType(c.Writer) r.WriteContentType(c.Writer)
c.Writer.WriteHeaderNow() c.Writer.WriteHeaderNow()
return return nil
} }
if err := r.Render(c.Writer); err != nil { return r.Render(c.Writer)
panic(err)
}
} }
// HTML renders the HTTP template specified by its file name. // HTML renders the HTTP template specified by its file name.
// It also updates the HTTP code and sets the Content-Type as "text/html". // It also updates the HTTP code and sets the Content-Type as "text/html".
// See http://golang.org/doc/articles/wiki/ // 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) 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. // IndentedJSON serializes the given struct as pretty JSON (indented + endlines) into the response body.
// It also sets the Content-Type as "application/json". // It also sets the Content-Type as "application/json".
// WARNING: we recommend using this only for development purposes since printing pretty JSON is // WARNING: we recommend using this only for development purposes since printing pretty JSON is
// more CPU and bandwidth consuming. Use Context.JSON() instead. // more CPU and bandwidth consuming. Use Context.JSON() instead.
func (c *Context) IndentedJSON(code int, obj interface{}) { func (c *Context) IndentedJSON(code int, obj interface{}) error {
c.Render(code, render.IndentedJSON{Data: obj}) return c.Render(code, render.IndentedJSON{Data: obj})
} }
// SecureJSON serializes the given struct as Secure JSON into the response body. // 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. // Default prepends "while(1)," to response body if the given struct is array values.
// It also sets the Content-Type as "application/json". // It also sets the Content-Type as "application/json".
func (c *Context) SecureJSON(code int, obj interface{}) { func (c *Context) SecureJSON(code int, obj interface{}) error {
c.Render(code, render.SecureJSON{Prefix: c.engine.secureJSONPrefix, Data: obj}) return c.Render(code, render.SecureJSON{Prefix: c.engine.secureJSONPrefix, Data: obj})
} }
// JSONP serializes the given struct as JSON into the response body. // 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 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". // 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", "") callback := c.DefaultQuery("callback", "")
if callback == "" { if callback == "" {
c.Render(code, render.JSON{Data: obj}) return c.Render(code, render.JSON{Data: obj})
return
} }
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. // JSON serializes the given struct as JSON into the response body.
// It also sets the Content-Type as "application/json". // It also sets the Content-Type as "application/json".
func (c *Context) JSON(code int, obj interface{}) { func (c *Context) JSON(code int, obj interface{}) error {
c.Render(code, render.JSON{Data: obj}) return c.Render(code, render.JSON{Data: obj})
} }
// AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string. // 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". // It also sets the Content-Type as "application/json".
func (c *Context) AsciiJSON(code int, obj interface{}) { func (c *Context) AsciiJSON(code int, obj interface{}) error {
c.Render(code, render.AsciiJSON{Data: obj}) return c.Render(code, render.AsciiJSON{Data: obj})
} }
// PureJSON serializes the given struct as JSON into the response body. // PureJSON serializes the given struct as JSON into the response body.
// PureJSON, unlike JSON, does not replace special html characters with their unicode entities. // PureJSON, unlike JSON, does not replace special html characters with their unicode entities.
func (c *Context) PureJSON(code int, obj interface{}) { func (c *Context) PureJSON(code int, obj interface{}) error {
c.Render(code, render.PureJSON{Data: obj}) return c.Render(code, render.PureJSON{Data: obj})
} }
// XML serializes the given struct as XML into the response body. // XML serializes the given struct as XML into the response body.
// It also sets the Content-Type as "application/xml". // It also sets the Content-Type as "application/xml".
func (c *Context) XML(code int, obj interface{}) { func (c *Context) XML(code int, obj interface{}) error {
c.Render(code, render.XML{Data: obj}) return c.Render(code, render.XML{Data: obj})
} }
// YAML serializes the given struct as YAML into the response body. // YAML serializes the given struct as YAML into the response body.
func (c *Context) YAML(code int, obj interface{}) { func (c *Context) YAML(code int, obj interface{}) error {
c.Render(code, render.YAML{Data: obj}) return c.Render(code, render.YAML{Data: obj})
} }
// ProtoBuf serializes the given struct as ProtoBuf into the response body. // ProtoBuf serializes the given struct as ProtoBuf into the response body.
func (c *Context) ProtoBuf(code int, obj interface{}) { func (c *Context) ProtoBuf(code int, obj interface{}) error {
c.Render(code, render.ProtoBuf{Data: obj}) return 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{}) error {
c.Render(code, render.String{Format: format, Data: values}) return c.Render(code, render.String{Format: format, Data: values})
} }
// Redirect returns an HTTP redirect to the specific location. // Redirect returns an HTTP redirect to the specific location.
func (c *Context) Redirect(code int, location string) { func (c *Context) Redirect(code int, location string) error {
c.Render(-1, render.Redirect{ return c.Render(-1, render.Redirect{
Code: code, Code: code,
Location: location, Location: location,
Request: c.Request, 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. // Data writes some data into the body stream and updates the HTTP code.
func (c *Context) Data(code int, contentType string, data []byte) { func (c *Context) Data(code int, contentType string, data []byte) error {
c.Render(code, render.Data{ return c.Render(code, render.Data{
ContentType: contentType, ContentType: contentType,
Data: data, Data: data,
}) })
} }
// DataFromReader writes the specified reader into the body stream and updates the HTTP code. // 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) { func (c *Context) DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string) error {
c.Render(code, render.Reader{ return c.Render(code, render.Reader{
Headers: extraHeaders, Headers: extraHeaders,
ContentType: contentType, ContentType: contentType,
ContentLength: contentLength, ContentLength: contentLength,
@ -1023,8 +1020,8 @@ func (c *Context) FileAttachment(filepath, filename string) {
} }
// SSEvent writes a Server-Sent Event into the body stream. // SSEvent writes a Server-Sent Event into the body stream.
func (c *Context) SSEvent(name string, message interface{}) { func (c *Context) SSEvent(name string, message interface{}) error {
c.Render(-1, sse.Event{ return c.Render(-1, sse.Event{
Event: name, Event: name,
Data: message, Data: message,
}) })
@ -1065,26 +1062,27 @@ type Negotiate struct {
} }
// Negotiate calls different Render according to acceptable Accept format. // 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...) { switch c.NegotiateFormat(config.Offered...) {
case binding.MIMEJSON: case binding.MIMEJSON:
data := chooseData(config.JSONData, config.Data) data := chooseData(config.JSONData, config.Data)
c.JSON(code, data) return c.JSON(code, data)
case binding.MIMEHTML: case binding.MIMEHTML:
data := chooseData(config.HTMLData, config.Data) data := chooseData(config.HTMLData, config.Data)
c.HTML(code, config.HTMLName, data) return c.HTML(code, config.HTMLName, data)
case binding.MIMEXML: case binding.MIMEXML:
data := chooseData(config.XMLData, config.Data) data := chooseData(config.XMLData, config.Data)
c.XML(code, data) return c.XML(code, data)
case binding.MIMEYAML: case binding.MIMEYAML:
data := chooseData(config.YAMLData, config.Data) data := chooseData(config.YAMLData, config.Data)
c.YAML(code, data) return c.YAML(code, data)
default: default:
c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server")) // nolint: errcheck 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)) assert.True(t, true, bodyAllowedForStatus(http.StatusInternalServerError))
} }
type TestPanicRender struct{} type TestErrorRender struct{}
func (*TestPanicRender) Render(http.ResponseWriter) error { func (*TestErrorRender) Render(http.ResponseWriter) error {
return errors.New("TestPanicRender") return errors.New("TestErrorRender")
} }
func (*TestPanicRender) WriteContentType(http.ResponseWriter) {} func (*TestErrorRender) WriteContentType(http.ResponseWriter) {}
func TestContextRenderPanicIfErr(t *testing.T) { func TestContextRenderPanicIfErr(t *testing.T) {
defer func() {
r := recover()
assert.Equal(t, "TestPanicRender", fmt.Sprint(r))
}()
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
assert.Error(t, c.Render(http.StatusOK, &TestErrorRender{}))
c.Render(http.StatusOK, &TestPanicRender{})
assert.Fail(t, "Panic not detected")
} }
// Tests that the response is serialized as JSON // Tests that the response is serialized as JSON
@ -670,7 +663,7 @@ func TestContextRenderJSON(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) 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, http.StatusCreated, w.Code)
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String()) assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String())
@ -684,7 +677,7 @@ func TestContextRenderJSONP(t *testing.T) {
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("GET", "http://example.com/?callback=x", nil) 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, http.StatusCreated, w.Code)
assert.Equal(t, "x({\"foo\":\"bar\"});", w.Body.String()) assert.Equal(t, "x({\"foo\":\"bar\"});", w.Body.String())
@ -698,7 +691,7 @@ func TestContextRenderJSONPWithoutCallback(t *testing.T) {
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("GET", "http://example.com", nil) 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, http.StatusCreated, w.Code)
assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String()) assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String())
@ -750,8 +743,7 @@ func TestContextRenderIndentedJSON(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) 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, 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, "{\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")) 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() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) 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.Equal(t, http.StatusNoContent, w.Code)
assert.Empty(t, w.Body.String()) assert.Empty(t, w.Body.String())
@ -776,7 +768,7 @@ func TestContextRenderSecureJSON(t *testing.T) {
c, router := CreateTestContext(w) c, router := CreateTestContext(w)
router.SecureJsonPrefix("&&&START&&&") 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, http.StatusCreated, w.Code)
assert.Equal(t, "&&&START&&&[\"foo\",\"bar\"]", w.Body.String()) assert.Equal(t, "&&&START&&&[\"foo\",\"bar\"]", w.Body.String())
@ -788,7 +780,7 @@ func TestContextRenderNoContentSecureJSON(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) 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.Equal(t, http.StatusNoContent, w.Code)
assert.Empty(t, w.Body.String()) assert.Empty(t, w.Body.String())
@ -799,7 +791,7 @@ func TestContextRenderNoContentAsciiJSON(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) 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.Equal(t, http.StatusNoContent, w.Code)
assert.Empty(t, w.Body.String()) assert.Empty(t, w.Body.String())
@ -812,7 +804,7 @@ func TestContextRenderNoContentAsciiJSON(t *testing.T) {
func TestContextRenderPureJSON(t *testing.T) { func TestContextRenderPureJSON(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) 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, http.StatusCreated, w.Code)
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"<b>\"}\n", w.Body.String()) assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"<b>\"}\n", w.Body.String())
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) 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}}`)) templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
router.SetHTMLTemplate(templ) 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, http.StatusCreated, w.Code)
assert.Equal(t, "Hello alexandernyquist", w.Body.String()) 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) 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, http.StatusCreated, w.Code)
assert.Equal(t, "Hello alexandernyquist", w.Body.String()) 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}}`)) templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
router.SetHTMLTemplate(templ) 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.Equal(t, http.StatusNoContent, w.Code)
assert.Empty(t, w.Body.String()) assert.Empty(t, w.Body.String())
@ -878,7 +870,7 @@ func TestContextRenderXML(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) 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, http.StatusCreated, w.Code)
assert.Equal(t, "<map><foo>bar</foo></map>", w.Body.String()) assert.Equal(t, "<map><foo>bar</foo></map>", w.Body.String())
@ -890,7 +882,7 @@ func TestContextRenderNoContentXML(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) 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.Equal(t, http.StatusNoContent, w.Code)
assert.Empty(t, w.Body.String()) assert.Empty(t, w.Body.String())
@ -903,7 +895,7 @@ func TestContextRenderString(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) 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, http.StatusCreated, w.Code)
assert.Equal(t, "test string 2", w.Body.String()) assert.Equal(t, "test string 2", w.Body.String())
@ -915,7 +907,7 @@ func TestContextRenderNoContentString(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) 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.Equal(t, http.StatusNoContent, w.Code)
assert.Empty(t, w.Body.String()) assert.Empty(t, w.Body.String())
@ -929,7 +921,7 @@ func TestContextRenderHTMLString(t *testing.T) {
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Header("Content-Type", "text/html; charset=utf-8") 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, http.StatusCreated, w.Code)
assert.Equal(t, "<html>string 3</html>", w.Body.String()) assert.Equal(t, "<html>string 3</html>", w.Body.String())
@ -942,7 +934,7 @@ func TestContextRenderNoContentHTMLString(t *testing.T) {
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Header("Content-Type", "text/html; charset=utf-8") 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.Equal(t, http.StatusNoContent, w.Code)
assert.Empty(t, w.Body.String()) assert.Empty(t, w.Body.String())
@ -955,7 +947,7 @@ func TestContextRenderData(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) 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, http.StatusCreated, w.Code)
assert.Equal(t, "foo,bar", w.Body.String()) assert.Equal(t, "foo,bar", w.Body.String())
@ -967,7 +959,7 @@ func TestContextRenderNoContentData(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) 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.Equal(t, http.StatusNoContent, w.Code)
assert.Empty(t, w.Body.String()) assert.Empty(t, w.Body.String())
@ -978,15 +970,15 @@ func TestContextRenderSSE(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.SSEvent("float", 1.5) assert.NoError(t, c.SSEvent("float", 1.5))
c.Render(-1, sse.Event{ assert.NoError(t, c.Render(-1, sse.Event{
Id: "123", Id: "123",
Data: "text", Data: "text",
}) }))
c.SSEvent("chat", H{ assert.NoError(t, c.SSEvent("chat", H{
"foo": "bar", "foo": "bar",
"bar": "foo", "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)) 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() w := httptest.NewRecorder()
c, _ := CreateTestContext(w) 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, http.StatusCreated, w.Code)
assert.Equal(t, "foo: bar\n", w.Body.String()) assert.Equal(t, "foo: bar\n", w.Body.String())
@ -1060,7 +1052,7 @@ func TestContextRenderProtoBuf(t *testing.T) {
Reps: reps, Reps: reps,
} }
c.ProtoBuf(http.StatusCreated, data) assert.NoError(t, c.ProtoBuf(http.StatusCreated, data))
protoData, err := proto.Marshal(data) protoData, err := proto.Marshal(data)
assert.NoError(t, err) assert.NoError(t, err)
@ -1092,10 +1084,10 @@ func TestContextRenderRedirectWithRelativePath(t *testing.T) {
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "http://example.com", nil) c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
assert.Panics(t, func() { c.Redirect(299, "/new_path") }) assert.Error(t, c.Redirect(299, "/new_path"))
assert.Panics(t, func() { c.Redirect(309, "/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() c.Writer.WriteHeaderNow()
assert.Equal(t, http.StatusMovedPermanently, w.Code) assert.Equal(t, http.StatusMovedPermanently, w.Code)
assert.Equal(t, "/path", w.Header().Get("Location")) assert.Equal(t, "/path", w.Header().Get("Location"))
@ -1106,7 +1098,7 @@ func TestContextRenderRedirectWithAbsolutePath(t *testing.T) {
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "http://example.com", nil) 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() c.Writer.WriteHeaderNow()
assert.Equal(t, http.StatusFound, w.Code) assert.Equal(t, http.StatusFound, w.Code)
@ -1118,7 +1110,7 @@ func TestContextRenderRedirectWith201(t *testing.T) {
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "http://example.com", nil) 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() c.Writer.WriteHeaderNow()
assert.Equal(t, http.StatusCreated, w.Code) assert.Equal(t, http.StatusCreated, w.Code)
@ -1128,12 +1120,12 @@ func TestContextRenderRedirectWith201(t *testing.T) {
func TestContextRenderRedirectAll(t *testing.T) { func TestContextRenderRedirectAll(t *testing.T) {
c, _ := CreateTestContext(httptest.NewRecorder()) c, _ := CreateTestContext(httptest.NewRecorder())
c.Request, _ = http.NewRequest("POST", "http://example.com", nil) c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
assert.Panics(t, func() { c.Redirect(http.StatusOK, "/resource") }) assert.Error(t, c.Redirect(http.StatusOK, "/resource"))
assert.Panics(t, func() { c.Redirect(http.StatusAccepted, "/resource") }) assert.Error(t, c.Redirect(http.StatusAccepted, "/resource"))
assert.Panics(t, func() { c.Redirect(299, "/resource") }) assert.Error(t, c.Redirect(299, "/resource"))
assert.Panics(t, func() { c.Redirect(309, "/resource") }) assert.Error(t, c.Redirect(309, "/resource"))
assert.NotPanics(t, func() { c.Redirect(http.StatusMultipleChoices, "/resource") }) assert.NoError(t, c.Redirect(http.StatusMultipleChoices, "/resource"))
assert.NotPanics(t, func() { c.Redirect(http.StatusPermanentRedirect, "/resource") }) assert.NoError(t, c.Redirect(http.StatusPermanentRedirect, "/resource"))
} }
func TestContextNegotiationWithJSON(t *testing.T) { func TestContextNegotiationWithJSON(t *testing.T) {
@ -1141,10 +1133,10 @@ func TestContextNegotiationWithJSON(t *testing.T) {
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "", nil) c.Request, _ = http.NewRequest("POST", "", nil)
c.Negotiate(http.StatusOK, Negotiate{ assert.NoError(t, c.Negotiate(http.StatusOK, Negotiate{
Offered: []string{MIMEJSON, MIMEXML, MIMEYAML}, Offered: []string{MIMEJSON, MIMEXML, MIMEYAML},
Data: H{"foo": "bar"}, Data: H{"foo": "bar"},
}) }))
assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String()) assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String())
@ -1156,10 +1148,10 @@ func TestContextNegotiationWithXML(t *testing.T) {
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "", nil) c.Request, _ = http.NewRequest("POST", "", nil)
c.Negotiate(http.StatusOK, Negotiate{ assert.NoError(t, c.Negotiate(http.StatusOK, Negotiate{
Offered: []string{MIMEXML, MIMEJSON, MIMEYAML}, Offered: []string{MIMEXML, MIMEJSON, MIMEYAML},
Data: H{"foo": "bar"}, Data: H{"foo": "bar"},
}) }))
assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "<map><foo>bar</foo></map>", w.Body.String()) 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}}`)) templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
router.SetHTMLTemplate(templ) router.SetHTMLTemplate(templ)
c.Negotiate(http.StatusOK, Negotiate{ assert.NoError(t, c.Negotiate(http.StatusOK, Negotiate{
Offered: []string{MIMEHTML}, Offered: []string{MIMEHTML},
Data: H{"name": "gin"}, Data: H{"name": "gin"},
HTMLName: "t", HTMLName: "t",
}) }))
assert.Equal(t, http.StatusOK, w.Code) assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, "Hello gin", w.Body.String()) assert.Equal(t, "Hello gin", w.Body.String())
@ -1189,9 +1181,9 @@ func TestContextNegotiationNotSupport(t *testing.T) {
c, _ := CreateTestContext(w) c, _ := CreateTestContext(w)
c.Request, _ = http.NewRequest("POST", "", nil) c.Request, _ = http.NewRequest("POST", "", nil)
c.Negotiate(http.StatusOK, Negotiate{ assert.NoError(t, c.Negotiate(http.StatusOK, Negotiate{
Offered: []string{MIMEPOSTForm}, Offered: []string{MIMEPOSTForm},
}) }))
assert.Equal(t, http.StatusNotAcceptable, w.Code) assert.Equal(t, http.StatusNotAcceptable, w.Code)
assert.Equal(t, c.index, abortIndex) assert.Equal(t, c.index, abortIndex)
@ -1297,7 +1289,7 @@ func TestContextAbortWithStatusJSON(t *testing.T) {
in.Bar = "barValue" in.Bar = "barValue"
in.Foo = "fooValue" 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, abortIndex, c.index)
assert.Equal(t, http.StatusUnsupportedMediaType, c.Writer.Status()) assert.Equal(t, http.StatusUnsupportedMediaType, c.Writer.Status())
@ -1925,7 +1917,7 @@ func TestContextRenderDataFromReader(t *testing.T) {
contentType := "image/png" contentType := "image/png"
extraHeaders := map[string]string{"Content-Disposition": `attachment; filename="gopher.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, http.StatusOK, w.Code)
assert.Equal(t, body, w.Body.String()) assert.Equal(t, body, w.Body.String())
@ -1943,7 +1935,7 @@ func TestContextRenderDataFromReaderNoHeaders(t *testing.T) {
contentLength := int64(len(body)) contentLength := int64(len(body))
contentType := "image/png" 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, http.StatusOK, w.Code)
assert.Equal(t, body, w.Body.String()) assert.Equal(t, body, w.Body.String())

View File

@ -54,10 +54,7 @@ var (
// Render (JSON) writes data with custom ContentType. // Render (JSON) writes data with custom ContentType.
func (r JSON) Render(w http.ResponseWriter) (err error) { func (r JSON) Render(w http.ResponseWriter) (err error) {
if err = WriteJSON(w, r.Data); err != nil { return WriteJSON(w, r.Data)
panic(err)
}
return
} }
// WriteContentType (JSON) writes JSON ContentType. // 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. // Render (Redirect) redirects the http request to new location and writes redirect response.
func (r Redirect) Render(w http.ResponseWriter) error { func (r Redirect) Render(w http.ResponseWriter) error {
if (r.Code < http.StatusMultipleChoices || r.Code > http.StatusPermanentRedirect) && r.Code != http.StatusCreated { 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) http.Redirect(w, r.Request, r.Location, r.Code)
return nil 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")) 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() w := httptest.NewRecorder()
data := make(chan int) data := make(chan int)
// json: unsupported type: 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) { func TestRenderIndentedJSON(t *testing.T) {
@ -320,10 +320,7 @@ func TestRenderRedirect(t *testing.T) {
} }
w = httptest.NewRecorder() w = httptest.NewRecorder()
assert.PanicsWithValue(t, "Cannot redirect with status code 200", func() { assert.EqualError(t, data2.Render(w), "Cannot redirect with status code 200")
err := data2.Render(w)
assert.NoError(t, err)
})
data3 := Redirect{ data3 := Redirect{
Code: http.StatusCreated, Code: http.StatusCreated,