415 Unsupported Media Type while sending json file over REST Template

女生的网名这么多〃 提交于 2019-12-02 06:42:42

问题


I am trying to send a json file over REST Template. When I send it via POST man as MULTIPART_FORM_DATA, it works fine. The name I am supposed to give is specific (lets say aaa). Attached screenshot of POSTMAN. But when I try same in code as specified in another stackoverflow post, I get 415 Unsupported Media Type error as

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 

Please do not mark it as duplicate as the specified answer did not work for me. Not sharing code as my code is exactly same as this except

requestParamerterMap.add("attachment", resource);

where as my code is

requestParamerterMap.add("aaa", resource);

After debugging it from the server side, looks like request is reaching out to server. I was able to see below error in the server side:

[{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]

So, from the server side logs, I am not sure where the content type is getting added as application/octet-stream as I have set the content type as

headers.setContentType(MediaType.MULTIPART_FORM_DATA);

Below is the code from server controller. Server side code uses 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;
        }

The server code has an interceptor where I can see my request has content type as multipart/form-data. It does not reach to RestController

When I debugged the server side code in 2 cases:

  1. POSTMAN request

  1. client code request

One thing I figured out that file iteam has content type as application/json when I post from POSTMAN and the content type was application/octet-stream when the request goes from client side code.

In my client side code, I am creating JSONObject as

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

and convert it to byte array as

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

then I have followed this . The difference in my code is, I am sending my JSONObject as byte stream as I can not create file (performance issues).

And I cant not send JSONObject as string as server is expecting multipart-form-data for both file and aaa

I have created the restTemplate as

 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;

Here is the client side code which calls the service:

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 ...
        }
}

And same code works when I send file (note file and aaa are two different things though both are multipart/form-data in server side. file is just a file of any time (image/text/pdf) but aaa is json data file)

After debugging little bit more, what I observed is server side controller is expecting the file content to be json as Jackson try to deserialize that json to MyRequest object. When I send post from POSTMAN, it has the json content so working as expected but from the client side code, the content is byteArray, and its not getting deserialize to MyRequest object. Not sure how to fix this


回答1:


Finally I solved this issue. As mentioned in question, having different content type of multipart file while sending request from POSTMAN vs code is where I began with. I will explain in details if anyone has any questions.

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

}



回答2:


The server-side exception is produced by org.springframework.http.converter.json.MappingJackson2HttpMessageConverter. Jackson is a JSON library and MessageConverter are used by Spring to format requests and responses.

Can it be that the client sends an "Accept: application/octet-stream" while the server has a @Produces(APPLICATION_JSON) annotation? That would mean that the server processes the request and only has problems sending the response. You could add some log.info() statements in the server to verify this.



来源:https://stackoverflow.com/questions/51527666/415-unsupported-media-type-while-sending-json-file-over-rest-template

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!