Skip to content

feat: Non-JSON bodies#53

Merged
thced merged 17 commits into
masterfrom
feat/non-json-bodies
May 13, 2026
Merged

feat: Non-JSON bodies#53
thced merged 17 commits into
masterfrom
feat/non-json-bodies

Conversation

@thced

@thced thced commented May 13, 2026

Copy link
Copy Markdown
Contributor

No description provided.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class support for non-JSON request bodies by dispatching request-body parsing based on the incoming Content-Type (matched against requestBody.content in the OpenAPI spec). This extends the server beyond the previous “always JSON-decode the body” behavior while reusing existing validation/coercion mechanics.

Changes:

  • Add built-in parsing for application/x-www-form-urlencoded (to Map<String,Object>, with property-type coercion) and text/plain (to String).
  • Refactor request preparation to select the correct parser by Content-Type (and hoist shared coercion into ValueCoercion).
  • Add unit + integration tests and update fixtures/docs to cover the new content types.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/main/java/com/retailsvc/http/internal/RequestPreparationFilter.java Dispatch body parsing by Content-Type and reuse shared coercion for parameters.
src/main/java/com/retailsvc/http/internal/FormUrlEncodedParser.java New form-urlencoded parser + schema-driven coercion for object properties.
src/main/java/com/retailsvc/http/internal/TextPlainParser.java New text/plain parser with charset handling.
src/main/java/com/retailsvc/http/internal/ContentTypeHeader.java New helper for parsing Content-Type subtype/parameters.
src/main/java/com/retailsvc/http/internal/ValueCoercion.java New shared coercion helper for parameter and form-field string values.
src/test/java/com/retailsvc/http/NonJsonBodyIT.java Integration tests covering form + text request bodies and error cases.
src/test/java/com/retailsvc/http/start/FormEchoHandler.java Test handler to echo parsed form body.
src/test/java/com/retailsvc/http/start/TextEchoHandler.java Test handler to echo parsed text body.
src/test/java/com/retailsvc/http/internal/FormUrlEncodedParserTest.java Unit tests for parsing and coercion behavior.
src/test/java/com/retailsvc/http/internal/TextPlainParserTest.java Unit tests for charset/default decoding.
src/test/java/com/retailsvc/http/internal/ContentTypeHeaderTest.java Unit tests for header parsing utilities.
src/test/java/com/retailsvc/http/internal/ValueCoercionTest.java Unit tests for coercion success/failure paths.
src/test/resources/openapi.json Add /form-echo and /text-echo operations for tests.
src/test/resources/openapi.yaml Mirror the new operations in the YAML fixture.
README.md Document supported request body content types and coercion behavior.
docs/superpowers/specs/2026-05-13-non-json-request-bodies-design.md Design doc for the feature and intended behaviors.
docs/superpowers/plans/2026-05-13-non-json-request-bodies.md Implementation plan/checklist for the feature.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/main/java/com/retailsvc/http/internal/FormUrlEncodedParser.java Outdated
Comment thread src/main/java/com/retailsvc/http/internal/ContentTypeHeader.java Outdated
Comment thread src/main/java/com/retailsvc/http/internal/ValueCoercion.java
thced and others added 3 commits May 13, 2026 09:36
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
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.
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, ...).
- 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("").

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 18 changed files in this pull request and generated 2 comments.

Comment thread src/main/java/com/retailsvc/http/internal/ContentTypeHeader.java
Comment thread README.md Outdated
thced added 2 commits May 13, 2026 09:51
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.
- 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.
@sonarqubecloud

Copy link
Copy Markdown

@thced thced merged commit 7fc5b19 into master May 13, 2026
4 checks passed
@thced thced deleted the feat/non-json-bodies branch May 13, 2026 08:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants