415通过REST模板发送json文件时不受支持的媒体类型

时间:2018-07-25 21:07:23

标签: java spring rest spring-mvc resttemplate

我正在尝试通过REST模板发送json文件。当我通过POST人以MULTIPART_FORM_DATA的形式发送它时,它工作正常。我应该给的名字是特定的(让我们说 aaa )。附加了POSTMAN的屏幕快照screenshot。但是,当我在另一个stackoverflow post中指定的代码中尝试相同的代码时,我收到415 Unsupported Media Type错误,为

org.springframework.web.client.HttpClientErrorException: 415 Unsupported Media Type
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91) ~[spring-web-4.1.9.RELEASE.jar:4.1.9.RELEASE]
    at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:616) ~[spring-web-4.1.9.RELEASE.jar:4.1.9.RELEASE]
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:572) ~[spring-web-4.1.9.RELEASE.jar:4.1.9.RELEASE]
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:532) ~[spring-web-4.1.9.RELEASE.jar:4.1.9.RELEASE]
    at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:332) ~[spring-web-4.1.9.RELEASE.jar:4.1.9.RELEASE]
    at 

请不要将其标记为重复项,因为指定的答案对我不起作用。不共享代码,因为我的代码与this完全相同,除了

requestParamerterMap.add("attachment", resource);

我的代码在哪里

requestParamerterMap.add("aaa", resource);

从服务器端对其进行调试后,似乎请求已到达服务器。我可以在服务器端看到以下错误:

[{error=Unsupported Media Type, exception=org.springframework.web.HttpMediaTypeNotSupportedException, message=Content type 'application/octet-stream' not supported, status=415, timestamp=1532557180124}] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@74d4827a]

因此,从服务器端日志中,由于将内容类型设置为

,因此我不确定在哪里将内容类型添加为应用程序/八位字节流。
headers.setContentType(MediaType.MULTIPART_FORM_DATA);

下面是来自服务器控制器的代码。服务器端代码使用Spring Boot。

    @RequestMapping(method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE,consumes = {"multipart/form-data"})
        @ResponseBody
        public MyResponse uploadPhoto(@RequestPart(value = "aaa", required = false) Optional<MyRequest> myRequest,
                                    @RequestPart(value = "file", required = false) Optional<MultipartFile> file,
                                    HttpServletRequest request) {
//some logic
            return myResponse;
        }

服务器代码具有拦截器,在这里我可以看到我的请求的内容类型为multipart / form-data。它没有到达RestController

在2种情况下调试服务器端代码时:

  1. 邮递员请求

enter image description here

  1. 客户代码请求

enter image description here

当我从 POSTMAN 发布时,我发现文件iteam的内容类型为 application / json ,而内容类型为 application / octet-stream (当请求来自客户端代码)时。

在我的客户端代码中,我将创建JSONObject为

JSONObject json = new JSONObject();
json.append("myKey", "myValue");

并将其转换为字节数组

json.toString().getBytes("UTF-8")

然后我遵循了this。我的代码不同之处在于,由于无法创建文件,我将JSONObject作为字节流发送(性能问题)。

而且我无法以字符串形式发送JSONObject,因为服务器期望文件和aaa

的多部分表单数据

