ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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 관리
    반응형

    댓글

Designed by Tistory.