Tengo un DTO que contiene otros DTO y una lista de archivos de varias partes. Estoy tratando de procesar ese DTO pero parece que no puedo leer la solicitud.
class TeacherDTO { private SpecializationDto specializationDto; private List<MultipartFile> files; } @PostMapping(consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE}) public ResponseEntity<Object> saveNewTeacher(@ModelAttribute @Valid TeacherDTO teacherDto){ //process request }Al crear una solicitud de ejemplo desde la interfaz de usuario de Swagger, obtengo la siguiente excepción:
type 'java.lang.String' to required type 'SpecializationDto' for property 'specializationDto': no matching editors or conversion strategy foundSi pongo @RequestBody en lugar de @ModelAttribute, obtengo
Content type 'multipart/form-data;boundary=----WebKitFormBoundaryVEgYwEbpl1bAOjAs;charset=UTF-8' not supported]Dependencias de Swagger:
<dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-ui</artifactId> <version>1.5.2</version> </dependency> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-data-rest</artifactId> <version>1.5.2</version> </dependency>Configuración de OpenAPI3.0:
@Configuration public class OpenApi30Config { private final String moduleName; private final String apiVersion; public OpenApi30Config( @Value("${spring.application.name}") String moduleName, @Value("${api.version}") String apiVersion) { this.moduleName = moduleName; this.apiVersion = apiVersion; } @Bean public OpenAPI customOpenAPI() { final var securitySchemeName = "bearerAuth"; final var apiTitle = String.format("%s API", StringUtils.capitalize(moduleName)); return new OpenAPI() .addSecurityItem(new SecurityRequirement().addList(securitySchemeName)) .components( new Components() .addSecuritySchemes(securitySchemeName, new SecurityScheme() .name(securitySchemeName) .type(SecurityScheme.Type.HTTP) .scheme("bearer") .bearerFormat("JWT") ) ) .info(new Info().title(apiTitle).version(apiVersion)); } }Esto parece ser un problema con la forma en que springdoc-openapi-ui crea la solicitud de datos de formulario. Pude reproducir esto y noté que envía una solicitud de varias partes como (interceptada a través de las herramientas de desarrollo del navegador):
-----------------------------207598777410513073071314493349 Content-Disposition: form-data; name="specializationDto"\r\n\r\n{\r\n "something": "someValue"\r\n} -----------------------------207598777410513073071314493349 Content-Disposition: form-data; name="files"; filename="somefile.txt" Content-Type: application/octet-stream <content> -----------------------------207598777410513073071314493349 Content-Disposition: form-data; name="files"; filename="somefile.txt" Content-Type: application/octet-stream <content> Con esa carga útil, Spring no puede deserializar la specializationDto Dto , lo que da como resultado la excepción "no se encontraron editores coincidentes o estrategia de conversión" que ha observado. Sin embargo, si envía la solicitud a través del cartero o curl (tenga en cuenta la notación de puntos para el objeto specializationDto)
curl --location --request POST 'http://localhost:8080/upload' \ --form 'files=@"/path/to/somefile"' \ --form 'files=@"/path/to/somefile"' \ --form 'specializationDto.something="someValue"'entonces Spring puede analizarlo correctamente. Aquí está mi mapeo de descanso que registrará lo siguiente como se esperaba:
@RequestMapping(value = "/upload", method = RequestMethod.POST, consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}) public void upload(@ModelAttribute TeacherDto requestDto) { System.out.println(requestDto); } // logs: TeacherDto(specializationDto=SpecializationDto(something=someValue), files=[org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@78186ea6, org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@461c9cbc])Le sugiero que abra un error en su página de github.
EDITAR:
Después de que OP abrió un ticket de github, aquí hay parte de los comentarios del autor:
[...] Con spring, puede usar la anotación de resorte @RequestPart para describir las diferentes partes, con el tipo de medio de codificación relacionado. Tenga en cuenta que existe una limitación con la implementación actual de swagger-ui ya que el atributo de codificación no se respeta en la solicitud.[...]
También proporcionaron una posible solución alternativa, que se ve así:
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<Object> saveNewTeacher( @RequestPart(value = "specializationDto") @Parameter(schema =@Schema(type = "string", format = "binary")) final SpecializationDto specializationDto, @RequestPart(value = "files") final List<MultipartFile> files){ return null; }