使用RestTemplate接收multipart/form-data类型MultiValueMap遇异常求助
问题:GET接口返回multipart/form-data时RestTemplate无法解析响应
服务端Mock接口代码
@GetMapping(value = "/getMsgFileExt", produces = MediaType.MULTIPART_FORM_DATA_VALUE) public ResponseEntity<MultiValueMap<String, Object>> getMsgFileExt() throws IOException { MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>(); Path path = Paths.get("C:\Users\User\IdeaProjects\ccgw_sources\TKmock\src\main\resources\test.txt.zip.total.zip"); formData.add("xml", respDataMsgInfo); formData.add("zip", Base64.getEncoder().encodeToString(Files.readAllBytes(path))); return new ResponseEntity<>(formData, HttpStatus.OK); }
客户端接收代码
String uri = String.format("%s/getMsgFileExt", customsUrl); UriComponents builder = UriComponentsBuilder.fromHttpUrl(uri) .queryParam("id", id) .build(); ResponseEntity<MultiValueMap<String, Object>> response = customsCardRestTemplate.exchange(builder.toUriString(), HttpMethod.GET, null, new ParameterizedTypeReference<>() {}); MultiValueMap<String, Object> map = response.getBody();
抛出异常
Could not extract response: no suitable HttpMessageConverter found for response type [org.springframework.util.MultiValueMap<java.lang.String, java.lang.Object>] and content type [multipart/form-data]
解决方案
1. 改用POST请求(推荐,符合HTTP规范)
HTTP规范中GET请求通常不用于返回multipart/form-data格式的响应,改用POST更合理,同时Spring对POST场景的multipart支持更完善。
修改后服务端代码
@PostMapping(value = "/getMsgFileExt", produces = MediaType.MULTIPART_FORM_DATA_VALUE, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) public ResponseEntity<MultiValueMap<String, Object>> getMsgFileExt(@RequestParam String id) throws IOException { MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>(); // 修正路径转义符 Path path = Paths.get("C:\\Users\\User\\IdeaProjects\\ccgw_sources\\TKmock\\src\\main\\resources\\test.txt.zip.total.zip"); formData.add("xml", respDataMsgInfo); // 直接返回字节资源,避免Base64编码解码损耗 formData.add("zip", new ByteArrayResource(Files.readAllBytes(path)) { @Override public String getFilename() { return "attachment.zip"; } }); return new ResponseEntity<>(formData, HttpStatus.OK); }
修改后客户端代码
String uri = String.format("%s/getMsgFileExt", customsUrl); // 构造请求参数 MultiValueMap<String, String> requestParams = new LinkedMultiValueMap<>(); requestParams.add("id", id); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(requestParams, headers); // 调用接口并解析响应 ResponseEntity<MultiValueMap<String, Object>> response = customsCardRestTemplate.exchange( uri, HttpMethod.POST, requestEntity, new ParameterizedTypeReference<>() {}); MultiValueMap<String, Object> responseMap = response.getBody(); // 解析xml和zip String xmlContent = (String) responseMap.getFirst("xml"); ByteArrayResource zipResource = (ByteArrayResource) responseMap.getFirst("zip"); byte[] zipBytes = zipResource.getByteArray();
2. 为RestTemplate配置支持multipart响应的转换器
如果必须保留GET请求,需要扩展RestTemplate的消息转换器,让它能处理multipart/form-data类型的响应:
@Bean public RestTemplate customsCardRestTemplate() { RestTemplate restTemplate = new RestTemplate(); List<HttpMessageConverter<?>> converters = restTemplate.getMessageConverters(); // 让Jackson转换器支持multipart/form-data类型 for (HttpMessageConverter<?> converter : converters) { if (converter instanceof MappingJackson2HttpMessageConverter) { MappingJackson2HttpMessageConverter jacksonConverter = (MappingJackson2HttpMessageConverter) converter; jacksonConverter.getSupportedMediaTypes().add(MediaType.MULTIPART_FORM_DATA); } } // 添加MultipartFormHttpMessageConverter(处理multipart格式) restTemplate.getMessageConverters().add(new MultipartFormHttpMessageConverter()); return restTemplate; }
注意:此方案下服务端返回的Base64编码zip字符串,客户端需要手动解码:
String zipBase64 = (String) responseMap.getFirst("zip"); byte[] zipBytes = Base64.getDecoder().decode(zipBase64);
3. 手动解析原始响应流
如果以上方案都不适用,可以直接获取响应的输入流,用第三方库手动解析multipart内容(需引入Apache Commons FileUpload依赖):
<!-- Maven依赖 --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.5</version> </dependency>
客户端代码:
ResponseEntity<Resource> response = customsCardRestTemplate.exchange( builder.toUriString(), HttpMethod.GET, null, Resource.class); // 解析multipart响应 String contentType = response.getHeaders().getContentType().toString(); InputStream inputStream = response.getBody().getInputStream(); MultipartParser parser = new MultipartParser(inputStream, contentType); Part part; while ((part = parser.readNextPart()) != null) { if (part.isParam()) { ParamPart paramPart = (ParamPart) part; if ("xml".equals(paramPart.getName())) { String xmlContent = paramPart.getStringValue(); // 处理xml内容 } } else if (part.isFile()) { FilePart filePart = (FilePart) part; if ("zip".equals(filePart.getName())) { byte[] zipBytes = filePart.getInputStream().readAllBytes(); // 处理zip文件 } } part.close(); }
内容的提问来源于stack exchange,提问作者FPS Bard
相关产品推荐
相关产品推荐

