복붙노트

[SPRING] 415 REST 템플릿을 통해 json 파일을 보내는 동안 지원되지 않는 미디어 유형

SPRING

415 REST 템플릿을 통해 json 파일을 보내는 동안 지원되지 않는 미디어 유형

REST 템플릿을 통해 json 파일을 보내려고합니다. MULTIPART_FORM_DATA로 POST 사람을 통해 보낼 때 제대로 작동합니다. 내가 제공해야하는 이름은 구체적입니다 (aaa라고 말하십시오). POSTMAN의 스크린 샷 첨부. 하지만 다른 stackoverflow 게시물에 지정된 코드에서 동일한 시도 할 때 415 지원되지 않는 미디어 형식 오류가 발생합니다.

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 

지정한 답이 나와 맞지 않을 때 중복으로 표시하지 마십시오. 내 코드가 다음 코드를 제외하고는 코드를 공유하지 않습니다.

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]

그래서, 서버 측 로그에서 컨텐트 유형을 application / octet-stream으로 추가 할 때 콘텐츠 유형이 다음과 같이

headers.setContentType(MediaType.MULTIPART_FORM_DATA);

아래는 서버 컨트롤러의 코드입니다. 서버 측 코드는 스프링 부팅을 사용합니다.

    @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 가지 경우에 서버 측 코드를 디버깅 할 때 :

내가 파일을 알아 낸 한가지는 POSTMAN에서 게시 할 때 application / json으로 콘텐츠 유형이 있고 요청이 클라이언트 측 코드에서 나올 때 콘텐츠 유형이 application / octet-stream이었습니다.

클라이언트 측 코드에서 JSONObject를 다음과 같이 작성합니다.

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

바이트 배열로 변환

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

나는 이것을 따라 갔다. 차이점은 파일을 만들 수 없기 때문에 JSONObject를 바이트 스트림으로 전송한다는 것입니다 (성능 문제).

그리고 JSONObject를 문자열로 보내지 않습니다. 서버는 파일과 aaa에 대해 multipart-form-data를 기다리고 있습니다.

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

그리고 동일한 코드는 파일을 보낼 때 작동합니다 (노트 파일과 aaa는 두 가지가 있지만 서버 쪽에서 multipart / form-data입니다. 파일은 언제든지 파일 (image / text / pdf)이지만 aaa는 json 데이터 파일입니다. )

조금 더 디버깅 한 후 Jackson이 MyRequest 객체에 json을 역 직렬화하려고 시도 할 때 서버 측 컨트롤러는 파일 내용이 json이 될 것으로 예상합니다. POSTMAN에서 게시물을 보낼 때 JSON 내용이 예상대로 작동하지만 클라이언트 측 코드에서 내용이 byteArray이며 MyRequest 객체로 deserialize되지 않습니다. 이 문제를 해결하는 방법을 모릅니다.

해결법

  1. ==============================

    1.마지막으로이 문제를 해결했습니다. 질문에서 언급했듯이, POSTMAN 코드에서 요청을 보내는 동안 멀티 파트 파일의 다른 콘텐츠 유형을 갖는 것은 내가 시작한 곳입니다. 누구든지 질문이 있으면 자세히 설명하겠습니다.

    마지막으로이 문제를 해결했습니다. 질문에서 언급했듯이, POSTMAN 코드에서 요청을 보내는 동안 멀티 파트 파일의 다른 콘텐츠 유형을 갖는 것은 내가 시작한 곳입니다. 누구든지 질문이 있으면 자세히 설명하겠습니다.

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

    2.서버 측 예외는 org.springframework.http.converter.json.MappingJackson2HttpMessageConverter에 의해 생성됩니다. Jackson은 JSON 라이브러리이고 MessageConverter는 요청과 응답의 형식을 지정하기 위해 Spring에서 사용합니다.

    서버 측 예외는 org.springframework.http.converter.json.MappingJackson2HttpMessageConverter에 의해 생성됩니다. Jackson은 JSON 라이브러리이고 MessageConverter는 요청과 응답의 형식을 지정하기 위해 Spring에서 사용합니다.

    서버가 @Produces (APPLICATION_JSON) 주석을 가지고있는 동안 클라이언트가 "Accept : application / octet-stream"을 전송할 수 있습니까? 이는 서버가 요청을 처리하고 응답을 보내는 데 문제가 있음을 의미합니다. 이를 확인하기 위해 서버에 log.info () 문을 추가 할 수 있습니다.

  3. from https://stackoverflow.com/questions/51527666/415-unsupported-media-type-while-sending-json-file-over-rest-template by cc-by-sa and MIT license