Swagger UI multi-part Request with JSON Issue

Photo by Gary Bendig on Unsplash

Swagger UI multi-part Request with JSON Issue

環境

  • springdoc-openapi-starter: 2.6.0

  • swagger-core-jakarta: 2.2.22

  • spring-boot: 3.3.3

Issue

我在設計檔案上傳的 APIs 中, 同時新增了一個 form-data 欄位 meta 讓 client 去擴增檔案描述, 然而在 swagger ui 操作過程中會產生 HttpMediaTypeNotSupportedException.

  @Operation(summary = "上傳檔案")
  @PostMapping(value = URI + "/{id}/file", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
  @ResponseStatus(HttpStatus.CREATED)
  ClmRegFileDto upload(
      @PathVariable("id") Long id,
      @RequestPart("file") MultipartFile file,
      @RequestPart("meta") @Validated(Create.class) ClmRegFileMetaCreateRequest create)
      throws IOException, NoSuchAlgorithmException;

依據 springdoc issue 93 中的討論, 應該是底層 swagger ui 的問題.

Unfortunately, swagger-ui doesn't handle yet the support of the encoding attribute.

As discussed here #396, there is a workaround is to use the type binary for the other parts: (swagger-ui scans for the file extension to add the type of the request part).

Here a working sample code: The first parameter content should be in a file (with .json extension)

解法

  1. 原本我的解法是在 spring boot 的 messageConverters 的 collection 最後再添加一個客製 MultipartJackson2HttpMessageConverter, 但我覺得風險太高, 主要這個設定為 Bean 會影響整個 spring boot container, 而 MessageConverters 的順序錯誤還導致了 client 在收到 response 時 Content-Type 遺失了.

     /** 支援 swagger ui 上傳檔案的 Converter, 檔案上傳的 MediaType 為 application/octet-stream. */
     public class MultipartJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {
    
       protected MultipartJackson2HttpMessageConverter(ObjectMapper objectMapper) {
         super(objectMapper, MediaType.APPLICATION_OCTET_STREAM);
       }
     }
    
  2. 推薦的作法還是以 issue 93 中建議的 @RequestBody 的方式解決.

       @Operation(summary = "上傳檔案")
       @PostMapping(value = URI + "/{id}/file", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
       @ResponseStatus(HttpStatus.CREATED)
       @io.swagger.v3.oas.annotations.parameters.RequestBody(
           content =
               @Content(
                   encoding = @Encoding(name = "meta", contentType = MediaType.APPLICATION_JSON_VALUE)))
       ClmRegFileDto upload(
           @PathVariable("id") Long id,
           @RequestPart("file") MultipartFile file,
           @RequestPart("meta") @Validated(Create.class) ClmRegFileMetaCreateRequest create)
           throws IOException, NoSuchAlgorithmException;