Body로 전달된 JSON/XML 데이터를 처리하는 HTTP Message Converter
이 글의 목적?
바디로 전달된 JSON/XML 데이터를 어떻게 객체로 변환할 수 있을까?
HTTP Message Converter?
요청 본문에서 메시지를 읽거나 응답 본문에 메시지를 작성할 때 사용한다. 즉, @RequestBody, @ResponseBody
어노테이션을 사용할 때 요청/응답을 처리해주는 변환기다.
의존성에 따라 조건적으로 등록이 되는데, 이 설정은 WebMvcConfigurationSupport 클래스의 코드에서 확인할 수 있다.
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
// (생략...)
static {
ClassLoader classLoader = WebMvcConfigurationSupport.class.getClassLoader();
romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);
gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader);
kotlinSerializationJsonPresent = ClassUtils.isPresent("kotlinx.serialization.json.Json", classLoader);
}
protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(new StringHttpMessageConverter());
messageConverters.add(new ResourceHttpMessageConverter());
messageConverters.add(new ResourceRegionHttpMessageConverter());
if (!shouldIgnoreXml) {
try {
messageConverters.add(new SourceHttpMessageConverter());
} catch (Throwable var3) {
}
}
messageConverters.add(new AllEncompassingFormHttpMessageConverter());
if (romePresent) {
messageConverters.add(new AtomFeedHttpMessageConverter());
messageConverters.add(new RssChannelHttpMessageConverter());
}
Jackson2ObjectMapperBuilder builder;
if (!shouldIgnoreXml) {
if (jackson2XmlPresent) {
builder = Jackson2ObjectMapperBuilder.xml();
if (this.applicationContext != null) {
builder.applicationContext(this.applicationContext);
}
messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));
} else if (jaxb2Present) {
messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
}
}
if (kotlinSerializationJsonPresent) {
messageConverters.add(new KotlinSerializationJsonHttpMessageConverter());
}
if (jackson2Present) {
builder = Jackson2ObjectMapperBuilder.json();
if (this.applicationContext != null) {
builder.applicationContext(this.applicationContext);
}
messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));
} else if (gsonPresent) {
messageConverters.add(new GsonHttpMessageConverter());
} else if (jsonbPresent) {
messageConverters.add(new JsonbHttpMessageConverter());
}
if (jackson2SmilePresent) {
builder = Jackson2ObjectMapperBuilder.smile();
if (this.applicationContext != null) {
builder.applicationContext(this.applicationContext);
}
messageConverters.add(new MappingJackson2SmileHttpMessageConverter(builder.build()));
}
if (jackson2CborPresent) {
builder = Jackson2ObjectMapperBuilder.cbor();
if (this.applicationContext != null) {
builder.applicationContext(this.applicationContext);
}
messageConverters.add(new MappingJackson2CborHttpMessageConverter(builder.build()));
}
}
// (생략...)
}
HTTP Message Converter에서 알아야하는 점은, 스프링 부트의 기능이 아닌 스프링 프레임워크 자체의 기능이라는 점이다. 단지 스프링 부트에는 기본적으로 JacksonJSON2가 의존성이 들어가있다는 점 밖에 없다. (스프링 부트를 사용하지 않을 경우에는 직접 JSON 라이브러리를 의존성에 추가해주어야 한다.)
기본적으로 많은 HTTP Message Converter를 제공하고 있어, 추가하는 일은 거의 없을 것 같다. 😆 하지만 새로운 Converter를 추가하고자 할 때는 WebConfigurer 인터페이스를 사용하여 등록하거나 간단하게 의존성 추가만으로도 등록할 수 있다. (강의에서 백기선님은 의존성 추가로 등록하는 방법을 추천하셨다.)
WebConfigurer 인터페이스에 등록할 때는 두 개의 메서드로 추가가 가능하다.
configureMessageConverters()
메서드는 기본적으로 등록되어있는 Converter를 모두 무시하기 때문에 extendMessageConverters()
메서드를 이용하여 기본적으로 등록되어있는 Converter에서 추가하는 형태로 등록하는 편이 안전해보인다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
// @Override
// public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// // 추가할 경우에는 기본적으로 제공하는 메시지 컨버터를 사용할 수 없음
// }
// @Override
// public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
// // 기본적으로 제공하는 메시지 컨버터에 추가
// }
}
@RestController
public class SimpleController {
@GetMapping("/message")
public String messageString(@RequestBody String body) {
return "message " + body;
}
}
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class SimpleControllerTest {
@Autowired
MockMvc mockMvc;
@Test
public void messageString() throws Exception {
this.mockMvc.perform(get("/message").content("body"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string("message body"));
}
}
JSON
스프링 부트에서는 기본적으로 JSON 의존성이 존재하기에 별도로 의존성을 추가하지 않고 확인할 수 있다.
@RestController
public class SimpleController {
@GetMapping("/jsonmessage")
public Person jsonmessage(@RequestBody Person person) {
return person;
}
}
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class SimpleControllerTest {
@Autowired
MockMvc mockMvc;
@Autowired
ObjectMapper objectMapper;
@Test
public void jsonmessage() throws Exception {
Person person = new Person();
person.setId(10L);
person.setName("kevin");
// 객체를 JSON으로 변환
String jsonString = objectMapper.writeValueAsString(person);
this.mockMvc.perform(get("/jsonmessage")
.contentType(MediaType.APPLICATION_JSON) // JSON 요청을 보냄
.accept(MediaType.APPLICATION_JSON) // JSON 응답을 기대
.content(jsonString))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(10L))
.andExpect(jsonPath("$.name").value("kevin"));
}
}
테스트 코드에서 사용한 JSON Path 문법은 하단의 링크를 참고하여 작성해주면 된다.
XML
JSON과 다르게 스프링 부트는 기본적으로 XML 의존성을 추가해주지 않기 때문에 별도로 추가를 해주어야 한다.
OXM(Object-XML Mapper) 라이브러리 중에서 스프링이 지원하는 의존성에는 JacksonXML과 JAXB가 있으니 JAXB를 추가해준다.
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
<!-- xml을 객체로 변환(Marshalling) / 객체를 xml로 변환(UnMarshalling) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${spring-framework.version}</version>
</dependency>
그 다음으로는 빈으로 Marshaller를 등록해주고, XML로 변환시킬 클래스에 @XmlRootElement
어노테이션을 붙여준다.
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public Jaxb2Marshaller jaxb2Marshaller() {
Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
jaxb2Marshaller.setPackagesToScan(Person.class.getPackageName()); // @XmlRootElement 어노테이션 스캔
return jaxb2Marshaller;
}
}
@XmlRootElement
@Getter
@Setter
@Entity
public class Person {
@Id @GeneratedValue
private Long id;
private String name;
}
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class SimpleControllerTest {
@Autowired
MockMvc mockMvc;
@Autowired
Marshaller marshaller; // WebConfig에 등록한 빈을 주입 받음
@Test
public void xmlmessage() throws Exception {
Person person = new Person();
person.setId(10L);
person.setName("kevin");
// 객체를 XML로 변환
StringWriter stringWriter = new StringWriter();
Result result = new StreamResult(stringWriter);
marshaller.marshal(person, result);
String xmlString = stringWriter.toString();
this.mockMvc.perform(get("/jsonmessage")
.contentType(MediaType.APPLICATION_XML) // XML 요청을 보냄
.accept(MediaType.APPLICATION_XML) // XML 응답을 기대
.content(xmlString))
.andDo(print())
.andExpect(status().isOk())
.andExpect(xpath("person/id").string("10"))
.andExpect(xpath("person/name").string("kevin"));
}
}
테스트 코드에서 사용한 XML Path 문법은 하단의 링크를 참고하여 작성해주면 된다.
이 글의 레퍼런스
- 백기선님의 스프링 웹 MVC
Author And Source
이 문제에 관하여(Body로 전달된 JSON/XML 데이터를 처리하는 HTTP Message Converter), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://velog.io/@maketheworldwise/Body로-전달된-JSONXML-데이터를-처리하는-HTTP-Message-Converter
저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
Author And Source
이 문제에 관하여(Body로 전달된 JSON/XML 데이터를 처리하는 HTTP Message Converter), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@maketheworldwise/Body로-전달된-JSONXML-데이터를-처리하는-HTTP-Message-Converter저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)