@RequestMapping의 produces,Content-Type,Consumes란?

2020년 07월 25일 by Xion

    @RequestMapping의 produces,Content-Type,Consumes란? 목차

| @RequestMapping의 produces 속성을 이용하여 Response의 Content-Type을 제어할 수 있다.

 

@Consumes : 수신 하고자하는 데이터 포맷을 정의한다.

@Produces : 출력하고자 하는 데이터 포맷을 정의한다.

 

 

실시간 베스트 글

혹시 국비지원이나 사설학원 (패스트 캠퍼스 등) 다니는 중이신가요?

 

현직 5년차가 직접 겪은 국비지원 패스트캠퍼스 후기 보러가기

 

국비지원 패스트캠퍼스 후기 현직 5년차 개발자가 알려주는 코딩학원 현실

국비지원 패스트캠퍼스 후기 현직 개발자가 경험한 코딩학원 선택 기준 및 현실에 대해 알려드립니다. 이런 분들은 절대 수업 듣지도 마세요.

www.allinfospace.com

 

 

 

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가 등록되게 됩니다.

MappingJackson2HttpMessageConverterHttpMessageConverter의 구현체로 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암묵적으로 사용됩니다.

Content-Type : application/x-www-form-urlencoded 이고 key1=value1 타입의 data

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