Skip to content
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

API Endpoint that Accepts Form Data as MultiValueMap Renders as Multiple Fields in Swagger UI #2912

Closed
cmiles74 opened this issue Feb 20, 2025 · 7 comments
Labels
invalid This doesn't seem right

Comments

@cmiles74
Copy link

cmiles74 commented Feb 20, 2025

Describe the bug

My application has an API endpoint that accepts a dynamic form post (we don't know the fields of the form ahead of time). The endpoint is looking for a POST with a request body of MultiValueMap<String, String>. This works well in my application but the Swagger API displays with two fields, "all" and "empty"; when the "all" field is filled in the data doesn't make it to the actual endpoint.

To Reproduce

I'm on the newest version of Spring Boot and the library. My controller methods looks like this...

    @RequestMapping(value = "/{name}/{version}/html", method = RequestMethod.POST,
            consumes = "application/x-www-form-urlencoded", produces = "application/json")
    public ResponseEntity<ValidatedFormDefinitionData> postFormDefinitionSubmission(
            @PathVariable String name,
            @PathVariable String version,
            @RequestBody MultiValueMap<String, String> formData) {

     // code goes here 

    }

That @RequestBody mapping is from the org.springframework.web.bind.annotation package, which I believe is correct.

Screenshots

When I visit the Swagger documentation page, the endpoint's API documentation is rendered like so:

Image

Additional context

My expectation is that there's one text area where I could paste URL encoded parameters, delineated with an ampersand. When I press the "Execute" button I'd like it to generate a cURL command that looks like this...

curl -X 'POST' \
     'http://localhost:8080/form/definition/sample/1/html' \
     -H 'accept: application/json' \
     -H 'Content-Type: application/x-www-form-urlencoded' \
     -d 'sample-form.biographical.first-name=Leonard&sample-form.biographical.last-name=McCoy'

There could be something obvious I'm missing but I have spent a good amount of time trying to make this work and I am stumped. Any help would be greatly appreciated.

Thank you!

@cmiles74 cmiles74 changed the title API Endpoint that Accepts Form Data as MultiValueMap Renders as Fields in Swagger UI API Endpoint that Accepts Form Data as MultiValueMap Renders as Multiple Fields in Swagger UI Feb 20, 2025
@bnasslahsen
Copy link
Collaborator

@cmiles74,

This is the generated spec:

openapi: 3.1.0
info:
  title: OpenAPI definition
  version: v0
servers:
  - url: http://localhost:8080
    description: Generated server url
paths:
  /{name}/{version}/html:
    post:
      tags:
        - hello-controller
      operationId: postFormDefinitionSubmission
      parameters:
        - name: name
          in: path
          required: true
          schema:
            type: string
        - name: version
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/MultiValueMapStringString'
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: string
components:
  schemas:
    MultiValueMapStringString:
      type: object
      additionalProperties:
        type: array
        items:
          type: string
      properties:
        all:
          type: object
          additionalProperties:
            type: string
          writeOnly: true
        empty:
          type: boolean

And the working curl:

curl -X 'POST' \
  'http://localhost:8080/test/v1/html' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'all=%7B%22additionalProp1%22%3A%22string1%22%2C%22additionalProp2%22%3A%22string2%22%2C%22additionalProp3%22%3A%22strin3%22%7D&empty=true'

@bnasslahsen bnasslahsen added the invalid This doesn't seem right label Mar 22, 2025
@cmiles74
Copy link
Author

Yes, this is my issue: the generated Swagger spec is looking for two fields in the posted data: "all" and "empty". Where are these coming from?

In my opinion, the endpoint should be looking for a body in the request and that body should contain an arbitrary string of form encoded values. The Swagger documentation should present only one text field where we might paste such a string of encoded data.

Why is this issue listed as invalid? I believe this is the standard way endpoints that accept form post data function.

@bnasslahsen
Copy link
Collaborator

bnasslahsen commented Mar 23, 2025

@cmiles74,

This is an invalid issue, as it is reported to the wrong project.
It's an issue in the swagger-core and not springdoc-openapi
You can verify it by the following code:

	Schema<?> resolvedSchema = ModelConverters.getInstance()
			.resolveAsResolvedSchema(
					new AnnotatedType(MultiValueMap.class).resolveAsRef(false)
			).schema;
	System.out.println(resolvedSchema.getProperties().get("all"));
	System.out.println(resolvedSchema.getProperties().get("empty"));

As workaround, you always have the swagger-core annotations, to generate the expected output:

public ResponseEntity<String> postFormDefinitionSubmission(
		@PathVariable String name,
		@PathVariable String version,
		@io.swagger.v3.oas.annotations.parameters.RequestBody(content = @Content(schema = @Schema(implementation = Map.class)))
		@RequestBody MultiValueMap<String, String> formData) {

@cmiles74
Copy link
Author

I'm willing to open an issue against swagger core, but I'd like to understand why this isn't an issue for this project... It looks to me like it's our use of the MultiValueMap and the annotations on that class that may be causing this issue

Your workaround seems to force the use of a Map when Swagger generates the schema for the field. Wouldn't a better solution be to emit the schema for a Map when we generate the schema for the MultiValueMap?

@bnasslahsen
Copy link
Collaborator

bnasslahsen commented Mar 23, 2025

@cmiles74,

We won't be addressing all the issues for the projects that the library depends on.
The issue you pointed, is caused by a code, that is not maintained by the springdoc-openapiproject and we have shown you the samples of codes that shows the root cause.

For the workaround, it's a workaround. Feel free to select any other solution that works better for you.
And don't forget to share your approach with the community in the comments!

@cmiles74
Copy link
Author

Thank you for the details, @bnasslahsen . 🙂 Do you know which Spring project is responsible for the MultiValueMap? My guess is Spring MVC but I thought I'd ask in case you had this info handy.

@bnasslahsen
Copy link
Collaborator

@cmiles74,

The issue is in swagger-core project and not spring framework.
Anyway, i believe MultiValueMapis maintained by the spring-core team.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
invalid This doesn't seem right
Projects
None yet
Development

No branches or pull requests

2 participants