Spring은 REST 서비스의 endpoint를 호출하는 2가지 방법을 제공한다. 방법은 동기, 비동기 방식이 존재하며 이번 Post에서는 동기 방식인 REST template에 대해 알아보고자 한다.
REST Template은 Spring 3.0부터 지원이 되었으며 REST API호출 이후 응답을 받을 때까지 기다리는 방식이다.
출처: 빨간색코딩
출처: https://advenoh.tistory.com/46
getForObject와 getForEntity같은 경우는 parameter로 request를 요청할 uri와 데이터를 받아 저장할 객체의 type을 적어줘야한다.
아래 getForEntity를 사용한 코드의 경우는 Json type으로 데이터를 받기 위해 DTO를 사용하여 받는 데이터 타입을 DTO인 userResponse.class설정한 예시이다.
RestTemplate (getForEntity사용)
@Service
public class RestTemplateService {
// http://localhost:9090/api/server/hello 로 요청해서 response를 받아오기
public UserResponse hello(){
// uri 주소 생성
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090") //http://localhost에 호출
.path("/api/server/hello")
.queryParam("name", "steve") // query parameter가 필요한 경우 이와 같이 사용
.queryParam("age", 10)
.encode()
.build()
.toUri();
System.out.println(uri.toString());
RestTemplate restTemplete = new RestTemplate();
ResponseEntity<UserResponse> result = restTemplete.getForEntity(uri, UserResponse.class);
// entity로 데이터를 가져오겠다(Get)~~
System.out.println(result.getStatusCode());
System.out.println(result.getBody());
return result.getBody();
}
}
DTO를 사용하지 않고 String, Integer와 같은 데이터 타입으로도 결과를 받을 수 있다. 이러한 경우에는 getForObject를 사용하여야하며 아래와 같이 responseType class를 String.class와 같이 적어주면 된다.
String result = restTemplete.getForObject(uri, String.class);
// getForObject는 Get Request를 uri로 보내서 결과를 두번째 parameter로 적은 Object타입으로 받아온다.
// getForEntity로 하면 결과를 Entity로 받아 http status와 data등 여러 데이터 확인이 가능하다.
ResponseEntity<String> result = restTemplete.getForEntity(uri, String.class);
controller의 코드는 다음과 같이 GetMapping으로 Method를 수행시켰을 때 RestTemplate을 사용한 method를 호출하여 return하는 방식으로 사용된다.
~/req URI로 Get request가 오면 getHello 메서드가 실행이되며 restTemplateService의 hello 메서드가 호출이된다. hello 메서드의 경우 코드를 보면 Server의 주소인http://localhost:9090/api/server/hello주소로 Get Request를 보내서 결과를 받아온다.
Controller
@RestController
@RequestMapping("/api/client")
public class ApiController {
private final RestTemplateService restTemplateService;
public ApiController(RestTemplateService restTemplateService) {
this.restTemplateService = restTemplateService;
}
@GetMapping("/req")
public Req<UserResponse> getHello(){
return restTemplateService.genericExchange();
}
}
postForEntity는 getForEntity와 다르게 2번째 parameter로 request object를 넘겨줘야한다.
아래의 코드의 경우는 내가 UserRequest DTO를 생성한 후 값을 임의로 넣어 Request object로 보내준 예시이다.
RestTemplate
public UserResponse post() {
// http://localhost:9090/api/server/user/{userId}/name/{userName} 로 post하기
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090")
.path("/api/server/user/{userId}/name/{userName}")
.encode()
.build()
.expand(100, "steve") // {userId}, {userName}에 들어갈 값을 순차적으로 입력
.toUri();
System.out.println(uri);
// object를 넣어주면 object mapper가 json으로 바꿔주고
// rest template에서 http body에 json을 넣어줄 것이다.
UserRequest req = new UserRequest();
req.setName("steve");
req.setAge(10);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<UserResponse> response = restTemplate.postForEntity(uri, req, UserResponse.class);
// uri에 req object를 보내서 응답은 UserResponse.class타입으로 받을 것이다!!
System.out.println(response.getStatusCode());
System.out.println(response.getHeaders());
System.out.println(response.getBody());
return response.getBody();
}
Controller
@GetMapping("/req")
public UserResponse getHello(){
return restTemplateService.post();
}
위의 예제의 경우는 Request를 요청할 때 Header의 정보전송은 따로 없이 데이터가 담긴 Request Object만을 request body에 넣어 Post하였다. 하지만 실무에서 RestTemplate을 사용할 때는 위의 예제와는 다르게 Header값을 추가하여 사용한다고 한다.
Header값과 데이터가 담긴 Object를 같이 보내려면 먼저 RequestEntity객체를 생성하고 RequestEntity객체안에 uri, header내용, request object들을 넣어줘야한다.
그 후 RestTemplate의 exchange()메서드안에 앞서 만든 RequestEntity객체와 response type을 넣어 ResponseEntity객체를 만들어 사용한다.
아직 공부 초기라 설명이 많이 미흡한 것 같다😥 아래 코드를 보면 이해가 훨씬 빠를 것이다.
RestTemplate
public UserResponse exchange(){
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090")
.path("/api/server/user/{userId}/name/{userName}")
.encode()
.build()
.expand(100, "steve") // {userId}, {userName}에 들어갈 값을 순차적으로 입력
.toUri();
System.out.println(uri);
UserRequest req = new UserRequest();
req.setName("steve");
req.setAge(10);
RequestEntity<UserRequest> requestEntity = RequestEntity
.post(uri)
.contentType(MediaType.APPLICATION_JSON)
.header("x-authorization", "abcd")
.header("custom-header", "ffff")
.body(req);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<UserResponse> response = restTemplate.exchange(requestEntity, UserResponse.class);
// RequestEntity객체와 반환 type을 적어서 출력
return response.getBody();
}
코드에서 .header()의 경우 headerName, headerValues값이 순차적으로 들어갑니다.
마지막으로 아래와 같이 Request body의 내용이 header는 일치하나, body의 내용이 다른 경우가 존재할 경우 Generic class를 사용해 코드의 불필요한 중복 작성 없이 이용하는 법을 다뤄보려 합니다.
{
"header": {
"response_code": ""
},
"body": {
불규칙한 body내용
}
}
위의 Json의 형태처럼 Body의 내용이 일관되지 않고 여러 data type을 받는 경우 아래 코드와 같이 Generic Class를 생성하고 해당 객체를 이용하여 RequestEntity, ResponseEntity를 만들어야 합니다.
Req.java
public class Req<T> {
// request의 body를 generic type으로 설정
private Header header;
private T resBody;
....
}
RestTemplate
public Req<UserResponse> genericExchange(){
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090")
.path("/api/server/user/{userId}/name/{userName}/post2")
.encode()
.build()
.expand(100, "steve")
.toUri();
System.out.println(uri);
UserRequest userRequest = new UserRequest();
userRequest.setName("steve");
userRequest.setAge(10);
Req req = new Req<UserRequest>();
req.setHeader(new Req.Header());
req.setResBody(userRequest);
RequestEntity<Req<UserRequest>> requestEntity = RequestEntity
.post(uri)
.contentType(MediaType.APPLICATION_JSON)
.header("x-authorization", "abcd")
.header("custom-header", "ffff")
.body(req);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<Req<UserResponse>> response = restTemplate.exchange(requestEntity, new ParameterizedTypeReference<Req<UserResponse>>(){});
// Response type으로 Req<UserResponse>.class을 쓰고 싶은데 Generic에는 class를 붙일 수 없다.
// 그래서 new ParameterizedTypeReference<Req<UserResponse>>(){}를 사용한다. <>안에 type은 앞에서 지정했기에 생략해도 괜찮다
return response.getBody();
}
Controller
@GetMapping("/req")
public Req<UserResponse> getHello(){
return restTemplateService.genericExchange();
}