我已将restTemplate创建为

 public RestTemplate myRestTemplate() {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
        requestFactory.setReadTimeout(HTTP_CLIENT_TIMEOUT);
        requestFactory.setConnectTimeout(HTTP_CLIENT_TIMEOUT);

        RestTemplate restTemplate = new RestTemplate(requestFactory);
        List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
        messageConverters.add(new FormHttpMessageConverter());
        messageConverters.add(new StringHttpMessageConverter());
        restTemplate.setMessageConverters(messageConverters);
        return restTemplate;

这是调用服务的客户端代码:

public Optional<JSONObject> callService(byte[] multipartFile) {
        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
        InputStream stream = new ByteArrayInputStream(multipartFile);
        MultipartByteArrayResource resource = new MultipartByteArrayResource(multipartFile,fileName);


       body.add("aaa", resource);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);

        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);

        try {
            response =  restTemplate.postForObject(url, requestEntity , String.class);


        } catch (Exception exception) {
            LOG.error("Error", exception);
            return Optional.empty();
        }
    }

   public class MultipartInputStreamFileResource extends InputStreamResource {

        private final String filename;

        MultipartInputStreamFileResource(InputStream inputStream, String filename) {
            super(inputStream);
            this.filename = filename;
        }

        @Override
        public String getFilename() {
            return this.filename;
        }

        @Override
        public long contentLength() throws IOException {
            return -1; // we do not want to generally read the whole stream into memory ...
        }
}

当我发送文件时,相同的代码也可以工作(注意 file aaa 是两个不同的东西,尽管两者都是 multipart / form-data 服务器端。文件只是任何时间的文件(图像/文本/ pdf),但 aaa json数据文件

更多调试之后,我观察到服务器端控制器期望文件内容为json,因为Jackson尝试将该json反序列化为 MyRequest 对象。当我从POSTMAN发送帖子时,它具有json内容,可以正常工作,但是从客户端代码来看,该内容为 byteArray ,并且不会反序列化为 MyRequest 对象。不确定如何解决

2 个答案:

答案 0 :(得分:1)

最后,我解决了这个问题。如前所述,从POSTMAN与代码发送请求时,具有不同内容类型的multipart文件是我的开始。如果有人有任何问题,我将详细解释。

    public Optional<JSONObject> save(byte[] multipartFile, String fileName) {
        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
        Resource content = new MultipartByteArrayResource(multipartFile , fileName);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<Resource> requestEntityBody = new HttpEntity<Resource>(content, headers);
        body.add("aaa", requestEntityBody);
        String result = "";
        JSONParser parser = new JSONParser();
        JSONObject json = null;


        HttpHeaders requestHeaders = new HttpHeaders();
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, requestHeaders);
        ResponseEntity<String> response = null;
        try {
           RestTemplate restTemplate = customizeRestTemplate(); //I have defined this in different config file in my actual code
           response =  restTemplate.exchange(url , HttpMethod.POST , requestEntity , String.class);
           result = (response != null && response.getBody() != null) ? response.getBody().toString() : result;
           json = (JSONObject) parser.parse(result);
           LOG.info( "Response:", response );

        } catch (Exception exception) {
            LOG.error("Error , exception);
            return Optional.empty();
        }
        return Optional.ofNullable(json);
    }

   public class MultipartByteArrayResource extends ByteArrayResource{

       private String fileName;

        public MultipartByteArrayResource(byte[] byteArray , String filename) {
               super(byteArray);
               this.fileName = filename;
           }

        public String getFilename() { 
            return fileName; 
          }

        public void setFilename(String fileName) {
            this.fileName= fileName;
         }

     }

      public RestTemplate customizeRestTemplate() {

            SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
            requestFactory.setReadTimeout(10000);
            requestFactory.setConnectTimeout(10000);

            RestTemplate restTemplate = new RestTemplate(requestFactory);
            List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
            messageConverters.add(new FormHttpMessageConverter());
            messageConverters.add(new StringHttpMessageConverter());
            restTemplate.setMessageConverters(messageConverters);
            return restTemplate;
        }

}

答案 1 :(得分:0)

服务器端异常是由org.springframework.http.converter.json.MappingJackson2HttpMessageConverter产生的。 Jackson是一个JSON库,Spring使用MessageConverter格式化请求和响应。

在服务器具有@Produces(APPLICATION_JSON)注释的同时,客户端是否可以发送“接受:应用程序/八位字节流”?这意味着服务器将处理请求,并且仅在发送响应时遇到问题。您可以在服务器中添加一些log.info()语句以进行验证。