-
[spring] 스프링에서 사용하는 RestTemplate - http 라이브러리Spring/spring boot 및 기타 2021. 2. 26. 14:06
RestTemplate이란
스프링에서 제공하는 http 통신에 유용하게 쓸 수 있는 템플릿
Spring 3부터 지원 되었고 REST API 호출이후 응답을 받을 때까지 기다리는 동기방식이다
AsyncRestTemplate
Spring 4에 추가된 비동기 RestTemplate이다
Spring 5.0에서는 deprecated 되었다
메소드
메서드
HTTP
설명
getForObject
GET
주어진 URL 주소로 HTTP GET 메서드로 객체로 결과를 반환받는다
getForEntity
GET
주어진 URL 주소로 HTTP GET 메서드로 결과는 ResponseEntity로 반환받는다
postForLocation
POST
POST 요청을 보내고 결과로 헤더에 저장된 URI를 결과로 반환받는다
postForObject
POST
POST 요청을 보내고 객체로 결과를 반환받는다
postForEntity
POST
POST 요청을 보내고 결과로 ResponseEntity로 반환받는다
delete
DELETE
주어진 URL 주소로 HTTP DELETE 메서드를 실행한다
headForHeaders
HEADER
헤더의 모든 정보를 얻을 수 있으면 HTTP HEAD 메서드를 사용한다
put
PUT
주어진 URL 주소로 HTTP PUT 메서드를 실행한다
patchForObject
PATCH
주어진 URL 주소로 HTTP PATCH 메서드를 실행한다
optionsForAllow
OPTIONS
주어진 URL 주소에서 지원하는 HTTP 메서드를 조회한다
exchange
any
HTTP 헤더를 새로 만들 수 있고 어떤 HTTP 메서드도 사용가능하다
execute
any
Request/Response 콜백을 수정할 수 있다
GET 메소드
getForObject()
Employee employee = restTemplate.getForObject(BASE_URL + "/{id}", Employee.class);
Employee 로의 매핑은 jackson-databind 가 기본적으로 담당하고 있다.
getForEntity()
응답을 ResponseEntity 객체로 받는다. getForObject()와 달리 HTTP 응답에 대한 추가 정보를 담고 있어서 GET 요청에 대한 응답 코드, 실제 데이터를 확인할 수 있다. 또한 ResponseEntity<T> 제네릭 타입에 따라서 응답을 String이나 Object 객체로 받을 수 있다.
ResponseEntity<String> responseEntity = restTemplate.getForEntity(BASE_URL + "/{id}", String.class, 25); log.info("statusCode: {}", responseEntity.getStatusCode()); log.info("getBody: {}", responseEntity.getBody());
getForEntity()에 여러 값을 담은 params을 같이 넘겨주기
LinkedMultiValueMap 객체에 담아서 params로 넘겨줄 수 있다.
MultiValueMap<String, String> params = new LinkedMultiValueMap<>(); params.add("name", "Frank Oh"); params.add("country", "US"); ResponseEntity<Employee> responseEntity = restTemplate.getForEntity(BASE_URL + "/{name}/{country}", Employee.class, params); log.info("statusCode: {}", responseEntity.getStatusCode()); log.info("getBody: {}", responseEntity.getBody());
get 요청에 header 값이 필요한 경우
get 메소드에서는 header 를 추가 할 수 가 없다.
exchange 메소드를 사용해야 한다.
HttpHeaders headers = new HttpHeaders(); headers.set("header", header); headers.set("header2", header2); HttpEntity request = new HttpEntity(headers); ResponseEntity<String> response = restTemplate.exchange( URL_PATH, HttpMethod.GET, request, String.class );
get 요청에 header 값 과 쿼리스트링(query String, param)이 필요한 경우
post 처럼 HttpEntity 에 넣어서 요청할 수가 없다.
HttpHeaders headers = new HttpHeaders(); headers.set("header", header); headers.set("header2", header2); HttpEntity request = new HttpEntity(headers); //adding the query params to the URL UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(URL_PATH) .queryParam("keywords", "11"); .queryParam("name", "22"); ResponseEntity<String> response = restTemplate.exchange( uriBuilder.toUriString(), HttpMethod.GET, request, String.class );
이렇게 UrlBuilder 를 사용해서 넣는 수 밖에 없다. post 방식과 달리 httpEntity 에 같이 넣거나 exchange 의 parameter 로 넘길 수가 없다.
사실 굳이 uriBuilder 를 써야 되나 싶기도 하다. 그냥 map 에 파라미터를 추가하고 map 을 parameter 로 변환해주는 메소드만 만들어서 사용하면 편할거 같다.
... HttpHeaders headers = new HttpHeaders(); headers.set("header", header); headers.set("header2", header2); HttpEntity request = new HttpEntity(headers); Map<String, String> params = new HashMap<String, String>(); params.put("query1", "test"); params.put("query2", "test2"); ResponseEntity<String> response = restTemplate.exchange( URL_PATH + "?" + this.mapToUrlParam(params), HttpMethod.GET, request, String.class ); ... // 위에서 사용하려고 만든 util 메소드 private static String mapToUrlParam(Map<String, Object> params) { StringBuffer paramData = new StringBuffer(); for (Map.Entry<String, Object> param : params.entrySet()) { if (paramData.length() != 0) { paramData.append('&'); } paramData.append(param.getKey()); paramData.append('='); paramData.append(String.valueOf(param.getValue())); } return paramData.toString(); }
사실 굳이 uriBuilder 를 써야 되나 싶기도 하다.
단순하게 map 에 파라미터를 추가하고 map 을 parameter 문자열로 변환해주는 메소드만 만들어서 사용하면 편할거 같다.
postForObject() 메소드 header 값 없는 경우
Employee newEmployee = Employee.builder() .name("Frank") .address(Address.builder() .country("US") .build()) .build(); Employee employee = restTemplate.postForObject(BASE_URL + "/employee", newEmployee, Employee.class);
postForObject() 메소드 header 포함해서 보내기
Employee newEmployee = Employee.builder() .name("Frank") .address(Address.builder() .country("US") .build()) .build(); HttpHeaders headers = new HttpHeaders(); headers.set("headerTest", "headerValue"); HttpEntity<Employee> request = new HttpEntity<>(newEmployee, headers); Employee employee = restTemplate.postForObject(BASE_URL + "/employee", request, Employee.class);
postForEntity
getForEntity 와 동일해서 생략
post form data 사용
// 헤더 설정 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); // 파라미터 세팅 MultiValueMap<String, String> map= new LinkedMultiValueMap<>(); map.add("id", "1"); // 요청 세팅 완료 HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers); // 실제 요청부 ResponseEntity<String> response = restTemplate.postForEntity(fooResourceUrl+"/form", request , String.class);
Timeout 설정하기
timeout 을 설정하려면 ClientHttpRequestFactory 와 같은 팩토리 메소드를 만들고 RestTemplate 의 생성자에 추가해야 한다.
RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory()); private ClientHttpRequestFactory getClientHttpRequestFactory() { int timeout = 5000; HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(); clientHttpRequestFactory.setConnectTimeout(timeout); return clientHttpRequestFactory; }
참고사항으로 timeout 이 0 이면 무제한설정이다. (infinite timeout)
Execute()
Execute()는 콜백을 통해 요청 준비와 응답 추출을 완벽하게 제어하여 요청을 수행하는 가장 일반적인 메서드를 RestTemplate에서 제공한다.
getForObject(), postForeObject() 등은 excute() 를 내부적으로 호출한다.
connection pool 적용
RestTemplate 은 기본적으로 connection pool 을 사용하지 않는다. 따라서 연결할 때 마다, 로컬 포트를 열고 tcp connection 을 맺는다. 이때 문제는 close() 이후에 사용된 소켓은 TIME_WAIT 상태가 되는데, 요청량이 많다면 이런 소켓들을 재사용하지 못하고 소켓이 오링나서 응답이 지연될 것이다.
참고문헌
www.baeldung.com/rest-template [샘플 문서]
stackoverflow.com/questions/31869193/using-spring-rest-template-either-creating-too-many-connections-or-slow/ [커넥션 많을 경우 pool 관리반응형'Spring > spring boot 및 기타' 카테고리의 다른 글
[spring] 스프링 elasticsearch NativeSearchQuery 사용방법 (0) 2021.03.16 [spring] 스프링 elasticsearch ElasticsearchRepository underscore 오류 해결방법 (0) 2021.03.16 [spring boot] 스프링부트 에서 elasticsearch 시작하기 (1) 2021.02.24 Jasypt 암호화 - spring 설정파일 암호화하기 (0) 2021.01.28 spring boot 1.X 와 2.X 차이점 (0) 2021.01.08