Skip to content

Generate @RequestPart parameters for multipart/form-data controllers - #389

Merged
jemacineiras merged 2 commits into
sngular:mainfrom
joseegman-idoneea:feat/multipart-requestpart
Jul 31, 2026
Merged

Generate @RequestPart parameters for multipart/form-data controllers#389
jemacineiras merged 2 commits into
sngular:mainfrom
joseegman-idoneea:feat/multipart-requestpart

Conversation

@joseegman-idoneea

@joseegman-idoneea joseegman-idoneea commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

What

Makes generated Spring controllers actually usable for multipart/form-data request bodies. Closes #388.

Each part of the form-data schema is now exposed as its own @RequestPart parameter, files are typed as MultipartFile / List<MultipartFile>, and the endpoint declares consumes = MediaType.MULTIPART_FORM_DATA_VALUE. The unused immutable wrapper DTO is no longer generated.

Why

Previously the whole multipart body was collapsed into one immutable @Value wrapper DTO passed as a bare @Valid parameter — no @RequestPart/@RequestParam, no consumes, and unbindable by Spring at runtime (see #388).

Change

  • template.ftlh: when the request is form-data, emit one @RequestPart(value = "<part>", required = <req>) per schema property; add consumes = {MediaType.MULTIPART_FORM_DATA_VALUE} to @RequestMapping; import MultipartFile when a file part is present; stop importing the wrapper type.
  • OpenApiUtil.processRequestBody: skip generating a wrapper model for multipart/form-data bodies.

Before / after

// before
@RequestMapping(method = RequestMethod.GET, value = "/test", produces = {"application/json"})
default ResponseEntity<Void> testMultipart(@Valid InlineObjectTestMultipart inlineObjectTestMultipart)

// after
@RequestMapping(method = RequestMethod.GET, value = "/test",
    produces = {"application/json"}, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
default ResponseEntity<Void> testMultipart(
    @RequestPart(value = "someFile", required = false) MultipartFile someFile,
    @RequestPart(value = "someFiles", required = false) List<MultipartFile> someFiles,
    @RequestPart(value = "someString", required = false) String someString)

Test

  • Extended the testFormDataMultipartGeneration fixture with a someFiles file-array part and updated the golden TestApi.java.
  • Full engine suite green: Tests run: 118, Failures: 0, Errors: 0.

Version

Bumps 6.6.16.6.2 across engine, Maven plugin and Gradle plugin.

Note

Builds on #387 (List<MultipartFile> array mapping, now merged); the someFiles file-array part in the test relies on that fix. Rebased onto main, so this PR contains only its own two commits.

🤖 Generated with Claude Code

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 complexity · 0 duplication

Metric Results
Complexity 0
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

joseegarcia and others added 2 commits July 31, 2026 10:36
The server interface template collapsed a multipart/form-data body into a
single immutable wrapper DTO passed as a bare @Valid parameter, with no
@RequestPart/@RequestParam binding and no consumes declaration, so the
generated controller could not bind multipart requests.

Now each part of the form-data schema is exposed as its own
@RequestPart parameter (files as MultipartFile / List<MultipartFile>),
the @RequestMapping declares consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
and the now-unused wrapper model is no longer generated
(OpenApiUtil.processRequestBody skips multipart/form-data bodies).

Extends the testFormDataMultipartGeneration fixture with a file-array part
and updates the golden interface accordingly.

Fixes sngular#388

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@joseegman-idoneea
joseegman-idoneea force-pushed the feat/multipart-requestpart branch from 924f108 to a049334 Compare July 31, 2026 08:37
@jemacineiras
jemacineiras merged commit f6a830d into sngular:main Jul 31, 2026
6 of 7 checks passed
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.

multipart/form-data controllers lack @RequestPart binding and consumes declaration

2 participants