環境
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)
解法
原本我的解法是在 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); } }
推薦的作法還是以 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;