2016년 7월 20일 수요일

restTemplate 이 xml로 요청을 하는 경우

다음과 같은 경우 restTemplate 요청을 xml로 한다.
  1. jackson-dataformat-xml를 의존성으로 가지고 있다.
  2. new RestTemplate(); 으로 restTemplate을 생성한다.
  3. HttpMessageConvertersAutoConfiguration을 사용해서 messageConverters를 구현하지 않는다.
이 경우 RestTemplate의 생성자에 설정된 messageConverter 추가 규칙대로 converter를 호출하여 사용하게 된다.

public RestTemplate() {
    this.messageConverters.add(new ByteArrayHttpMessageConverter());
    this.messageConverters.add(new StringHttpMessageConverter());
    this.messageConverters.add(new ResourceHttpMessageConverter());
    this.messageConverters.add(new SourceHttpMessageConverter());
    this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());

    if (romePresent) {
        this.messageConverters.add(new AtomFeedHttpMessageConverter());
        this.messageConverters.add(new RssChannelHttpMessageConverter());
    }

    if (jackson2XmlPresent) {
        this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
    }
    else if (jaxb2Present) {
        this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
    }

    if (jackson2Present) {
        this.messageConverters.add(new MappingJackson2HttpMessageConverter());
    }
    else if (gsonPresent) {
        this.messageConverters.add(new GsonHttpMessageConverter());
    }
}


이 경우 jackson xml messageConverter가 json 보다 우선 추가가 되며 restTemplate 호출 시 HttpMessageConverterExtractor의 extractData 메소드에서 추가된 순서대로 converter를 찾아 canRead(Class clazz, MediaType mediaType) 호출이 true인 messageConverter를 사용하게 된다.

restTemplate을 별다른 설정없이 호출해서 사용하게 되면 mediaType은 null로 없다.

대부분의 messageConverter는 생성자에서 자신이 지원하는 mediaType을 선언하여 mediaType에 대한 제한을 두지만 MappingJackson2XmlHttpMessageConverter는 mediaType이 없는 경우도 canRead에서 true로 응답한다. (다르게 말하자면 제한을 두지 않는다.)

이에 대해 spring boot의 HttpMessageConverters 생성 시 reorderXmlConvertersToEnd 메소드를 통해 AbstractXmlHttpMessageConverter이나 MappingJackson2XmlHttpMessageConverter인 경우는 converters 리스트의 가장 맨뒤로 위치시키는 처리를 하여 가장 나중에 체크 되도록 하였다.

따라서 MappingJackson2XmlHttpMessageConverter에 의존성이 있으며 boot 의 autoConfiguration을 통한 생성을 하지 않은 new RestTemplate을 사용할 경우엔 converter에서 MappingJackson2XmlHttpMessageConverter를 제거하거나 아니면 HttpMessageConvertersAutoConfiguration을 통해 생성된 HttpMessageConverters를 constructor를 통해 넘겨주면 된다.


@Autowired
private HttpMessageConverters messageConverters;

new RestTemplate(messageConverters.getConverters());


spring boot를 사용하지 않고 restTemplate을 쓰는 경우 xmlConveter를 맨 뒤로 보내는 HttpMessageConverters의 reorderXmlConvertersToEnd method를 참고하면 된다.
private void reorderXmlConvertersToEnd(List> converters) {
    List> xml = new ArrayList>();
    for (Iterator> iterator = converters.iterator(); iterator.hasNext();) {
        HttpMessageConverter converter = iterator.next();
        if ((converter instanceof AbstractXmlHttpMessageConverter) || (converter instanceof MappingJackson2XmlHttpMessageConverter)) {
            xml.add(converter);
            iterator.remove();
        }
    }
    converters.addAll(xml);
}


restTemplate에서 xmlConverter를 안 쓴다면 restTemplate에서 해당 converter를 제거하는 방법도 있다.
RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().removeIf(messageConverter -> messageConverter instanceof MappingJackson2XmlHttpMessageConverter);


MessageConverter를 사용한다면 되도록 boot에서 bean으로 등록해준 messageConverter를 사용하는게 좋다

댓글 없음:

댓글 쓰기