mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-17 15:36:48 +00:00
fix(binding): better error messages (#9010)
This commit is contained in:
parent
b6f5c053a0
commit
f63f175a77
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
ErrCodeInvalidRequestBody = errors.MustNewCode("invalid_request_body")
|
ErrCodeInvalidRequestBody = errors.MustNewCode("invalid_request_body")
|
||||||
|
ErrCodeInvalidRequestField = errors.MustNewCode("invalid_request_field")
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@ -7,6 +7,11 @@ import (
|
|||||||
"github.com/SigNoz/signoz/pkg/errors"
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ErrMessageInvalidJSON string = "request body contains invalid JSON, please verify the format and try again."
|
||||||
|
ErrMessageInvalidField string = "request body contains invalid field value"
|
||||||
|
)
|
||||||
|
|
||||||
var _ Binding = (*jsonBinding)(nil)
|
var _ Binding = (*jsonBinding)(nil)
|
||||||
|
|
||||||
type jsonBinding struct{}
|
type jsonBinding struct{}
|
||||||
@ -32,10 +37,30 @@ func (b *jsonBinding) BindBody(body io.Reader, obj any, opts ...BindBodyOption)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := decoder.Decode(obj); err != nil {
|
if err := decoder.Decode(obj); err != nil {
|
||||||
|
var syntaxError *json.SyntaxError
|
||||||
|
var unmarshalError *json.InvalidUnmarshalError
|
||||||
|
var unmarshalTypeError *json.UnmarshalTypeError
|
||||||
|
|
||||||
|
if errors.As(err, &syntaxError) || errors.As(err, &unmarshalError) || errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {
|
||||||
return errors.
|
return errors.
|
||||||
New(errors.TypeInvalidInput, ErrCodeInvalidRequestBody, "request body is invalid").
|
New(errors.TypeInvalidInput, ErrCodeInvalidRequestBody, ErrMessageInvalidJSON).
|
||||||
WithAdditional(err.Error())
|
WithAdditional(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if errors.As(err, &unmarshalTypeError) {
|
||||||
|
if unmarshalTypeError.Field != "" {
|
||||||
|
return errors.
|
||||||
|
New(errors.TypeInvalidInput, ErrCodeInvalidRequestField, ErrMessageInvalidField).
|
||||||
|
WithAdditional("value of type '" + unmarshalTypeError.Value + "' was received for field '" + unmarshalTypeError.Field + "', try sending '" + unmarshalTypeError.Type.String() + "' instead?")
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.
|
||||||
|
New(errors.TypeInvalidInput, ErrCodeInvalidRequestField, ErrMessageInvalidField).
|
||||||
|
WithAdditional("value of type '" + unmarshalTypeError.Value + "' was received, try sending '" + unmarshalTypeError.Type.String() + "' instead?")
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
68
pkg/http/binding/json_test.go
Normal file
68
pkg/http/binding/json_test.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/SigNoz/signoz/pkg/errors"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type s struct {
|
||||||
|
A int `json:"a"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (req *s) UnmarshalJSON(b []byte) error {
|
||||||
|
type Alias s
|
||||||
|
|
||||||
|
var temp Alias
|
||||||
|
if err := json.Unmarshal(b, &temp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if temp.A <= 10 {
|
||||||
|
return errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "a must be greater than 10")
|
||||||
|
}
|
||||||
|
|
||||||
|
*req = s(temp)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type n struct {
|
||||||
|
S s `json:"s"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSONBinding_BindBodyErrors(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
body string
|
||||||
|
obj any
|
||||||
|
opts []BindBodyOption
|
||||||
|
code errors.Code
|
||||||
|
message string
|
||||||
|
a []string
|
||||||
|
}{
|
||||||
|
{name: "Empty", body: "", opts: nil, obj: &struct{}{}, code: ErrCodeInvalidRequestBody, message: ErrMessageInvalidJSON, a: []string{io.EOF.Error()}},
|
||||||
|
{name: "String", body: "invalid json", opts: nil, obj: &struct{}{}, code: ErrCodeInvalidRequestBody, message: ErrMessageInvalidJSON, a: []string{"invalid character 'i' looking for beginning of value"}},
|
||||||
|
{name: "Invalid", body: `{"a":"b}`, opts: nil, obj: &struct{}{}, code: ErrCodeInvalidRequestBody, message: ErrMessageInvalidJSON, a: []string{io.ErrUnexpectedEOF.Error()}},
|
||||||
|
{name: "CustomValid", body: `{"a":9}`, opts: nil, obj: new(s), code: errors.CodeInvalidInput, message: "a must be greater than 10", a: []string{}},
|
||||||
|
{name: "CustomInvalidJSON", body: `{"a:9}`, opts: nil, obj: new(s), code: ErrCodeInvalidRequestBody, message: ErrMessageInvalidJSON, a: []string{io.ErrUnexpectedEOF.Error()}},
|
||||||
|
{name: "CustomMismatchedType", body: `{"a":"b"}`, opts: nil, obj: new(s), code: ErrCodeInvalidRequestField, message: ErrMessageInvalidField, a: []string{`value of type 'string' was received for field 'a', try sending 'int' instead?`}},
|
||||||
|
{name: "CustomNestedMismatchedType", body: `{"s":{"a":"b"}}`, opts: nil, obj: new(n), code: ErrCodeInvalidRequestField, message: ErrMessageInvalidField, a: []string{`value of type 'string' was received for field 's.a', try sending 'int' instead?`}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
err := JSON.BindBody(strings.NewReader(testCase.body), testCase.obj, testCase.opts...)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
typ, c, m, _, _, a := errors.Unwrapb(err)
|
||||||
|
assert.Equal(t, errors.TypeInvalidInput, typ)
|
||||||
|
assert.Equal(t, testCase.code, c)
|
||||||
|
assert.Equal(t, testCase.message, m)
|
||||||
|
assert.ElementsMatch(t, testCase.a, a)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user