[Spring] Content-Type 'application/octet-stream' is not supported
컨트롤러에서 이미지 파일과 dto를 잘 받아오는지 확인하려는 메소드를 실행했을때
@LoginRequired
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<HttpStatus> test(@RequestPart(value = "files") List<MultipartFile> files, @RequestPart(value = "postDto") PostDto postDto, @LoginMember MemberDto memberDto){
if(files.isEmpty()){
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
if(postDto == null || memberDto == null){
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
log.info(postDto.toString());
log.info(memberDto.toString());
return ResponseEntity.status(HttpStatus.OK).build();
}
Content-Type 'application/octet-stream' is not supported
415 오류 메시지가 나왔다.
application/octet-stream 타입은 뭘까?
MIME
Multipurpose Internet Mail Extensions
음악이나 사진, 비디오 등의 이진 파일을 네트워크를 통해 전송할 목적으로 텍스트로 변환하고, 이를 수신하는 입장에서 다시 파일화할 때 사용해야 할 기준을 제시하기 위한 목적으로 사용된다.
● multipart/form-data 타입
요청 바디에 담기는 내용의 종류가 하나가 아니라서 각 구성요소마다 따로 MIME타입을 지정해야하는 상황에서 사용한다.
● application/octect-stream 타입
모든 이진 파일들의 제네릭한 타입을 의미
-> 제네릭을 사용했다는 건 어떤 방법을 사용하여 텍스트를 해석해야할지 모를 때 사용하는 방법이다.
Content-Type 'application/octet-stream' is not supported 오류 메시지는
application/octet-stream 형식으로 요청을 받았으며 서버측에서는 이 형식을 지원하지 않기 때문에 발생하는 예외라고 한다.
컨트롤러에서 어노테이션으로 미디어 타입을 설정했는데 왜 application/octet-stream 으로 나올까??
스프링에서 request를 받으면 메시지 컨버터를 통해 JSON->자바 객체로 변환해준다.
HttpMessageConvert의 추상 구현체인 AbstractMessageConvertMethodArgumentResolver 를 통해 Http Request,Response를 자바 객체로 받을 수 있다.
이 때, contentType의 값이 null인 경우 디폴트로 application/octet-stream타입으로 지정해버린다.
이로 인해 서버측 예외 메시지에 해당 타입이 등장한 것이다.
multipart/form-data -> application/octect-stream 변환 원인
Http Request, Response를 자바 클래스로 만들어주는 HttpMessageConvert의 추상 구현체인 AbstractMessageConvertMethodArgumentResolve의 readWithMessageConverter메서드를 보면 contentType의 값이 null인 경우 디폴트로 application/octet-stream타입으로 지정해버린다. 이로 인해 서버 측 예외 메시지에는 해당 타입이 등장한 것이다.
따라서 이 문제를 해결하기 위해 application/octet-stream 타입을 처리할 수 있는 메시지 컨버터를 Spring컨텍스트에 추가해야 한다.
Custom converter생성
@Component
public class MultipartJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {
//"Content-Type: multipart/form-data"헤더를 지원하는 HTTP요청 반환기
public MultipartJackson2HttpMessageConverter(ObjectMapper objectMapper){
super(objectMapper, MediaType.APPLICATION_OCTET_STREAM);
}
/*
기존 application/octet-stream 타입을 쓰기로 다루는 메시지 컨버터가 이미 존재 (ByteArrayHttpMessageConverter)
따라서 해당 컨버터는 쓰기 작업에서 이용하면 안됨
*/
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return false;
}
@Override
public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {
return false;
}
@Override
public boolean canWrite(MediaType mediaType){
return false;
}
}
ObjectMapper객체를 생성자로 받는다.
ObjectMapper는 JSON데이터와 Java객체 간의 변환을 담당하는 Spring의 기본 JSON라이브러리이다.
커스텀 컨버터에서 HTTP응답 출력을 지원하지 않도록 canWrite()메서드들을 오버라이드하여 모두 false를 반환하도록 설정해야 한다. 따라서 이 메시지 컨버터는 주로 HTTP요청 데이터를 객체로 변환하는데 사용된다.
AbstractJackson2HttpMessageConverter를 상속받는 이유는 Jackson라이브러리 기반으로 문자열 형태의 json을 클래스로 변환해줄 수 있는데, 현재 서버 측에서 문제가 되는 경우는 Multipart/form-data 이하의 application/json 타입이 누락되는 것이기 때문이다.
결국 문제를 해결하기 위해서는 application/octet-stream타입을 다루는 HttpMessageConvert를 추가하면 된다.
즉, application/octet-stream타입을 처리할 수 있는 메시지 컨버터를 Spring컨텍스트에 추가해야 한다.
@Component어노테이션을 통해 빈으로 등록하면 Spring컨텍스트에서 이 클래스의 인스턴스가 생성되어 관리된다.
참고
https://tomining.tistory.com/190