Skip to content

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

Description

@joseegman-idoneea

Description

For a multipart/form-data request body, the generated Spring controller interface is not usable as-is:

  1. No @RequestPart / @RequestParam binding annotation. The whole multipart body is collapsed into a single generated wrapper DTO passed as a bare @Valid parameter (the only effect of the form-data branch is that @RequestBody is omitted). Spring cannot bind multipart parts this way.
  2. No consumes declaration. The @RequestMapping hard-codes produces = {"application/json"} and never advertises consumes = MediaType.MULTIPART_FORM_DATA_VALUE, even though the operation's consumes list is already computed.
  3. The wrapper DTO is immutable. It is generated as an immutable Lombok @Value type (builder only, no no-arg constructor / setters), so even Spring's default @ModelAttribute binding cannot instantiate it at runtime.

Steps to reproduce

paths:
  /test:
    post:
      operationId: testMultipart
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                someString:
                  type: string
                someFile:
                  type: string
                  format: binary

Actual result

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

No @RequestPart, no consumes, and InlineObjectTestMultipart is an immutable @Value class.

Expected result

Each multipart part exposed as its own @RequestPart, files as MultipartFile / List<MultipartFile>, plus a consumes declaration:

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

Root cause

templates/openapi/template.ftlh only uses the isFormData flag to drop @RequestBody; it never emits @RequestPart/consumes. OpenApiUtil.processRequestBody registers the multipart schema as a wrapper model regardless.

Environment

  • Affects the OpenAPI code generator (multiapi-engine), Spring server interface template.
  • Reproduced on main.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions