from Dictionary - Http Interface Client
Http Interface Client
RestClient
동기 API가 있는 클라이언트
initialize
RestClient.crate()로 생성한다 혹은 builder도 제공한다.
RestClient defaultClient = RestClient.create();
RestClient customClient = RestClient.builder()
.requestFactory(new HttpComponentsClientHttpRequestFactory())
//Allows to use a pre-configured HttpClient
.messageConverters(converters -> converters.add(new MyCustomMessageConverter()))
//메시지 컨버터 지정
.baseUrl("https://example.com")
//baseUrl
.defaultUriVariables(Map.of("variable", "foo"))
//default Uri
.defaultHeader("My-Header", "Foo")
//defaultHeader
.requestInterceptor(myCustomInterceptor)
.requestInitializer(myCustomInitializer)
.build();
Request and Response
restClient.get().uri("https://example.com/orders/{id}", 42)
//Uri는 UriBuilderFactory로 만들 수 있다.
// .headers()
// .accept()
// .acceptCharset()
.retrieve() //retrieve() 실행으로 body에 접근할 수 있다.
.body(String.class); //Jackson과 같은 MessageConverter로 Object로 받을 수도 있다.
ResponseEntity
ResponseEntity<String> result = restClient.get()
.uri("https://example.com")
.retrieve()
.toEntity(String.class); //toEntity로 ResponseEntity로 받을 수 있다.
System.out.println("Response status: " + result.getStatusCode());
System.out.println("Response headers: " + result.getHeaders());
System.out.println("Contents: " + result.getBody());
ErrorHandling
String result = restClient.get()
.uri("https://example.com/this-url-does-not-exist")
.retrieve()
.onStatus(HttpStatusCode::is4xxClientError, (request, response) -> {
throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders())
})
//이와 같이 400번 대 에러를 처리할 수도 있다.
.body(String.class);
Exchange
retrieve() 대신 exchange()를 사용할 수도 있다. 이를 통해서 더 디테일하게 RestClient를 사용할 수 있다.
Pet result = restClient.get()
.uri("https://petclinic.example.com/pets/{id}", id)
.accept(APPLICATION_JSON)
.exchange((request, response) -> {
if (response.getStatusCode().is4xxClientError()) {
throw new MyCustomRuntimeException(response.getStatusCode(), response.getHeaders());
}
else {
Pet pet = convertResponse(response);
return pet;
}
});
MessageConverter
| MessageConverter | Description |
|---|---|
| StringHttpMessageConverter | 메시지를 String으로 변환해주는 컨버터다. |
| FormHttpMessageConverter | FormData 를 읽고 쓸 수 있게 해주는 컨버터다. 기본적으로 application/x-www-form-urlencoded를 형태일 때 읽고 쓴다. |
| ByteArrayHttpMessageConverter | byte[]를 일고 쓸 수 있게 한다. application/octet-stream일 때 주로 사용한다. |
| MarshallingHttpMessageConverter | XML 일 때 읽고 쓸 수 있게 해주는 컨버터다. text/xml, application/xml 일 때 주로 읽고 쓴다. |
| MappingJackson2HttpMessageConverter | Jackson의 ObjectMapper를 사용해서 JSON을 읽고 쓸 수 있게 해주는 컨버터다. |
| MappingJackson2XmlHttpMessageConverter | Jackson의 ObjectMapper를 사용해서 XML을 읽고 쓸 수 있게 해주는 컨버터다. |
| SourceHttpMessageConverter | javax.xml.transform.Source을 읽고 쓸 수 있게 해주는 컨버터다. DOMSource, SAXSource, StreamSource를 읽고 쓸수 있게 해준다. |
WebClient
비동기 API가 있는 클아이언트
특징
- 논 블로킹 I/O 지원
- 리액티브 스트림의 백프레셔 지원
- 고가용성
- 함수형 타입을 지원한다
- 동기, 비동기 상호 작용 지원
initialize
- WebClient.create()
- WebClient.create(String baseUrl)
WebClient.builder()는 아래 옵션을 설정할 수 있게 한다.
- uriBuilderFactory:
UriBuilderFactory로 URI를 커스터마이징 할 수 있다. - defaultUriVariables: UriTemplate을 확장할 때 사용한다.
- defaultHeader: 매 요청 시 담기는 기본 헤더
- defaultCookie: 매 요청 시 담기는 기본 쿠키
- defaultRequest:매 요청 시 기본 요청으로 실행할 Consumer
- filter: 매 요청 시 적용할 필터
- exchangeStrategies: Http 메시지를 읽고 쓸 전략을 설정한다.
- clientConnector: HTTP client library를 세팅할 수 있다.
- observationRegistry: the registry to use for enabling Observability support.
- observationConvention: an optional, custom convention to extract metadata for recorded observations.
WebClient client1 = WebClient.builder()
.filter(filterA).filter(filterB).build();
WebClient client2 = client1.mutate()
.filter(filterC).filter(filterD).build();
// client1 has filterA, filterB
// client2 has filterA, filterB, filterC, filterD
Request
WebClient와 유사하다만 Reactor를 채용하고 있단 부분이 다르다.
retrieve()로 response를 추출할 수 있다.
WebClient client = WebClient.create("https://example.org");
Mono<ResponseEntity<Person>> result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.toEntity(Person.class);
- 바디만을 직접 받을 수도 있다.
WebClient client = WebClient.create("https://example.org");
Mono<Person> result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Person.class);
Flux<Quote> result = client.get()
.uri("/quotes").accept(MediaType.TEXT_EVENT_STREAM)
.retrieve()
.bodyToFlux(Quote.class);
exchange()로 디테일하게 사용하기
exchangeToMono()exchangeToFlux()awaitExchange()exchangeToFlow()
Mono<Person> entityMono = client.get()
.uri("/persons/1")
.accept(MediaType.APPLICATION_JSON)
.exchangeToMono(response -> {
if (response.statusCode().equals(HttpStatus.OK)) {
return response.bodyToMono(Person.class);
}
else {
// Turn to error
return response.createError();
}
});
RestTemplate
템플릿 메소드 API가 있는 동기 클라이언트
WebClient, RestClient에 비해 스프링에서 제공하는 오래된 템플릿 클래스다.
| Method | Description |
|---|---|
| getForObject | GET으로 요청 |
| getForEntity | GET으로 요청 ResponseEntity로 반환 |
| headForHeaders | HEAD 메소드로 리소스 헤더 요청 |
| postForLocation | POST로 요청하고 Location header를 반환 받음 |
| postForObject | POST로 요청하고 반환 받음 |
| postForEntity | POST로 요청하고 ResponseEntity로를 반환 받음 |
| put | PUT |
| patchForObject | PATCH로 요청 |
| delete | DELETE |
| optionsForAllow | ALLOW로 허용된 HTTP 메소드 검색 |
| exchange | 디테일하게 요청을 설정해서 보내는 메소드 |
| execute | 콜백 인터페이스로 요청 - 응답을 하는 방식 |
After Spring 6
spring 6 이후로 정의 된 기능이다. Java Interface로 HTTP 메소드를 정의하면 프록시로 이를 정의해서 API 호출을 가능하게 해준다.
자바 인터페이스 + @HttpExchange를 조합해서 HttpServiceProxyFactory에 넘기면 프록시로 생성한다.
형태
@HttpExchange(url = "/repos/{owner}/{repo}", accept = "application/vnd.github.v3+json")
interface RepositoryService {
@GetExchange
Repository getRepository(@PathVariable String owner, @PathVariable String repo);
@PatchExchange(contentType = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
void updateRepository(@PathVariable String owner, @PathVariable String repo,
@RequestParam String name, @RequestParam String description, @RequestParam String homepage);
}
RestClient를 사용해서 HttpInterface 등록
RestClient restClient = RestClient.builder().baseUrl("https://api.github.com/").build();
RestClientAdapter adapter = RestClientAdapter.create(restClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
RepositoryService service = factory.createClient(RepositoryService.class);
WebClient를 사용해서 HttpInterface 등록
WebClient webClient = WebClient.builder().baseUrl("https://api.github.com/").build();
WebClientAdapter adapter = WebClientAdapter.create(webClient);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
RepositoryService service = factory.createClient(RepositoryService.class);
RestTemplate을 사용해서 HttpInterface 등록
RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory("https://api.github.com/"));
RestTemplateAdapter adapter = RestTemplateAdapter.create(restTemplate);
HttpServiceProxyFactory factory = HttpServiceProxyFactory.builderFor(adapter).build();
RepositoryService service = factory.createClient(RepositoryService.class);
Annotation
| Annotation | Description |
|---|---|
| @HttpExchange | HttpExchange 대상으로 선정 |
| @RequestHeader | 요청 헤더 |
| @PathVariable | Path Variable을 설정 |
| @RequestBody | RequestBody로 지정 |
| @RequestParam | QueryString으로 지정 |
| @RequestPart | PartFile로 지정 |
| @CookieValue | 쿠키 값으로 지정 |
| @GetExchange | GET |
| @PostExchange | POST |
| @PutExchange | PUT |
| @PatchExchange | PATCH |
| @DeleteExchange | DELETE |