springdoc / springdoc-openapi

Library for OpenAPI 3 with spring-boot
https://springdoc.org
Apache License 2.0
3.24k stars 489 forks source link

Swagger UI Missing "Choose File" Button for File Upload with springdoc.api-docs.version=openapi_3_1 #2667

Open m-titov opened 1 month ago

m-titov commented 1 month ago

When using springdoc.api-docs.version=openapi_3_1, the Swagger UI does not display the "Choose File" button for file upload endpoints. Instead, it shows a text area for inputting the Request Body. This issue is observed for both endpoints in the provided sample code. In contrast, with springdoc.api-docs.version=openapi_3_0, the "Choose File" button appears as expected. To Reproduce

Steps to reproduce the behavior:

Configuration:

Configure your application.properties with the following settings: properties

springdoc.api-docs.path=/doc
springdoc.api-docs.version=openapi_3_1
springdoc.auto-tag-classes=false
springdoc.nullable-request-parameter-enabled=true
springdoc.writer-with-order-by-keys=true
springdoc.swagger-ui.display-request-duration=true
springdoc.swagger-ui.disable-swagger-default-url=true
springdoc.swagger-ui.show-extensions=true
springdoc.swagger-ui.show-common-extensions=true
springdoc.swagger-ui.try-it-out-enabled=true

Use the following Java code for file upload endpoints:

package demo;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@RestController
public class FileController {

    @PostMapping(value = "/file1", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public String handleFileUpload(MultipartFile file) {
        return file.getOriginalFilename();
    }

    public record FileUpload(@RequestPart MultipartFile file) { }

    @PostMapping(value = "/file2", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public String handleFileUpload2(FileUpload fileUpload) {
        return fileUpload.file().getOriginalFilename();
    }
}

Add the following dependencies and plugin configuration:

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.3.2'
    id 'io.spring.dependency-management' version '1.1.6'
    id("org.springdoc.openapi-gradle-plugin") version "1.9.0"
}

group = 'some'
version = '0.0.1-SNAPSHOT'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation "org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0"
    implementation "com.github.therapi:therapi-runtime-javadoc:0.15.0"
}

openApi {
    apiDocsUrl.set("http://localhost:8080/doc.yaml")
    outputDir.set(layout.buildDirectory.dir("doc"))
    outputFileName.set("doc.yaml")
    customBootRun {
        workingDir.set(project.rootProject.projectDir) // TODO remove it as soon as https://github.com/springdoc/springdoc-openapi-gradle-plugin/issues/150 is fixed
    }
}

Observation:

Access Swagger UI at http://localhost:8080/swagger-ui.html. For the /file1 and /file2 endpoints, observe that the Swagger UI displays a text area for inputting the Request Body instead of the "Choose File" button. Expected behavior

For endpoints that support file uploads, Swagger UI should display the "Choose File" button, allowing users to select and upload files through the Swagger interface.

Screenshots image

Additional context

The issue is specific to springdoc.api-docs.version=openapi_3_1 With springdoc.api-docs.version=openapi_3_0, the Swagger UI correctly displays the "Choose File" button for file upload endpoints. It may be related to changes or differences in how OpenAPI 3.1 specifications handle file uploads compared to OpenAPI 3.0.

m-titov commented 1 month ago

It looks like it works for /file1 endpoint if we make the following changes:

image image
openapi: 3.1.0
info:
  title: OpenAPI definition
  version: v0
servers:
- description: Generated server url
  url: http://localhost:8080
paths:
  /file1:
    post:
      operationId: handleFileUpload
      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              example: null
              properties:
                file:
                  type: string
                  format: binary
                  example: null
      responses:
        "200":
          content:
            '*/*':
              schema:
                type:
                - string
                example: null
          description: OK
  /file2:
    post:
      operationId: handleFileUpload2
      requestBody:
        content:
          multipart/form-data:
            schema:
              $ref: "#/components/schemas/FileUpload"
              example: null
      responses:
        "200":
          content:
            '*/*':
              schema:
                type:
                - string
                example: null
          description: OK
components:
  schemas:
    FileUpload:
      example: null
      properties:
        file:
          type:
          - string
          format: binary
          example: null