* RestTemplate 클래스
1. Spring에서 제공하는 RESTful 방식의 API 호출하는 클래스이며, REST API 서버와 HTTP 통신을 위한 클래스
2. Spring 3.0부터 지원하며, JSON, XML 응답
3. 반복적인 코드를 줄임
4. org.springframework.web.client.RestTemplate
* 그 외 REST API 호출하는 HTTP Client 방법
URLConnection
HttpClient
AsyncRestTemplate
WebClient
WebFlux
* RestTemplate 메소드 종류 (서버[uri]쪽으로 연결이 붙는 시점)
1. getForEntity : GET방식이며, 반환의형태가 ResponseType을 지정하여 ResponseEntity를 반환
2. getForObject : GET방식이며, 제네릭으로 지정한 타입의 객체를 반환
3. postForObject(URI, Object, Class<T> responsType) : POST방식이며, 객체를 반환
4. postForEntity(URI, Object, Class<T> responsType) : POST방식이며, ResponseEntity를 반환
5. exchange(RequestEntity, Class<T> responseType) : HTTP 헤더를 생성하여 어떤 정보(JSON,헤더)와 요청 가능, 제네릭 타입의 인스턴스에는 .class를 사용할 수 없어 new ParameterizedTypeReference<>(){}
Etc. postForLocation, delete, headForHeaders, put, patchForObject, optionsForAllow, execute
* RestTemplate 동작 원리
1. Application이 RestTemplate 생성(URI,HTTP 헤더정보) 후 요청
2. RestTemplate이 HttpMessageConverter를 사용하여 RequestEntity를 요청 메시지(JSON)로 변환
3. RestTemplate이 ClientHttpRequestFactory로 부터 ClientHttpRequest를 가져와서 요청
4. ClientHttpRequest는 요청메세지를 만들어 HTTP 프로토콜을 통해 서버와 통신
5. Restemplate는 ResponseErrorHandler로 오류를 확인하고 있다면 처리로직을 실행
6. ResponseErrorHandler는 오류가있다면 ClientHttpResponse에서 응답데이터를 가져와서 처리
7. RestTemplate는 HttpMessageConverter를 이용해서 응답메시지를 java object(Class<T> responseType)로 변환
8. 반환
참고 : https://gngsn.tistory.com/154
https://sjh836.tistory.com/141
* UriComponentsBuilder 클래스
1. org.springframework.web.util.UriComponentsBuilder
2. URI를 구성하는 Components들을 효과적으로 다룰 수 있도록 하는클래스
3. 서버쪽 주소 호출하기위해 UriComponentsBuilder 사용
4. Components : Scheme, UserInfo, Host, Port, Path, Query, Fragment
* RequestEntity, ResponseEntity 클래스
1. org.springframework.http.HttpEntity<T>를 상속받은 org.springframework.http.RequestEntity<T>, org.springframework.http.ResponseEntity<T>
2. URL 요청, 응답시 Header, Body...의 생성자 파라미터를 넘기거나 받을 수 있다.
Client 서버
server.port=8080
Client - Service.java
import com.example.client.dto.FixedJsonRequest;
import com.example.client.dto.UserRequest;
import com.example.client.dto.UserResponse;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
@Service
public class RestTemplateSVC {
/*
* GET방식으로 서버 통신
* 서버쪽 호출 /api/server/hello
* 응답을 받아옴
*/
public UserResponse getHello(){
/*
* UriComponentsBuilder 클래스
* org.springframework.web.util.UriComponentsBuilder
* URI를 구성하는 Components들을 효과적으로 다룰 수 있도록 하는클래스
* Components : Scheme, UserInfo, Host, Port, Path, Query, Fragment
*/
// 서버쪽 주소 호출하기위해 UriComponentsBuilder 사용
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090")
.path("/api/server/hello")
.queryParam("name","steve")
.queryParam("age",10)
.encode() // 파라미터 유무에 따라 생략가능
.build()
.toUri();
System.out.println("GET Request Uri : "+uri.toString());
/*
* RestTemplate 클래스
* org.springframework.web.client.RestTemplate
* Spring 에서 제공하는 RESTful 방식의 api 호출하는 클래스
* response방식은 JSON, XML, String
*
* RestTemplate 메소드 종류 (서버[uri]쪽으로 연결이 붙는 시점)
* getForEntity : GET방식이며, 반환의형태가 ResponseType을 지정하여 ResponseEntity를 반환
* getForObject : GET방식이며, 제네릭으로 지정한 타입의 객체를 반환
*/
RestTemplate restTemplate = new RestTemplate();
/*
* String 형태로 응답값 return
*/
// 1. Object인 경우
// String result = restTemplate.getForObject(uri,String.class);
// 2. ResponseEntity의 경우(헤더의정보와 함께받는 경우)
//ResponseEntity<String> result = restTemplate.getForEntity(uri,String.class);
/*
* JSON 형태로 응답값 return
*/
// 1. Object인 경우
UserResponse result = restTemplate.getForObject(uri,UserResponse.class);
// 2. ResponseEntity의 경우(헤더의정보와 함께받는 경우)
// 컨트롤러와 서비스단 메소드리턴타입 수정 String -> UserResponse
//ResponseEntity<UserResponse> result = restTemplate.getForEntity(uri,UserResponse.class);
// Object
System.out.println("GET Response Body : "+result);
// ResponseEntity 정보
//System.out.println("GET Response status : "+result.getStatusCode());
//System.out.println("GET Response Body : "+result.getBody());
return result;
//return result.getBody();
}
/*
* POST방식으로 서버 통신
* /api/server/user/{userId}/name/{userName}
* http body에 object를 보내면 object mapper가 JSON으로 변환해줘서 RestTemplate로 http body에 json으로 넣어줌
*/
public UserResponse postHello(){
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090")
.path("/api/server/user/{userAge}/name/{userName}")
.encode()
.build() // buildAndExpand()와 동일한 기능 제공
.expand(100,"steve") // 순서대로 PathVariable에 매칭
.toUri();
System.out.println("POST Request Uri : "+uri);
/*
* Object->Object Mapper
* {
* "name" : "aaa"
* "age" : 99
* }
* Client에서 HTTP BODY에 담긴 JSON 데이터
*/
UserRequest req = new UserRequest();
req.setName("aaa"); // HTTP BODY 정보
req.setAge(99);
System.out.println("POST Request JSON : "+req);
System.out.println("--------------------------------");
/*
* postForObject(URI, Object, Class<T> responsType) : POST방식이며, 객체를 반환
* postForEntity(URI, Object, Class<T> responsType) : POST방식이며, ResponseEntity를 반환
*/
// RestTemplate->HTTP BODY JSON
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<UserResponse> response = restTemplate.postForEntity(uri, req, UserResponse.class);
System.out.println("POST Response status : "+response.getStatusCode());
System.out.println("POST Response Header : "+response.getHeaders());
System.out.println("POST Response Body : "+response.getBody());
return response.getBody();
}
/*
* 헤더정보 추가하여 서버 통신
*/
public UserResponse exchange(){
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090")
.path("/api/server/user/{userAge}/name/{userName}/header")
.encode()
.build() // buildAndExpand()와 동일한 기능 제공
.expand(100,"steve") // 순서대로 PathVariable에 매칭
.toUri();
System.out.println("POST Request Uri : "+uri);
/*
* Object->Object Mapper
* {
* "name" : "aaa"
* "age" : 99
* }
* Client에서 HTTP BODY에 담긴 JSON 데이터
*/
UserRequest req = new UserRequest();
req.setName("aaa"); // HTTP BODY 정보
req.setAge(99);
System.out.println("POST Request JSON : "+req);
System.out.println("--------------------------------");
/*
* RequestEntity, ResponseEntity 클래스
* org.springframework.http.HttpEntity<T>를 상속 받은 org.springframework.http.RequestEntity<T>, org.springframework.http.ResponseEntity<T>
* URL 요청, 응답시 Header, Body...의 생성자 파라미터를 넘기거나 받을 수 있다.
*/
// RequestEntity를 사용하여 HTTP Request Header에 값추가하여 RequestEntity 생성
RequestEntity<UserRequest> requestEntity =
RequestEntity.post(uri)
.contentType(MediaType.APPLICATION_JSON)
.header("x-authorization","abcdValue") // .header()로 계속 추가
.header("custom-header","randomValue")
.body(req);
System.out.println("POST RequestEntity Header : "+requestEntity.getHeaders());
System.out.println("POST RequestEntity METHOD : "+requestEntity.getMethod());
System.out.println("POST RequestEntity Body : "+requestEntity.getBody());
/*
* exchange(RequestEntity, Class<T> responseType)
* HTTP 헤더를 생성하여 어떤 정보(JSON,헤더)와 요청 가능
*/
// RestTemplate->HTTP BODY JSON
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<UserResponse> response = restTemplate.exchange(requestEntity, UserResponse.class);
return response.getBody();
}
public FixedJsonRequest<UserResponse> genericExchange(){
URI uri = UriComponentsBuilder
.fromUriString("http://localhost:9090")
.path("/api/server/user/{userAge}/name/{userName}/json")
.encode()
.build() // buildAndExpand()와 동일한 기능 제공
.expand(100,"steve") // 순서대로 PathVariable에 매칭
.toUri();
System.out.println("POST Request Uri : "+uri);
/*
* Object->Object Mapper
* {
* "name" : "aaa"
* "age" : 99
* }
* Client에서 HTTP BODY에 담긴 JSON 데이터
*/
UserRequest userRequest = new UserRequest();
userRequest.setName("aaa"); // HTTP BODY 정보
userRequest.setAge(99);
System.out.println("POST Request JSON : "+userRequest);
System.out.println("--------------------------------");
// Request할 정적static Header정보 + Body 정보
FixedJsonRequest<UserRequest> fixedJsonRequest = new FixedJsonRequest<>();
fixedJsonRequest.setHeader(
new FixedJsonRequest.Header("200") // HTTP Header
);
fixedJsonRequest.setResponseBody(
userRequest // HTTP Body JSON -> T responseBody
);
/*
* RequestEntity, ResponseEntity 클래스
* org.springframework.http.HttpEntity<T>를 상속 받은 org.springframework.http.RequestEntity<T>, org.springframework.http.ResponseEntity<T>
* URL 요청, 응답시 Header, Body...의 생성자 파라미터를 넘기거나 받을 수 있다.
*/
// RequestEntity를 사용하여 HTTP Request Header에 값추가하여 RequestEntity 생성
RequestEntity<FixedJsonRequest<UserRequest>> requestEntity =
RequestEntity.post(uri)
.contentType(MediaType.APPLICATION_JSON)
.header("x-authorization","abcdValue") // .header()로 계속 추가
.header("custom-header","randomValue")
.body(fixedJsonRequest);
System.out.println("POST RequestEntity Header : "+requestEntity.getHeaders());
System.out.println("POST RequestEntity METHOD : "+requestEntity.getMethod());
System.out.println("POST RequestEntity Body : "+requestEntity.getBody());
/*
* exchange(RequestEntity, ParameterizedTypeReference<T> responseType)
* HTTP 헤더를 생성하여 어떤 정보(JSON,헤더)와 요청 가능
* 제네릭 타입의 인스턴스에는 .class를 사용할 수 없어 new ParameterizedTypeReference<>(){}
*/
// RestTemplate->HTTP BODY JSON
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<FixedJsonRequest<UserResponse>> response
= restTemplate.exchange(requestEntity, new ParameterizedTypeReference<FixedJsonRequest<UserResponse>>(){});
return response.getBody();
}
}
Client - Controller.java
import com.example.client.dto.FixedJsonRequest;
import com.example.client.dto.UserResponse;
import com.example.client.service.RestTemplateSVC;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/client")
public class ApiController {
/*
// 필드주입
@Autowired
private RestTemplateSVC restTemplateSVC;
*/
// 명시적으로 생성자주입
private final RestTemplateSVC restTemplateSVC;
public ApiController(RestTemplateSVC restTemplateSVC) {
this.restTemplateSVC = restTemplateSVC;
}
@GetMapping("/hello")
public UserResponse getHello(){
return restTemplateSVC.getHello();
}
@PostMapping("/hello")
public UserResponse getPostHello(){
return restTemplateSVC.postHello();
}
@PostMapping("/hello/header")
public UserResponse getPostHeaderHello(){
return restTemplateSVC.exchange();
}
@PostMapping("/hello/json")
public FixedJsonRequest<UserResponse> getPostJsonHello(){
return restTemplateSVC.genericExchange();
}
}
Client - Static인 Data일시 Request.java
public class FixedJsonRequest<T> {
// 정적 Header 클래스
private Header header;
// 어떤 타입의 정보가 올지 몰라 타입형태의 제네릭으로 받아옴
private T responseBody;
// 고정 Header 항목
public static class Header{
private String responseCode;
public String getResponseCode() {
return responseCode;
}
public void setResponseCode(String responseCode) {
this.responseCode = responseCode;
}
public Header() {
}
public Header(String responseCode) {
this.responseCode = responseCode;
}
@Override
public String toString() {
return "Header{" +
"responseCode='" + responseCode + '\'' +
'}';
}
}
public Header getHeader() {
return header;
}
public void setHeader(Header header) {
this.header = header;
}
public T getResponseBody() {
return responseBody;
}
public void setResponseBody(T responseBody) {
this.responseBody = responseBody;
}
@Override
public String toString() {
return "FixedJsonRequest{" +
"header=" + header +
", body=" + responseBody +
'}';
}
}
Client - Request.java
public class UserRequest {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "UserResponse{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Client - Response.java
public class UserResponse {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "UserResponse{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
ResponseServer 서버 (Lombok 사용)
server.port=9090
ResponseServer - Controller.java
import com.example.server.dto.MultiJsonRequest;
import com.example.server.dto.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/server")
@Slf4j
public class ServerApiController {
@GetMapping("/hello")
public User getHello(@RequestParam String name, @RequestParam int age){
System.out.println("RequestParam name : "+name);
System.out.println("RequestParam age : "+age);
User user = new User();
user.setName(name);
user.setAge(age);
System.out.println("Setting User : "+user);
return user;
}
@PostMapping("/user/{userAge}/name/{userName}")
public User postHello(@RequestBody User user,@PathVariable int userAge, @PathVariable String userName){
log.info("Client Request Body : {}", user);
log.info("Client PathVariable userAge : {}", userAge);
log.info("Client PathVariable userName : {}", userName);
log.info("---------------------------------------------");
log.info("Client Return Body : {}", user);
return user;
}
@PostMapping("/user/{userAge}/name/{userName}/header")
public User postExchangeHello(@RequestBody User user,
@PathVariable int userAge,
@PathVariable String userName,
@RequestHeader("x-authorization") String authorization,
@RequestHeader("custom-header") String customHeader){
log.info("Client Request Body : {}", user);
log.info("Client RequestHeader x-authorization : {}", authorization);
log.info("Client RequestHeader custom-header : {}", customHeader);
log.info("---------------------------------------------");
log.info("Client Return Body : {}", user);
return user;
}
@PostMapping("/user/{userAge}/name/{userName}/json")
public MultiJsonRequest<User> postFixJsonHello(//HttpEntity<String> entity,
@RequestBody MultiJsonRequest<User> user,
@PathVariable int userAge,
@PathVariable String userName,
@RequestHeader("x-authorization") String authorization,
@RequestHeader("custom-header") String customHeader){
//log.info("HTTP Request HttpEntity : {}", entity.getBody());
log.info("Client Request Body : {}", user);
log.info("Client RequestHeader x-authorization : {}", authorization);
log.info("Client RequestHeader custom-header : {}", customHeader);
MultiJsonRequest<User> response = new MultiJsonRequest<>();
response.setHeader(user.getHeader());
response.setResponseBody(user.getResponseBody());
log.info("---------------------------------------------");
log.info("Client Return Body : {}", response);
return response;
}
}
ResponseServer - Static한 Data를 받는 Request.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data @NoArgsConstructor @AllArgsConstructor
public class MultiJsonRequest<T> {
private Header header;
private T responseBody;
@Data @NoArgsConstructor @AllArgsConstructor
public static class Header{
private String responseCode;
}
}
ResponseServer - Request.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String name;
private int age;
}
테스트
1. GetForEntity
GetForEntity - ClientServer
GET Request Uri : http://localhost:9090/api/server/hello?name=steve&age=10
GET Response status : 200 OK
GET Response Body : UserResponse{name='steve', age=10}
GetForEntity - ResponseServer
RequestParam name : steve
RequestParam age : 10
Setting User : User(name=steve, age=10)
2. GetForObject
GetForObject - ClientServer
GET Request Uri : http://localhost:9090/api/server/hello?name=steve&age=10
GET Response Body : UserResponse{name='steve', age=10}
GetForObject - ResponseServer
RequestParam name : steve
RequestParam age : 10
Setting User : User(name=steve, age=10)
3. PostForEntity
PostForEntity - ClientServer
POST Request Uri : http://localhost:9090/api/server/user/100/name/steve
POST Request JSON : UserResponse{name='aaa', age=99}
--------------------------------
POST Response status : 200 OK
POST Response Header : [Content-Type:"application/json", Transfer-Encoding:"chunked", Date:"Fri, 25 Nov 2022 01:10:23 GMT", Keep-Alive:"timeout=60", Connection:"keep-alive"]
POST Response Body : UserResponse{name='aaa', age=99}
PostForEntity - ResponseServer
2022-11-25 10:10:23.387 INFO 59619 --- [nio-9090-exec-5] c.e.s.controller.ServerApiController : Client Request Body : User(name=aaa, age=99)
2022-11-25 10:10:23.389 INFO 59619 --- [nio-9090-exec-5] c.e.s.controller.ServerApiController : Client PathVariable userAge : 100
2022-11-25 10:10:23.389 INFO 59619 --- [nio-9090-exec-5] c.e.s.controller.ServerApiController : Client PathVariable userName : steve
2022-11-25 10:10:23.389 INFO 59619 --- [nio-9090-exec-5] c.e.s.controller.ServerApiController : ---------------------------------------------
2022-11-25 10:10:23.390 INFO 59619 --- [nio-9090-exec-5] c.e.s.controller.ServerApiController : Client Return Body : User(name=aaa, age=99)
4. Exchange
Exchange - ClientServer
POST Request Uri : http://localhost:9090/api/server/user/100/name/steve/header
POST Request JSON : UserResponse{name='aaa', age=99}
--------------------------------
POST RequestEntity Header : [Content-Type:"application/json", x-authorization:"abcdValue", custom-header:"randomValue"]
POST RequestEntity METHOD : POST
POST RequestEntity Body : UserResponse{name='aaa', age=99}
Exchange - ResponseServer
2022-11-25 10:12:57.298 INFO 59619 --- [nio-9090-exec-6] c.e.s.controller.ServerApiController : Client Request Body : User(name=aaa, age=99)
2022-11-25 10:12:57.298 INFO 59619 --- [nio-9090-exec-6] c.e.s.controller.ServerApiController : Client RequestHeader x-authorization : abcdValue
2022-11-25 10:12:57.298 INFO 59619 --- [nio-9090-exec-6] c.e.s.controller.ServerApiController : Client RequestHeader custom-header : randomValue
2022-11-25 10:12:57.298 INFO 59619 --- [nio-9090-exec-6] c.e.s.controller.ServerApiController : ---------------------------------------------
2022-11-25 10:12:57.298 INFO 59619 --- [nio-9090-exec-6] c.e.s.controller.ServerApiController : Client Return Body : User(name=aaa, age=99)
5. ExchangeMultiJSON
ExchangeMultiJSON - ClientServer
POST Request Uri : http://localhost:9090/api/server/user/100/name/steve/json
POST Request JSON : UserResponse{name='aaa', age=99}
--------------------------------
POST RequestEntity Header : [Content-Type:"application/json", x-authorization:"abcdValue", custom-header:"randomValue"]
POST RequestEntity METHOD : POST
POST RequestEntity Body : FixedJsonRequest{header=Header{responseCode='200'}, body=UserResponse{name='aaa', age=99}}
ExchangeMultiJSON - ResponseServer
2022-11-25 10:15:22.742 INFO 59619 --- [nio-9090-exec-9] c.e.s.controller.ServerApiController : Client Request Body : MultiJsonRequest(header=MultiJsonRequest.Header(responseCode=200), responseBody=User(name=aaa, age=99))
2022-11-25 10:15:22.746 INFO 59619 --- [nio-9090-exec-9] c.e.s.controller.ServerApiController : Client RequestHeader x-authorization : abcdValue
2022-11-25 10:15:22.747 INFO 59619 --- [nio-9090-exec-9] c.e.s.controller.ServerApiController : Client RequestHeader custom-header : randomValue
2022-11-25 10:15:22.747 INFO 59619 --- [nio-9090-exec-9] c.e.s.controller.ServerApiController : ---------------------------------------------
2022-11-25 10:15:22.747 INFO 59619 --- [nio-9090-exec-9] c.e.s.controller.ServerApiController : Client Return Body : MultiJsonRequest(header=MultiJsonRequest.Header(responseCode=200), responseBody=User(name=aaa, age=99))
'Spring > 1-4. API' 카테고리의 다른 글
HttpURLConnection, HttpsURLConnection이란? (0) | 2022.11.25 |
---|