Skip to content

Commit 7fc5b19

Browse files
thcedCopilot
andauthored
feat: Non-JSON bodies (#53)
* fix: Reject NaN and Infinity as Number coercion results Double.parseDouble accepts "NaN", "Infinity", and "-Infinity", which are not valid JSON numbers. They would slip past NumberSchema coercion and bypass numeric constraint validation (any comparison with NaN is false). Reject non-finite results with the same type error used for other coercion failures. * fix: Case-insensitive Content-Type matching Per RFC 9110/2045 media types are case-insensitive. Lower-case the result of ContentTypeHeader.subtype() and lower-case spec content-type keys at parse time so headers like "Application/JSON" or "Text/Plain" match requestBody.content entries regardless of casing. Also fixes a compile error in FormUrlEncodedParser.decodeComponent which referenced a non-existent ValidationException constructor; the URLDecoder failure path now wraps the IllegalArgumentException as a proper ValidationError(/body, decode, ...). * refactor: Address SonarQube findings on body parsing helpers - ContentTypeHeader.parameter: replace dual-continue loop with a single early-return guarded by an inline key check; extract quote-stripping into a small unquote() helper. - FormUrlEncodedParser.decodeComponent: chain the original IllegalArgumentException via initCause() on the rethrown ValidationException so the underlying cause is preserved. - FormUrlEncodedParser.addEntry: replace get()+null-check+put() with Map.merge(), which is the right primitive for collision handling. - TextPlainParserTest: use isEmpty() instead of isEqualTo(""). * refactor: Rename ContentTypeHeader.subtype to mediaType The method returns the full media type (e.g. "application/json"), not the RFC 9110 subtype (e.g. "json"). Rename the method, its single caller's local variable, and the test method names to match what's actually returned. * docs: Fix README inaccuracies on parser selection and form value types - Selection is by the full media type (type/subtype), not the RFC 9110 subtype. Note that lookup is case-insensitive. - After coercion, form list values are not strictly List<String> — an integer-array property yields List<Long>, etc. Describe the pre- and post-coercion shape correctly. --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 7447a0e commit 7fc5b19

18 files changed

Lines changed: 2248 additions & 53 deletions

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,20 @@ Map<String, Object> raw = new Yaml().load(Files.newInputStream(Path.of("openapi.
100100
```
101101
The rest is identical.
102102

103+
### Request body content types
104+
105+
The server reads `requestBody.content` from the spec and selects a parser by the request's media type (the bare `type/subtype` from `Content-Type`, e.g. `application/json`; lookup is case-insensitive):
106+
107+
| Content type | Parser | Coercion |
108+
| ------------------------------------- | ---------------------------------------------------------------------------- | -------- |
109+
| `application/json` | Caller-supplied `JsonMapper` | No — strict against the schema |
110+
| `application/x-www-form-urlencoded` | Built-in. `Map<String, Object>`. A single value is a `String`; repeated keys produce a `List`. After coercion the element type tracks the schema (e.g. an `integer` array yields `List<Long>`). | Yes — field values coerced to the property schema type (integer / number / boolean / array of those) |
111+
| `text/plain` | Built-in. Decoded `String` | No — schema should be `type: string` |
112+
113+
Form-field coercion mirrors the rules already used at the parameter boundary: the wire is string-only by definition, so a property typed as `integer` accepts `"42"` and yields `42`. Coercion failures surface as RFC-7807 `400` responses with a JSON-pointer to the failing field.
114+
115+
Both built-in parsers honour the `charset=` parameter on the `Content-Type` header (default UTF-8). Unknown charsets fall back to UTF-8.
116+
103117
### Extra (non-OpenAPI) handlers
104118

105119
Mount handlers at arbitrary paths outside the OpenAPI spec — useful for liveness probes,

0 commit comments

Comments
 (0)