이 글의 코드 및 정보들은 강의를 들으며 정리한 내용을 토대로 작성하였습니다.
@RequestBody
-
요청 Body에 있는 값을
HttpMessageConveter를 이용하여
특정 객체 타입으로 변환시킬 수 있다.
또한 @Valid와 @Validated를 사용하여 값을 검증할 수 있다.
How?
-
어떻게 가능할까?
WebConfig 파일에서
WebConfig
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
...
}
- @EnableWebMvc를 선언한다.
EnableWebMvc
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({DelegatingWebMvcConfiguration.class})
public @interface EnableWebMvc {
}
- 그러면 DelegatingWebMvcConfiguration를 Import 하는 것을 볼 수 있다.
DelegatingWebMvcConfiguration
@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();
public DelegatingWebMvcConfiguration() {
}
...
}
-
그리고 DelegatingWebMvcConfiguration는
다시 또 WebMvcConfigurationSupport를 확장(= extends)한다.
-
그리고 WebMvcConfigurationSupport 내부에 있는
addDefaultHttpMessageConverters 메소드에서 messageConverter를 등록해주는 코드를 찾을 수 있다.
WebMvcConfigurationSupport.addDefaultHttpMessageConverters( )
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
stringConverter.setWriteAcceptCharset(false);
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(stringConverter);
messageConverters.add(new ResourceHttpMessageConverter());
messageConverters.add(new SourceHttpMessageConverter());
messageConverters.add(new AllEncompassingFormHttpMessageConverter());
if (romePresent) {
messageConverters.add(new AtomFeedHttpMessageConverter());
messageConverters.add(new RssChannelHttpMessageConverter());
}
ObjectMapper objectMapper;
if (jackson2XmlPresent) {
objectMapper = Jackson2ObjectMapperBuilder.xml().applicationContext(this.applicationContext).build();
messageConverters.add(new MappingJackson2XmlHttpMessageConverter(objectMapper));
} else if (jaxb2Present) {
messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
}
if (jackson2Present) {
objectMapper = Jackson2ObjectMapperBuilder.json().applicationContext(this.applicationContext).build();
messageConverters.add(new MappingJackson2HttpMessageConverter(objectMapper));
} else if (gsonPresent) {
messageConverters.add(new GsonHttpMessageConverter());
}
}
}
Who use it?
-
그렇다면 이 messgaeConverts를 누가 사용할까?
Handler Mapping ?
Handler Adapter ?
View Resolver ?
정답은 Handler Adapter가 사용한다.
-
등록된 메시지 Converter들을
Handler Adapter가 사용해서 Argument Resolving에 사용한다.
-
즉 메소드 Argument를 Resolving 할 때
Handler Adapter에 등록된
여러 HttpMessageConveter 중에
이 요청에 들어있는 본문을 Converting 할 수 있는 Converter를 찾아서 Conversion을 한다.
-
예를 들어
Json 요청을 한다면
요청 헤더에 Content Type으로 Json임을 명시해줘야 한다.
-
그러면 그 Content Type을 보고
등록되어 있는 Converter들 중에서
Json Convert가 가능한 것을 찾아
Json 문자열을 해당 도메인 객체로 변경해준다.
Example Code
TC
@RunWith(SpringRunner.class)
@WebMvcTest
public class SampleControllerTest {
@Autowired
ObjectMapper objectMapper;
@Autowired
MockMvc mockMvc;
@Test
public void createEvent() throws Exception {
Event event = new Event();
event.setName("goodGid");
event.setLimit(10);
String json = objectMapper.writeValueAsString(event);
mockMvc.perform(post("/api/events")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(json)) // = 요청 내용 = Body
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("name").value("goodGid"));
}
}
Result
MockHttpServletRequest:
HTTP Method = POST
Request URI = /api/events
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8"]
Body = {"id":null,"name":"goodGid","limit":10}
Session Attrs = {}
한계
-
@RequestBody Annotation은
Header 정보에는 접근을 할 수 없다.
단순히 Body에 있는 정보만 접근이 가능하다.
-
그래서 그 대체재로 생각해 볼 수 있는 개념으로는
Header와 Body까지 접근이 가능한 HttpEntity가 있다.
Summary
-
기본적으로 HttpMessageConveter에
특정 객체 타입으로 변환하는 Converter가 등록되어 있다.
-
만약 HttpMessageConveter과 관련하여
설정하는 방법이 궁금하다면 HttpMessageConverter 설정하기글을 참고하자.