如何解决@WebMvcTest中DTO含Map元数据的文件上传转换异常?
问题描述
我想要实现一个支持上传带元数据Map的文件接口,把参数封装到ConvertFileRequest这个POJO里。但在@WebMvcTest测试中,将metadata作为JSON字符串传入请求参数时,报错no matching editors or conversion strategy found。想明确:
- 是
@WebMvcTest未配置相关转换器? - 还是MockMvc的使用方式有误?
- 或是设计上存在遗漏?
- 有没有无需JSON序列化就能把剩余参数存入metadata字段的方法?
相关测试代码
@ActiveProfiles("test") @WebMvcTest(controllers = FileFormatConvertController.class) class FileFormatConvertControllerTest { @Autowired MockMvc mockMvc; @Test void shouldUploadFileWithMetadata() throws Exception { MockMultipartFile file = new MockMultipartFile( "file", "file.docx", MediaType.APPLICATION_OCTET_STREAM_VALUE, "file content".getBytes() ); mockMvc.perform(multipart("/file") .file(file) .param("title", "File title") .param("metadata", new ObjectMapper().writeValueAsString(Map.of("some-value", "some-key"))) ) .andExpect(status().isOk()); } }
测试报错信息
Resolved [org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors Field error in object 'convertFileRequest' on field 'metadata': rejected value [{"some-value":"some-key"}]; codes [typeMismatch.convertFileRequest.metadata,typeMismatch.metadata,typeMismatch.java.util.Map,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [convertFileRequest.metadata,metadata]; arguments []; default message [metadata]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Map' for property 'metadata'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'java.util.Map' for property 'metadata': no matching editors or conversion strategy found]]
其余核心代码
控制器类:
@RestController @RequestMapping("/file") @RequiredArgsConstructor class FileFormatConvertController { @PostMapping ResponseEntity<ConvertFileResponse> convertFile(@Valid @ModelAttribute final ConvertFileRequest request) { return ResponseEntity.ok().build(); } }
请求POJO:
@Data public class ConvertFileRequest { @NotNull private MultipartFile file; @NotEmpty private String title; private Map<String, String> metadata; }
原因与解决方案
核心原因
报错本质是Spring MVC默认没有提供String(JSON字符串)到Map类型的属性转换器,@ModelAttribute绑定参数时无法自动完成这个转换,和@WebMvcTest本身无关,属于默认配置缺失对应转换规则。
解决方案
方案一:添加自定义转换器,支持JSON字符串转Map
创建一个实现Converter<String, Map<String, String>>的转换器,让Spring能识别JSON字符串并转换为Map:
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.core.convert.converter.Converter; import org.springframework.stereotype.Component; @Component public class StringToMapConverter implements Converter<String, Map<String, String>> { private final ObjectMapper objectMapper; public StringToMapConverter(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } @Override public Map<String, String> convert(String source) { try { return objectMapper.readValue(source, new TypeReference<>() {}); } catch (Exception e) { throw new IllegalArgumentException("Invalid JSON format for metadata", e); } } }
在测试类中引入该转换器:
@ActiveProfiles("test") @WebMvcTest(controllers = FileFormatConvertController.class) @Import(StringToMapConverter.class) // 引入自定义转换器 class FileFormatConvertControllerTest { // 原有测试代码不变 }
配置完成后,Spring就能自动将JSON字符串转换为Map,绑定到metadata字段。
方案二:无需JSON序列化,直接通过前缀参数绑定Map
利用Spring MVC的Map参数绑定规则:传递多个以metadata.为前缀的请求参数,Spring会自动将这些参数封装到Map中。
修改测试代码:
@ActiveProfiles("test") @WebMvcTest(controllers = FileFormatConvertController.class) class FileFormatConvertControllerTest { @Autowired MockMvc mockMvc; @Test void shouldUploadFileWithMetadata() throws Exception { MockMultipartFile file = new MockMultipartFile( "file", "file.docx", MediaType.APPLICATION_OCTET_STREAM_VALUE, "file content".getBytes() ); mockMvc.perform(multipart("/file") .file(file) .param("title", "File title") .param("metadata.some-key", "some-value") // 用metadata.前缀绑定Map的key-value .param("metadata.another-key", "another-value") ) .andExpect(status().isOk()); } }
这种方式无需额外配置转换器,符合表单提交的常规逻辑,参数传递更直观。
内容的提问来源于stack exchange,提问作者rozeus
相关产品推荐
相关产品推荐

