RestTemplate 실무에 제대로 적용해보자

git source 링크

RestTemplate을 실무에 적용할수 있도록 추상화 해보자

특징

  1. 동기 클라이언트
  2. 스프링5.0이 올라가면서 WebClient가 대세가 된! 그래도 좋은 client
  3. fegin의 내부 구현체

CRUD

Create : Post Method
Read : Get Method
Update : Put Method
Delete : Delete Method

Post,Put : requestBody
Get, Delete : RequestBody 사용하지 않음

  • 공식 가이드에서는 그러함
  • Webclient 사용시 delete 에서 body에 데이터 넣을 수 없음 그럼 우찌함?
  • 메서드 .delete()로 쓰지 말고 .method()해서 메소드 선택하면 쓸수 있음
  • 이번 장의 강의는 webclient가 아닌 restTemplate

추상화 해보자

SampleRestClient.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68


public abstract class SampleRestClient {


protected String endPointHost;


@Autowired
private RestTemplate client;

private <T> RequestEntity<T> beforeSend(
String subUri, HttpMethod method, T bodyData
) throws URISyntaxException {

RequestEntity<T> request = RequestEntity.method(method, new URI(endPointHost + subUri))
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.body(bodyData);

client.setErrorHandler(new ClientErrorHandler());

return request;
}


protected <T, S> ResponseEntity<S> send(String subUri, HttpMethod method, T bodyData, Class<S> returnClassName) throws URISyntaxException {

ResponseEntity<S> response = client.exchange(beforeSend(subUri, method, bodyData), returnClassName);

return response;
}

protected <T, S> ResponseEntity<S> send(String subUri, HttpMethod method, T bodyData, ParameterizedTypeReference<S> returnClassName) throws URISyntaxException {

ResponseEntity<S> response = client.exchange(beforeSend(subUri, method, bodyData), returnClassName);

return response;
}

private RequestEntity<Void> beforeSendGetMethod(
String subUri, HttpMethod method
) throws URISyntaxException {

RequestEntity.BodyBuilder builder = RequestEntity.method(method, new URI(endPointHost + subUri))
.accept(MediaType.APPLICATION_JSON);

client.setErrorHandler(new ClientErrorHandler());

return builder.build();
}

protected <S> ResponseEntity<S> send(String subUri, HttpMethod method, Class<S> returnClassName) throws URISyntaxException {

ResponseEntity<S> response = client.exchange(beforeSendGetMethod(subUri, method), returnClassName);

return response;
}

protected <S> ResponseEntity<S> send(String subUri, HttpMethod method, ParameterizedTypeReference<S> returnClassName) throws URISyntaxException {

ResponseEntity<S> response = client.exchange(beforeSendGetMethod(subUri, method), returnClassName);

return response;
}


}

  1. 명시적으로 return클래스 보여주자
  2. ClientErrorHandler를 달아서 클라이언트 통신에서 발생하는 에러는 명시적으로 확인하자

ClientErrorHandler.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

@Slf4j
public class ClientErrorHandler implements ResponseErrorHandler {



@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return response.getStatusCode().series()!= HttpStatus.Series.SUCCESSFUL;
}


@Override
public void handleError(ClientHttpResponse response) throws IOException {

String resString = StreamUtils.copyToString(response.getBody(), Charsets.UTF_8);



log.debug("start to check what is wrong ");
log.debug("Status code : {}", response.getStatusCode());
log.debug("Status text : {}", response.getStatusText());
log.debug("Headers : {}", response.getHeaders());
log.debug("Response body: {}", resString);
log.debug("end to check what is wrong");


//이쪽부분에서 정의한 exception으로 던진 후 adviceController에서 정의된 에러를 던져함


}
}
  1. HttpStatus가 200이 아닐 경우 던져서 명시적으로 에러를 확인하자(귀찮아서 못달겟음;…..)

실제 구현체

UserClient.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@Component
public class UserClient extends SampleRestClient {

private String host = "http://localhost:9090";

private String uri = "/user";

public UserClient() {
this.endPointHost = host;
}

public void createUser(UserInfo userInfo) throws URISyntaxException {



send(uri, HttpMethod.POST, userInfo, (Class<Object>) null);

}

public void updateUser(UserInfo userInfo) throws URISyntaxException {

send(uri, HttpMethod.PUT, userInfo, (Class<Object>) null);
}

public ResponseEntity<UserInfo> getUser(String userId) throws URISyntaxException {

String temp = uri+ "?userId=" + userId;

return send(temp, HttpMethod.GET , UserInfo.class);

}

public void removeUser() {

}

}

  1. 맨뒤에 파라미터는 null이 들어갈수 있게 하는 이유는 명시적으로 status code만 체크 할뿐! 바디값은 없다 이런뜻으로..

샘플코드를 짤때 프로젝트를 분리해서 짜야 했으나.. 그렇게 하지 않아.. 불편함 점이 있음. 위의 글들을 다른 프로젝트에서 짜는 것을 추천