Spring MVC中包含多部分文件和其他DTO的POST请求

4

我有一个包含其他DTO和一个multipart文件列表的DTO。我正在尝试处理该DTO,但似乎无法读取请求。

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

}

在使用Swagger UI创建示例请求时,我遇到了以下异常:

type 'java.lang.String' to required type 'SpecializationDto' for property 'specializationDto': no matching editors or conversion strategy found

如果我使用@RequestBody而不是@ModelAttribute,那么我会得到:

Content type 'multipart/form-data;boundary=----WebKitFormBoundaryVEgYwEbpl1bAOjAs;charset=UTF-8' not supported]

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>

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));
  }
}

你只在Swagger客户端遇到这个问题吗? - b.s
是的,我希望能够让它能够与Swagger UI一起使用。 - 2dor
你需要提供更多细节,包括Swagger版本等,否则如果有问题的话就很难描述了。 - b.s
@harry 已更新开放 API 配置。我使用的是 Open API 3.0。 - 2dor
1
你在这里混淆了两件事情。发布JSON对象和发布多部分数据。我建议不要这样做。只向某个端点发布多部分数据。 - Bart
1个回答

3
这似乎是springdoc-openapi-ui构建form-data请求的问题。我已经复现了这个问题,并注意到它发送了一个类似multipart-request的请求(通过浏览器的开发工具进行拦截)。
-----------------------------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>

由于这样的负载,Spring 无法反序列化 specializationDto,导致你观察到的 "找不到匹配的编辑器或转换策略" 异常。然而,如果你使用 postman 或 curl 发送请求,并使用点符号表示 specializationDto 对象,则可以解决问题。

curl --location --request POST 'http://localhost:8080/upload' \
--form 'files=@"/path/to/somefile"' \
--form 'files=@"/path/to/somefile"' \
--form 'specializationDto.something="someValue"'

那么Spring就能正确地解析它。以下是我的rest-mapping,按预期将记录以下内容:

    @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])

建议您在他们的GitHub 页面上开一个缺陷。

编辑:

OP打开了一个github工单后,这是作者的部分反馈:

[...] 使用Spring,您可以使用@RequestPart Spring注释描述不同的部分和相关的编码媒体类型。请注意,由于请求上未尊重编码属性,当前的swagger-ui实现存在限制。[...]

他们还提供了一个可能的解决方法,看起来像这样:

@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;
}

2
谢谢您的回复。我已在他们的Github存储库上创建了一个错误报告,他们的回复在这里。您能否编辑您的回复并附上他们的回复,以便我可以将您的消息批准为正确答案,考虑到您已经将其推向正确方向?https://github.com/springdoc/springdoc-openapi/issues/1416 - 2dor
做得好 :) 很高兴听到至少有一个解决方法。 - eol

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接