- @RequestMapping의 produces,Content-Type,Consumes란? 목차
| @RequestMapping의 produces 속성을 이용하여 Response의 Content-Type을 제어할 수 있다.
@Consumes : 수신 하고자하는 데이터 포맷을 정의한다.
@Produces : 출력하고자 하는 데이터 포맷을 정의한다.
실시간 베스트 글
혹시 국비지원이나 사설학원 (패스트 캠퍼스 등) 다니는 중이신가요?
현직 5년차가 직접 겪은 국비지원 패스트캠퍼스 후기 보러가기
1.요청헤더 사용하기
최근의 추세는 요청 URl을 통해 응답할 데이터를 정하는 것이 아니라, 요청시 Accept header 를 이용하여 응답 받고자 하는 dataType을 요청 헤더 에 적도록 하고, Content-Type 이란 헤더를 통하여 사용자가 Request Body에 담은 내용을 알리도록 하고 있습니다.
1.1 요청 페이로등 타입 제한하기 (consumes)
@Controller
public class Test{
@GetMapping("/test", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public String hello(){
return "hello";
}
@RequestMapping, @GetMapping 등등 어노테이션 속성으로 "consumes" 이라는 속성이 존재합니다.
이 속성을 이용하면 사용자가 Request Body에 담는 타입을 제한할 수 있습니다.
즉, (서버에서) 이 핸들러에서는 body에 담긴 데이터의 타입이
APPLIACTION_JSON_UTF8일 경우의 요청만을 처리한다는 의미입니다.
따라서 요청시 헤더에 꼭 application/json 이 존재해야합니다.
Test
@Test
public void testHelloHandler() throws Exception{
mockMvc.perform(post("/hello"))
.andExpect(status().isOK());
}
위와 같이 테스트 코드를 작성한 뒤 테스트를 진행하면
이렇게 416 ERROR가 나타납니다 이유는 지원하지 않는 body 데이터 형식을 header에 설정해 요청했기 때문입니다.
Header를 확인하면 아무런 header가 들어있지 않는 것을 볼 수 있습니다.
ContentType
@Test
public void testMethod(){
mockMvc.perform(post("/hello")
.contentType(MediaType.APPLIACTION_JSON_UTF8))
.andDo(print())
.andExpect(status().isOK());
}
위와 같이 contentType() 을 이용하여 테스트 요청에 header를 추가할 수 있습니다.
여기에 MediaType.Type을 이용하여 원하는 타입의 헤더를 넣어 테스트를 할 수 있습니다
또 andDo(print())를 추가하면 요청,응답 결과를 console에서 확인할 수 있습니다.
->요청 Headersfmf ghkrdlsgkaus 핸들러에서 제한한 타입의 header가 담겨 있는 것을 볼 수 있습니다.
RestAPI의 경우 보통 JSON 타입으로 요청하고, 요청을 받습니다.
그래서 당연히 application/json 타입으로 Content-Type을 사용한다고 생각을 하는 경우가 많습니다.
하지만 자료를 찾아보니 그렇지 않다는 것을 알게되었습니다.
!!
html form 태그를 사용하여 post 방식으로 요청하거나
JQuery의 ajax 등의 요청을 할 때 default Content-Type은 "application/json"이 아니라
"application/x-www-form-urlencoded'이다.
기본 값 확인
위와 같은 코드로 client에서 server로 요청을 보낸다고 해보자.
Content-Type에 따라서 client에서 server로 보내는 데이터의 형식이 달라진다.
- > content-type이 따로 설정되어 있지 않기 때문에 기본값인 ( application/x-www-form-urlencoded:)로 나오고 그 결과 값 역시
이런식으로 출력이 된다.
지정 값으로 출력
Content-Type : appication/json 지정
이런식으로 출력이 된다.
Spring에서의 POST요청 데이터 받기
보통 Spring을 개발한 RestAPI에서 POST요청을 받을 때 json 형식으로 body에 들어 있다고 가정 하고 개발을 하게 된다.
따라서 body에서 json 객체를 꺼내어 알맞은 dto로 받기 위해 아래와 같은 코드를 짜게 됩니다.
이때, client에서 header에 content-type으로 'application/json'을 추가해서 보내지 않으면
에러가 발생합니다. 따라서 클라이언트에서 해결하기 위해서는 content-type을 application/json으로 보내야 합니다.
그럼 해결 방법은 과연 무엇일까?
바로, @RequestBody , @ModelAttribute, @RequestParam 등 어노테이션을 사용하게 되면 해당 어노테이션과 매칭되는 "메시지 컨버터" 가 AnnotationMethodHandlerAdapter에 의해 등록되게 됩니다.
model객체 앞에 @RequestBody 어노테이샨을 붙이면 AnnotationMethodHandlerAdapter에 의해 MappingJacksonHttpMessageConverter가 등록되게 됩니다.
MappingJackson2HttpMessageConverter는 HttpMessageConverter의 구현체로 JSON형식의 데이터가 들어오면
(당연히 content-type은 application/json) 해당 json 데이터를 jackson 라이브러리를 사용하여 model객체로 변환해게 됩니다.
*Content-Type : application/json이고 json타입의 데이터
@ModelAttribute Person person
model 객체와 함게 @ModelAttribute를 사용하면 FormHttpMessageConverter가 등록되어
key = value 를 model로 converting 하게 됩니다.
앞에 아무런 어노테이션도 안붙이고 Person person 으로 선언하면 @ModelAttribute가 암묵적으로 사용됩니다.
FormHttpMessageConverter는 MediaType이 application/x-www-form-urlencoded로 정의된 폼 데이터를 주고 받을 때사용하게 됩니다.
위에서 정리한 것 처럼 가장 좋은 방법은 API명세를 정하여 client 들은 application/json 미디어 타입과 함께
son 방식으로 요청하도록 강제하는 것이 깔끔하고 좋다.
하지만 실무에서는 "저희 부서는 이미 모든 요청을 x-www-url-encoded로 하고 있어서 json 으로 바꿀 수가 없습니다".. 와 같은 요청이 들어오는데
기존에 jsonn 방식으로 요청을 날리던 유저들에게는 그대로 json 방식을 제공하되,
추가적으로 x-www-url-encoded 방식도 제공을 해야하는 상황이다.
위에 사진 처럼 두개의 메소드를 만들어주면된다.
첫 번째 메소드는 json 타입을 두 번째 메소드는 form-urlencoded 타입을 받게 된다.
첫 번째 결과 (JSON TYPE)의 요청
두 번째 결과 ( X-WWW-form-urlencoded 타입의 요청 )
정리하자면 단순히 "POST 방식에서는 @RequestBody를 써야한다" 거나 반대로 "@ModelAttrubute는 GET방식에서만 쓸 수 있다" 는 고정관념을 깨자 !
| 응답 데이터 제한하기(produces,headers)
@PostMapping( value ="/hello", produces = MediaType.TEXT_PLAIN_VALUE)
@ResponseBody
public String hello(){
return "hello";
}
위 코드와 같이 @Mapping 언노테이션의 속성으로 produces 를 추가하고 data Type을 지정하면 해당 dataType으로만 사용자에게 응답하겠다는 의미입니다.
test code에서 accept()를 이용하여 요청 헤더에 accept를 추가합니다.
JSON 응답을 원하는 요청을 만들어 테스트를 해보겠습니다. 406ERROR(Not Supported)가 나타납니다.
@Test
public void testHello()throws Exception{
mockMvc.perform(post("/hello")
.accept(MediaType.APPLICATION_JSON_UTF8))
.andDo(print())
.andExpect(status().isOK());
}
| headers ( HttpHeaders )
이번에는 headers 속성을 사용하는 방법을 알아보도록 하겠습니다.
headers라는 속성은 사용자가 요청하는 header에 특정 header가 존재하도록 제한할 때 사용할 수 있습니다.
@PostMapping( value="/hello", headers = {HttpHeaders.FROM} )
@ResponseBody
public String hello(){
return "hello";
}
위와 같이 headers에는 배열로 값을 줄 수 있습니다 ( 하나도 가능합니다.)
@Test
public void testHello()throws Exception{
mockMvc.perform(post("/hello")
.header(HttpHeaders.FROM,"localhost"))
.andDo(print())
.andExpect(status().isOk());
}
//결과는 tets 케이스가 통과합니다.
headers에는 문자열을 이용해 값이 존재할 때, 아닐때(!), ~와 같을 때 (=) 등의 경우를 만들 수도 있습니다.
header에 Authorization 헤더가 존재하지 않은 경우에 응답을 받겠다는 의미입니다.
'Spring' 카테고리의 다른 글
@RequestBody,@ResponseBody (0) | 2020.07.25 |
---|---|
메시지 컨버터란? (0) | 2020.07.25 |
커맨드 객체란? (0) | 2020.07.25 |
@RequestParam과 @PathVariable? (0) | 2020.07.25 |
[Spring]ResponseEntity (0) | 2020.03.11 |