-
Notifications
You must be signed in to change notification settings - Fork 0
Fix JSON unmarshal error handling and tests #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,6 @@ | ||
| package fiberoapi | ||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "errors" | ||
| "fmt" | ||
| "reflect" | ||
| "strconv" | ||
|
|
@@ -52,12 +50,33 @@ func parseInput[TInput any](app *OApiApp, c *fiber.Ctx, path string, options *Op | |
| // It's OK, the POST has no body - ignore the error | ||
| } else { | ||
| // Transform JSON unmarshal type errors into readable validation errors | ||
| // Using errors.As for more robust error handling (handles wrapped errors) | ||
| var unmarshalErr *json.UnmarshalTypeError | ||
| if errors.As(err, &unmarshalErr) { | ||
| return input, fmt.Errorf("invalid type for field '%s': expected %s but got %s", | ||
| unmarshalErr.Field, unmarshalErr.Type.String(), unmarshalErr.Value) | ||
| // Check if error message contains unmarshal type error pattern | ||
| errMsg := err.Error() | ||
| if strings.Contains(errMsg, "json: cannot unmarshal") && strings.Contains(errMsg, "into Go struct field") { | ||
| // Parse the error message to extract field name and type info | ||
| // Format: "json: cannot unmarshal <type> into Go struct field <StructName>.<Field> of type <GoType>" | ||
| parts := strings.Split(errMsg, "into Go struct field ") | ||
| if len(parts) == 2 { | ||
| afterField := parts[1] | ||
| fieldParts := strings.Split(afterField, " of type ") | ||
| if len(fieldParts) == 2 { | ||
| // Extract field name (after the last dot) | ||
| fullFieldName := fieldParts[0] | ||
| fieldNameParts := strings.Split(fullFieldName, ".") | ||
| fieldName := fieldNameParts[len(fieldNameParts)-1] | ||
|
|
||
| // Extract expected type | ||
| expectedType := fieldParts[1] | ||
|
|
||
| // Extract actual type from the first part | ||
| typePart := strings.TrimPrefix(parts[0], "json: cannot unmarshal ") | ||
|
|
||
| return input, fmt.Errorf("invalid type for field '%s': expected %s but got %s", | ||
| fieldName, expectedType, typePart) | ||
| } | ||
| } | ||
| } | ||
|
Comment on lines
+53
to
78
|
||
|
|
||
| return input, err | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -18,11 +18,14 @@ import ( | |||||||||||||||||
| // the raw Go error message is not user-friendly: | ||||||||||||||||||
| // "json: cannot unmarshal number into Go struct field Request.Description of type string" | ||||||||||||||||||
| // | ||||||||||||||||||
| // Solution: Detect json.UnmarshalTypeError and transform it into a readable message: | ||||||||||||||||||
| // Solution: Parse the error message to extract field name and type information, | ||||||||||||||||||
| // then transform it into a readable message: | ||||||||||||||||||
| // "invalid type for field 'description': expected string but got number" | ||||||||||||||||||
| // | ||||||||||||||||||
| // Implementation: Uses errors.As (not type assertion) to handle wrapped errors correctly. | ||||||||||||||||||
| // This ensures the error detection works even if the error is wrapped by Fiber or middleware. | ||||||||||||||||||
| // Implementation: The error type from c.BodyParser() is *errors.UnmarshalTypeError | ||||||||||||||||||
| // (not *json.UnmarshalTypeError), so we parse the error message string to extract | ||||||||||||||||||
| // the field name, expected type, and actual type. This approach works reliably | ||||||||||||||||||
| // across different Fiber versions and handles all JSON unmarshal type errors. | ||||||||||||||||||
|
Comment on lines
+25
to
+28
|
||||||||||||||||||
| // Implementation: The error type from c.BodyParser() is *errors.UnmarshalTypeError | |
| // (not *json.UnmarshalTypeError), so we parse the error message string to extract | |
| // the field name, expected type, and actual type. This approach works reliably | |
| // across different Fiber versions and handles all JSON unmarshal type errors. | |
| // Implementation: The error returned by c.BodyParser() is typically a wrapped *json.UnmarshalTypeError | |
| // (from the encoding/json package), or sometimes a string error depending on Fiber's internal handling. | |
| // Therefore, we parse the error message string to extract the field name, expected type, and actual type. | |
| // This approach works reliably across different Fiber versions and handles all JSON unmarshal type errors. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation extracts the Go struct field name (e.g.,
Description) from the error message, but for user-facing error messages, the JSON field name (e.g.,description) should be used instead.The current code on line 66 extracts
Descriptionfrom the error message "json: cannot unmarshal number into Go struct field Request.Description of type string", but clients send JSON with the field namedescription(lowercase, as specified in the JSON tag).To fix this, you would need to:
The test expectations in
json_type_error_test.go(lines 66, 72) expect lowercase field names like'description', which suggests this is a bug that will cause test failures.