스프링 웹 MVC-10.HTTP 메시지 컨버터(개요, JSON, XML)
HTTP 메시지 컨버터 1부: 개요
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.html#configureMessageConverters-java.util.List-
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/WebMvcConfigurer.html#extendMessageConverters-java.util.List-
HTTP 메시지 컨버터
- 요청 본문에서 메시지를 읽어들이거나(@RequestBody), 응답 본문에 메시지를 작성할 때(@ResponseBody) 사용한다.
기본 HTTP 메시지 컨버터
- 바이트 배열 컨버터
- 문자열 컨버터
- Resource 컨버터
- Form 컨버터 (폼 데이터 to/from MultiValueMap<String, String>)
() 괄호는 의존성이 있는 경우만. - (JAXB2 컨버터)
- (Jackson2 컨버터)
- (Jackson 컨버터)
- (Gson 컨버터)
- (Atom 컨버터)
- (RSS 컨버터) …
설정 방법
- 기본으로 등록해주는 컨버터에 새로운 컨버터 추가하기: extendMessageConverters
- 기본으로 등록해주는 컨버터는 다 무시하고 새로 컨버터 설정하기: configureMessageConverters
- 의존성 추가로 컨버터 등록하기 (추천)
- 메이븐 또는 그래들 설정에 의존성을 추가하면 그에 따른 컨버터가 자동으로 등록 된다.
- WebMvcConfigurationSupport
- (이 기능 자체는 스프링 프레임워크의 기능임, 스프링 부트 아님.)
참고
- https://www.baeldung.com/spring-httpmessageconverter-rest
HTTP 메시지 컨버터 2부: JSON
스프링 부트를 사용하지 않는 경우
- 사용하고 싶은 JSON 라이브러리를 의존성으로 추가
- GSON
- JacksonJSON
- JacksonJSON 2
스프링 부트를 사용하는 경우
- 기본적으로 JacksonJSON 2가 의존성에 들어있다.
- 즉, JSON용 HTTP 메시지 컨버터가 기본으로 등록되어 있다.
참고
- JSON path 문법
- https://github.com/json-path/JsonPath
- http://jsonpath.com/
@RestController // Rest 붙이면 이 클래스의 모든 메서드에 @ResponseBody 붙인것과 동일
public class SampleController {
@GetMapping("/message")
public String message(@RequestBody String body) { // @RequestBody 요청시 본문에 들어있는 메세지를 HTTP 메세지 컨버터를 사용해서 컨버전을 한다.
System.out.println("message");
return body;
}
@GetMapping("/jsonMessage")
public Person jsonMessage(@RequestBody Person person){ // 부트는 기본적으로 jackson2가 제공하는 objectMapper 사용해서 컨버전
return person;
}
}
@AutoConfigureMockMvc
class SampleControllerTest {
@Autowired
MockMvc mockMvc;
@Autowired
ObjectMapper objectMapper;
@Test
public void jsonMessage() throws Exception {
Person person = new Person();
person.setId(1l);
person.setName("jaeuk");
String jsonString = objectMapper.writeValueAsString(person);
System.out.println(jsonString);
this.mockMvc.perform(get("/jsonMessage")
.contentType(MediaType.APPLICATION_JSON) // 제이슨으로 컨텐츠를 보낼거고
.accept(MediaType.APPLICATION_JSON) // 제이슨으로 응답이 오길 바란다.
.content(jsonString))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.name").value("jaeuk"));;
}
테스트 성공
{"id":1,"name":"jaeuk"}
MockHttpServletRequest:
HTTP Method = GET
Request URI = /jsonMessage
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Accept:"application/json", Content-Length:"23"]
Body = {"id":1,"name":"jaeuk"}
Session Attrs = {}
Handler:
Type = me.jaeuk.webmvc.SampleController
Method = me.jaeuk.webmvc.SampleController#jsonMessage(Person)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"application/json"]
Content type = application/json
Body = {"id":1,"name":"jaeuk"}
Forwarded URL = null
Redirected URL = null
Cookies = []
HTTP 메시지 컨버터 3부: XML
OXM(Object-XML Mapper) 라이브러리 중에 스프링이 지원하는 의존성 추가
- JacksonXML
- JAXB
스프링 부트를 사용하는 경우
- 기본으로 XML 의존성 추가해주지 않음.
JAXB 의존성 추가
<!-- jaxb 인터페이스 -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<!-- jaxb 구현체 -->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
<!-- xml->객체, 객체->xml 변환 (마샬러)-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${spring-framework.version}</version>
</dependency>
Marshaller 등록
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public Jaxb2Marshaller jaxb2Marshaller() {
Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
String name = Person.class.getPackage().toString(); // getPackageName 8에는 없음.
System.out.println(name); // 이게 package me.jaeuk.webmvc 이렇게 출력됨..
name = name.replace("package ", "");
System.out.println(name);
jaxb2Marshaller.setPackagesToScan(name);
return jaxb2Marshaller;
}
도메인 클래스에 @XmlRootElement 애노테이션 추가
import org.springframework.oxm.Marshaller; // 잘못 받지 않도록 주의
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamResult;
@ExtendWith(SpringExtension.class)
@SpringBootTest
@AutoConfigureMockMvc
class SampleControllerTest {
@Autowired
MockMvc mockMvc;
@Autowired
ObjectMapper objectMapper;
@Autowired
Marshaller marshaller;
@Test
public void xmlMessage() throws Exception {
Person person = new Person();
person.setId(1l);
person.setName("jaeuk");
StringWriter stringWriter = new StringWriter();
Result result = new StreamResult(stringWriter);
marshaller.marshal(person, result);
String xmlString = stringWriter.toString();
//System.out.println(xmlString);
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/name").string("jaeuk"))
.andExpect(xpath("person/id").string("1"));
}
..
참고
- Xpath 문법
- https://www.w3schools.com/xml/xpath_syntax.asp
- https://www.freeformatter.com/xpath-tester.html