mirror of
https://github.com/gin-gonic/gin.git
synced 2026-04-29 15:08:19 +08:00
Merge eb225210b80be49fd2cd8c96d44444dfaaf013d5 into d3ffc9985281dcf4d3bef604cce4e662b1a327a6
This commit is contained in:
commit
142c271e06
21
context.go
21
context.go
@ -244,16 +244,37 @@ func (c *Context) AbortWithError(code int, err error) *Error {
|
||||
/********* ERROR MANAGEMENT *********/
|
||||
/************************************/
|
||||
|
||||
// joinedError is an interface for errors that wrap multiple inner errors,
|
||||
// such as those created by errors.Join.
|
||||
type joinedError interface {
|
||||
Unwrap() []error
|
||||
}
|
||||
|
||||
// Error 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.
|
||||
// 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.
|
||||
// Error will panic if err is nil.
|
||||
// If err is a joined error (created by errors.Join), it is unwrapped and each
|
||||
// individual error is added as a separate entry. The last *Error added is returned.
|
||||
func (c *Context) Error(err error) *Error {
|
||||
if err == nil {
|
||||
panic("err is nil")
|
||||
}
|
||||
|
||||
// Unwrap joined errors so each one becomes a separate entry.
|
||||
if joined, ok := err.(joinedError); ok {
|
||||
errs := joined.Unwrap()
|
||||
if len(errs) > 0 {
|
||||
var last *Error
|
||||
for _, e := range errs {
|
||||
last = c.Error(e)
|
||||
}
|
||||
return last
|
||||
}
|
||||
// Fall through for empty joined errors — store as-is.
|
||||
}
|
||||
|
||||
var parsedError *Error
|
||||
ok := errors.As(err, &parsedError)
|
||||
if !ok {
|
||||
|
||||
@ -1960,6 +1960,63 @@ func TestContextTypedError(t *testing.T) {
|
||||
assert.Equal(t, []string{"externo 0", "interno 0"}, c.Errors.Errors())
|
||||
}
|
||||
|
||||
func TestContextErrorJoined(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
|
||||
// Single errors around the joined one.
|
||||
c.Error(errors.New("first")) //nolint: errcheck
|
||||
|
||||
joined := errors.Join(errors.New("service error"), errors.New("store error"))
|
||||
last := c.Error(joined) //nolint: errcheck
|
||||
|
||||
c.Error(errors.New("last")) //nolint: errcheck
|
||||
|
||||
// Joined error must be unwrapped into separate entries.
|
||||
assert.Len(t, c.Errors, 4)
|
||||
assert.Equal(t, "first", c.Errors[0].Error())
|
||||
assert.Equal(t, "service error", c.Errors[1].Error())
|
||||
assert.Equal(t, "store error", c.Errors[2].Error())
|
||||
assert.Equal(t, "last", c.Errors[3].Error())
|
||||
|
||||
// Return value is the last unwrapped entry.
|
||||
assert.Equal(t, "store error", last.Error())
|
||||
|
||||
// All unwrapped entries default to ErrorTypePrivate.
|
||||
for _, e := range c.Errors {
|
||||
assert.Equal(t, ErrorTypePrivate, e.Type)
|
||||
}
|
||||
|
||||
// String output should list each error individually.
|
||||
expected := "Error #01: first\nError #02: service error\nError #03: store error\nError #04: last\n"
|
||||
assert.Equal(t, expected, c.Errors.String())
|
||||
}
|
||||
|
||||
func TestContextErrorNestedJoined(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
|
||||
inner := errors.Join(errors.New("a"), errors.New("b"))
|
||||
outer := errors.Join(inner, errors.New("c"))
|
||||
c.Error(outer) //nolint: errcheck
|
||||
|
||||
assert.Len(t, c.Errors, 3)
|
||||
assert.Equal(t, []string{"a", "b", "c"}, c.Errors.Errors())
|
||||
}
|
||||
|
||||
func TestContextErrorJoinedWithTypedError(t *testing.T) {
|
||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||
|
||||
typedErr := &Error{Err: errors.New("typed"), Type: ErrorTypePublic, Meta: "meta"}
|
||||
joined := errors.Join(errors.New("plain"), typedErr)
|
||||
c.Error(joined) //nolint: errcheck
|
||||
|
||||
assert.Len(t, c.Errors, 2)
|
||||
assert.Equal(t, "plain", c.Errors[0].Error())
|
||||
assert.Equal(t, ErrorTypePrivate, c.Errors[0].Type)
|
||||
assert.Equal(t, "typed", c.Errors[1].Error())
|
||||
assert.Equal(t, ErrorTypePublic, c.Errors[1].Type)
|
||||
assert.Equal(t, "meta", c.Errors[1].Meta)
|
||||
}
|
||||
|
||||
func TestContextAbortWithError(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := CreateTestContext(w)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user