mirror of https://github.com/gin-gonic/gin.git
Fixing new errors API
This commit is contained in:
parent
306da81aaf
commit
37b6f6c179
32
context.go
32
context.go
|
@ -126,7 +126,7 @@ func (c *Context) AbortWithStatus(code int) {
|
||||||
c.Abort()
|
c.Abort()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) AbortWithError(code int, err error) *errorMsg {
|
func (c *Context) AbortWithError(code int, err error) *Error {
|
||||||
c.AbortWithStatus(code)
|
c.AbortWithStatus(code)
|
||||||
return c.Error(err)
|
return c.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -142,21 +142,19 @@ func (c *Context) IsAborted() bool {
|
||||||
// Attaches an error to the current context. The error is pushed to a list of errors.
|
// Attaches an error to the current context. The error is pushed to a list of errors.
|
||||||
// It's a good idea to call Error for each error that occurred during the resolution of a request.
|
// It's a good idea to call Error for each error that occurred during the resolution of a request.
|
||||||
// A middleware can be used to collect all the errors and push them to a database together, print a log, or append it in the HTTP response.
|
// A middleware can be used to collect all the errors and push them to a database together, print a log, or append it in the HTTP response.
|
||||||
func (c *Context) Error(err error) *errorMsg {
|
func (c *Context) Error(err error) *Error {
|
||||||
newError := &errorMsg{
|
var parsedError *Error
|
||||||
Err: err,
|
switch err.(type) {
|
||||||
Flags: ErrorTypePrivate,
|
case *Error:
|
||||||
|
parsedError = err.(*Error)
|
||||||
|
default:
|
||||||
|
parsedError = &Error{
|
||||||
|
Err: err,
|
||||||
|
Type: ErrorTypePrivate,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c.Errors = append(c.Errors, newError)
|
c.Errors = append(c.Errors, parsedError)
|
||||||
return newError
|
return parsedError
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) LastError() error {
|
|
||||||
nuErrors := len(c.Errors)
|
|
||||||
if nuErrors > 0 {
|
|
||||||
return c.Errors[nuErrors-1].Err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/************************************/
|
/************************************/
|
||||||
|
@ -270,7 +268,7 @@ func (c *Context) BindJSON(obj interface{}) error {
|
||||||
|
|
||||||
func (c *Context) BindWith(obj interface{}, b binding.Binding) error {
|
func (c *Context) BindWith(obj interface{}, b binding.Binding) error {
|
||||||
if err := b.Bind(c.Request, obj); err != nil {
|
if err := b.Bind(c.Request, obj); err != nil {
|
||||||
c.AbortWithError(400, err).Type(ErrorTypeBind)
|
c.AbortWithError(400, err).SetType(ErrorTypeBind)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -309,7 +307,7 @@ func (c *Context) Render(code int, r render.Render) {
|
||||||
c.Writer.WriteHeader(code)
|
c.Writer.WriteHeader(code)
|
||||||
if err := r.Write(c.Writer); err != nil {
|
if err := r.Write(c.Writer); err != nil {
|
||||||
debugPrintError(err)
|
debugPrintError(err)
|
||||||
c.AbortWithError(500, err).Type(ErrorTypeRender)
|
c.AbortWithError(500, err).SetType(ErrorTypeRender)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -349,56 +349,50 @@ func TestContextAbortWithStatus(t *testing.T) {
|
||||||
|
|
||||||
func TestContextError(t *testing.T) {
|
func TestContextError(t *testing.T) {
|
||||||
c, _, _ := createTestContext()
|
c, _, _ := createTestContext()
|
||||||
assert.Nil(t, c.LastError())
|
assert.Empty(t, c.Errors)
|
||||||
assert.Empty(t, c.Errors.String())
|
|
||||||
|
|
||||||
c.Error(errors.New("first error")).Meta("some data")
|
c.Error(errors.New("first error"))
|
||||||
assert.Equal(t, c.LastError().Error(), "first error")
|
|
||||||
assert.Len(t, c.Errors, 1)
|
assert.Len(t, c.Errors, 1)
|
||||||
assert.Equal(t, c.Errors.String(), "Error #01: first error\n Meta: some data\n")
|
assert.Equal(t, c.Errors.String(), "Error #01: first error\n")
|
||||||
|
|
||||||
c.Error(errors.New("second error")).Meta("some data 2")
|
c.Error(&Error{
|
||||||
assert.Equal(t, c.LastError().Error(), "second error")
|
Err: errors.New("second error"),
|
||||||
|
Meta: "some data 2",
|
||||||
|
Type: ErrorTypePublic,
|
||||||
|
})
|
||||||
assert.Len(t, c.Errors, 2)
|
assert.Len(t, c.Errors, 2)
|
||||||
assert.Equal(t, c.Errors.String(), "Error #01: first error\n Meta: some data\n"+
|
|
||||||
"Error #02: second error\n Meta: some data 2\n")
|
|
||||||
|
|
||||||
assert.Equal(t, c.Errors[0].Err, errors.New("first error"))
|
assert.Equal(t, c.Errors[0].Err, errors.New("first error"))
|
||||||
assert.Equal(t, c.Errors[0].Metadata, "some data")
|
assert.Nil(t, c.Errors[0].Meta)
|
||||||
assert.Equal(t, c.Errors[0].Flags, ErrorTypePrivate)
|
assert.Equal(t, c.Errors[0].Type, ErrorTypePrivate)
|
||||||
|
|
||||||
assert.Equal(t, c.Errors[1].Err, errors.New("second error"))
|
assert.Equal(t, c.Errors[1].Err, errors.New("second error"))
|
||||||
assert.Equal(t, c.Errors[1].Metadata, "some data 2")
|
assert.Equal(t, c.Errors[1].Meta, "some data 2")
|
||||||
assert.Equal(t, c.Errors[1].Flags, ErrorTypePrivate)
|
assert.Equal(t, c.Errors[1].Type, ErrorTypePublic)
|
||||||
|
|
||||||
|
assert.Equal(t, c.Errors.Last(), c.Errors[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContextTypedError(t *testing.T) {
|
func TestContextTypedError(t *testing.T) {
|
||||||
c, _, _ := createTestContext()
|
c, _, _ := createTestContext()
|
||||||
c.Error(errors.New("externo 0")).Type(ErrorTypePublic)
|
c.Error(errors.New("externo 0")).SetType(ErrorTypePublic)
|
||||||
c.Error(errors.New("externo 1")).Type(ErrorTypePublic)
|
c.Error(errors.New("interno 0")).SetType(ErrorTypePrivate)
|
||||||
c.Error(errors.New("interno 0")).Type(ErrorTypePrivate)
|
|
||||||
c.Error(errors.New("externo 2")).Type(ErrorTypePublic)
|
|
||||||
c.Error(errors.New("interno 1")).Type(ErrorTypePrivate)
|
|
||||||
c.Error(errors.New("interno 2")).Type(ErrorTypePrivate)
|
|
||||||
|
|
||||||
for _, err := range c.Errors.ByType(ErrorTypePublic) {
|
for _, err := range c.Errors.ByType(ErrorTypePublic) {
|
||||||
assert.Equal(t, err.Flags, ErrorTypePublic)
|
assert.Equal(t, err.Type, ErrorTypePublic)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, err := range c.Errors.ByType(ErrorTypePrivate) {
|
for _, err := range c.Errors.ByType(ErrorTypePrivate) {
|
||||||
assert.Equal(t, err.Flags, ErrorTypePrivate)
|
assert.Equal(t, err.Type, ErrorTypePrivate)
|
||||||
}
|
}
|
||||||
|
assert.Equal(t, c.Errors.Errors(), []string{"externo 0", "interno 0"})
|
||||||
assert.Equal(t, c.Errors.Errors(), []string{"externo 0", "externo 1", "interno 0", "externo 2", "interno 1", "interno 2"})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContextFail(t *testing.T) {
|
func TestContextAbortWithError(t *testing.T) {
|
||||||
c, w, _ := createTestContext()
|
c, w, _ := createTestContext()
|
||||||
c.AbortWithError(401, errors.New("bad input"))
|
c.AbortWithError(401, errors.New("bad input")).SetMeta("some input")
|
||||||
c.Writer.WriteHeaderNow()
|
c.Writer.WriteHeaderNow()
|
||||||
|
|
||||||
assert.Equal(t, w.Code, 401)
|
assert.Equal(t, w.Code, 401)
|
||||||
assert.Equal(t, c.LastError().Error(), "bad input")
|
|
||||||
assert.Equal(t, c.index, AbortIndex)
|
assert.Equal(t, c.index, AbortIndex)
|
||||||
assert.True(t, c.IsAborted())
|
assert.True(t, c.IsAborted())
|
||||||
}
|
}
|
||||||
|
|
41
errors.go
41
errors.go
|
@ -21,33 +21,37 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Used internally to collect errors that occurred during an http request.
|
// Used internally to collect errors that occurred during an http request.
|
||||||
type errorMsg struct {
|
type Error struct {
|
||||||
Err error `json:"error"`
|
Err error `json:"error"`
|
||||||
Flags int `json:"-"`
|
Type int `json:"-"`
|
||||||
Metadata interface{} `json:"meta"`
|
Meta interface{} `json:"meta"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *errorMsg) Type(flags int) *errorMsg {
|
var _ error = &Error{}
|
||||||
msg.Flags = flags
|
|
||||||
|
func (msg *Error) SetType(flags int) *Error {
|
||||||
|
msg.Type = flags
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *errorMsg) Meta(data interface{}) *errorMsg {
|
func (msg *Error) SetMeta(data interface{}) *Error {
|
||||||
msg.Metadata = data
|
msg.Meta = data
|
||||||
return msg
|
return msg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *errorMsg) JSON() interface{} {
|
func (msg *Error) JSON() interface{} {
|
||||||
json := H{}
|
json := H{}
|
||||||
if msg.Metadata != nil {
|
if msg.Meta != nil {
|
||||||
value := reflect.ValueOf(msg.Metadata)
|
value := reflect.ValueOf(msg.Meta)
|
||||||
switch value.Kind() {
|
switch value.Kind() {
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
return msg.Metadata
|
return msg.Meta
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
for _, key := range value.MapKeys() {
|
for _, key := range value.MapKeys() {
|
||||||
json[key.String()] = value.MapIndex(key).Interface()
|
json[key.String()] = value.MapIndex(key).Interface()
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
json["meta"] = msg.Meta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if _, ok := json["error"]; !ok {
|
if _, ok := json["error"]; !ok {
|
||||||
|
@ -56,11 +60,11 @@ func (msg *errorMsg) JSON() interface{} {
|
||||||
return json
|
return json
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *errorMsg) Error() string {
|
func (msg *Error) Error() string {
|
||||||
return msg.Err.Error()
|
return msg.Err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
type errorMsgs []*errorMsg
|
type errorMsgs []*Error
|
||||||
|
|
||||||
func (a errorMsgs) ByType(typ int) errorMsgs {
|
func (a errorMsgs) ByType(typ int) errorMsgs {
|
||||||
if len(a) == 0 {
|
if len(a) == 0 {
|
||||||
|
@ -68,14 +72,14 @@ func (a errorMsgs) ByType(typ int) errorMsgs {
|
||||||
}
|
}
|
||||||
result := make(errorMsgs, 0, len(a))
|
result := make(errorMsgs, 0, len(a))
|
||||||
for _, msg := range a {
|
for _, msg := range a {
|
||||||
if msg.Flags&typ > 0 {
|
if msg.Type&typ > 0 {
|
||||||
result = append(result, msg)
|
result = append(result, msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a errorMsgs) Last() *errorMsg {
|
func (a errorMsgs) Last() *Error {
|
||||||
length := len(a)
|
length := len(a)
|
||||||
if length == 0 {
|
if length == 0 {
|
||||||
return nil
|
return nil
|
||||||
|
@ -115,7 +119,10 @@ func (a errorMsgs) String() string {
|
||||||
}
|
}
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
for i, msg := range a {
|
for i, msg := range a {
|
||||||
fmt.Fprintf(&buffer, "Error #%02d: %s\n Meta: %v\n", (i + 1), msg.Err, msg.Metadata)
|
fmt.Fprintf(&buffer, "Error #%02d: %s\n", (i + 1), msg.Err)
|
||||||
|
if msg.Meta != nil {
|
||||||
|
fmt.Fprintf(&buffer, " Meta: %v\n", msg.Meta)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return buffer.String()
|
return buffer.String()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestError(t *testing.T) {
|
||||||
|
baseError := errors.New("test error")
|
||||||
|
err := &Error{
|
||||||
|
Err: baseError,
|
||||||
|
Type: ErrorTypePrivate,
|
||||||
|
}
|
||||||
|
assert.Equal(t, err.Error(), baseError.Error())
|
||||||
|
assert.Equal(t, err.JSON(), H{"error": baseError.Error()})
|
||||||
|
|
||||||
|
assert.Equal(t, err.SetType(ErrorTypePublic), err)
|
||||||
|
assert.Equal(t, err.Type, ErrorTypePublic)
|
||||||
|
|
||||||
|
assert.Equal(t, err.SetMeta("some data"), err)
|
||||||
|
assert.Equal(t, err.Meta, "some data")
|
||||||
|
assert.Equal(t, err.JSON(), H{
|
||||||
|
"error": baseError.Error(),
|
||||||
|
"meta": "some data",
|
||||||
|
})
|
||||||
|
|
||||||
|
err.SetMeta(H{
|
||||||
|
"status": "200",
|
||||||
|
"data": "some data",
|
||||||
|
})
|
||||||
|
assert.Equal(t, err.JSON(), H{
|
||||||
|
"error": baseError.Error(),
|
||||||
|
"status": "200",
|
||||||
|
"data": "some data",
|
||||||
|
})
|
||||||
|
|
||||||
|
err.SetMeta(H{
|
||||||
|
"error": "custom error",
|
||||||
|
"status": "200",
|
||||||
|
"data": "some data",
|
||||||
|
})
|
||||||
|
assert.Equal(t, err.JSON(), H{
|
||||||
|
"error": "custom error",
|
||||||
|
"status": "200",
|
||||||
|
"data": "some data",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorSlice(t *testing.T) {
|
||||||
|
errs := errorMsgs{
|
||||||
|
{Err: errors.New("first"), Type: ErrorTypePrivate},
|
||||||
|
{Err: errors.New("second"), Type: ErrorTypePrivate, Meta: "some data"},
|
||||||
|
{Err: errors.New("third"), Type: ErrorTypePublic, Meta: H{"status": "400"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, errs.Last().Error(), "third")
|
||||||
|
assert.Equal(t, errs.Errors(), []string{"first", "second", "third"})
|
||||||
|
assert.Equal(t, errs.ByType(ErrorTypePublic).Errors(), []string{"third"})
|
||||||
|
assert.Equal(t, errs.ByType(ErrorTypePrivate).Errors(), []string{"first", "second"})
|
||||||
|
assert.Equal(t, errs.ByType(ErrorTypePublic|ErrorTypePrivate).Errors(), []string{"first", "second", "third"})
|
||||||
|
assert.Empty(t, errs.ByType(ErrorTypeBind))
|
||||||
|
|
||||||
|
assert.Equal(t, errs.String(), `Error #01: first
|
||||||
|
Error #02: second
|
||||||
|
Meta: some data
|
||||||
|
Error #03: third
|
||||||
|
Meta: map[status:400]
|
||||||
|
`)
|
||||||
|
assert.Equal(t, errs.JSON(), []interface{}{
|
||||||
|
H{"error": "first"},
|
||||||
|
H{"error": "second", "meta": "some data"},
|
||||||
|
H{"error": "third", "status": "400"},
|
||||||
|
})
|
||||||
|
|
||||||
|
errs = errorMsgs{
|
||||||
|
{Err: errors.New("first"), Type: ErrorTypePrivate},
|
||||||
|
}
|
||||||
|
assert.Equal(t, errs.JSON(), H{"error": "first"})
|
||||||
|
|
||||||
|
errs = errorMsgs{}
|
||||||
|
assert.Nil(t, errs.Last())
|
||||||
|
assert.Nil(t, errs.JSON())
|
||||||
|
assert.Empty(t, errs.String())
|
||||||
|
}
|
Loading…
Reference in New Issue