Skip to content

Commit 1fd200a

Browse files
moutonjeremyJeremy Mouton
andauthored
Fix serialization errors in custom error handling (#28)
* Fix error serialization in custom error handler * Fix response serialization error handling in Method function * Add tests for unserializable output and error handling in serialization * Fix fallback error handling in custom error serialization * Fix error handling for response serialization in Method function --------- Co-authored-by: Jeremy Mouton <[email protected]>
1 parent 632fdab commit 1fd200a

File tree

3 files changed

+127
-2
lines changed

3 files changed

+127
-2
lines changed

common.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,14 @@ func handleCustomError(c *fiber.Ctx, customErr interface{}) error {
145145
}
146146

147147
// Return the error as JSON
148-
return c.Status(statusCode).JSON(customErr)
148+
if err := c.Status(statusCode).JSON(customErr); err != nil {
149+
if fallbackErr := c.Status(500).JSON(fiber.Map{"error": "Failed to serialize error response"}); fallbackErr != nil {
150+
// Both serializations failed, return original error to Fiber
151+
return err
152+
}
153+
return nil
154+
}
155+
return nil
149156
}
150157

151158
// Utility to check if a value is zero

fiberoapi.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -861,7 +861,18 @@ func Method[TInput any, TOutput any, TError any](
861861
return handleCustomError(c, customErr)
862862
}
863863

864-
return c.JSON(output)
864+
if err := c.JSON(output); err != nil {
865+
if fallbackErr := c.Status(500).JSON(ErrorResponse{
866+
Code: 500,
867+
Details: "Failed to serialize response",
868+
Type: "serialization_error",
869+
}); fallbackErr != nil {
870+
// Both serializations failed, return original error to Fiber
871+
return err
872+
}
873+
return nil
874+
}
875+
return nil
865876
}
866877

867878
app.f.Add(m, fullPath, fiberHandler)

serialization_error_test.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package fiberoapi
2+
3+
import (
4+
"encoding/json"
5+
"io"
6+
"net/http/httptest"
7+
"testing"
8+
9+
"github.com/gofiber/fiber/v2"
10+
)
11+
12+
// UnserializableOutput contains a channel which cannot be serialized to JSON
13+
type UnserializableOutput struct {
14+
Name string `json:"name"`
15+
Channel chan string `json:"channel"` // Channels cannot be JSON serialized
16+
}
17+
18+
type SerializationTestInput struct{}
19+
20+
type SerializationTestError struct {
21+
StatusCode int `json:"statusCode"`
22+
Message string `json:"message"`
23+
}
24+
25+
func TestResponseSerializationError(t *testing.T) {
26+
app := fiber.New()
27+
oapi := New(app)
28+
29+
// Handler that returns an unserializable output (contains a channel)
30+
Get(oapi, "/unserializable", func(c *fiber.Ctx, input SerializationTestInput) (UnserializableOutput, *SerializationTestError) {
31+
return UnserializableOutput{
32+
Name: "test",
33+
Channel: make(chan string), // This will fail JSON marshaling
34+
}, nil
35+
}, OpenAPIOptions{
36+
Summary: "Test endpoint with unserializable response",
37+
})
38+
39+
req := httptest.NewRequest("GET", "/unserializable", nil)
40+
resp, err := app.Test(req)
41+
if err != nil {
42+
t.Fatalf("Failed to make request: %v", err)
43+
}
44+
45+
// Should return 500 status code
46+
if resp.StatusCode != 500 {
47+
t.Errorf("Expected status 500, got %d", resp.StatusCode)
48+
}
49+
50+
// Should return a proper error response
51+
body, _ := io.ReadAll(resp.Body)
52+
var errorResp ErrorResponse
53+
if err := json.Unmarshal(body, &errorResp); err != nil {
54+
t.Fatalf("Failed to unmarshal error response: %v. Body: %s", err, string(body))
55+
}
56+
57+
if errorResp.Code != 500 {
58+
t.Errorf("Expected error code 500, got %d", errorResp.Code)
59+
}
60+
61+
if errorResp.Type != "serialization_error" {
62+
t.Errorf("Expected error type 'serialization_error', got '%s'", errorResp.Type)
63+
}
64+
}
65+
66+
// UnserializableError contains a channel which cannot be serialized
67+
type UnserializableError struct {
68+
StatusCode int `json:"statusCode"`
69+
Channel chan string `json:"channel"`
70+
}
71+
72+
func TestErrorSerializationError(t *testing.T) {
73+
app := fiber.New()
74+
oapi := New(app)
75+
76+
// Handler that returns an unserializable error
77+
Get(oapi, "/unserializable-error", func(c *fiber.Ctx, input SerializationTestInput) (map[string]string, *UnserializableError) {
78+
return nil, &UnserializableError{
79+
StatusCode: 400,
80+
Channel: make(chan string), // This will fail JSON marshaling
81+
}
82+
}, OpenAPIOptions{
83+
Summary: "Test endpoint with unserializable error",
84+
})
85+
86+
req := httptest.NewRequest("GET", "/unserializable-error", nil)
87+
resp, err := app.Test(req)
88+
if err != nil {
89+
t.Fatalf("Failed to make request: %v", err)
90+
}
91+
92+
// Should return 500 status code (fallback error)
93+
if resp.StatusCode != 500 {
94+
t.Errorf("Expected status 500, got %d", resp.StatusCode)
95+
}
96+
97+
// Should return a proper error response
98+
body, _ := io.ReadAll(resp.Body)
99+
var errorResp map[string]string
100+
if err := json.Unmarshal(body, &errorResp); err != nil {
101+
t.Fatalf("Failed to unmarshal error response: %v. Body: %s", err, string(body))
102+
}
103+
104+
if errorResp["error"] != "Failed to serialize error response" {
105+
t.Errorf("Expected error message 'Failed to serialize error response', got '%s'", errorResp["error"])
106+
}
107+
}

0 commit comments

Comments
 (0)