1. 소개

이 예제에서는 Java 9의 새로운 인큐베이팅 HttpClient 를 살펴보겠습니다 .

아주 최근까지 Java는 HttpURLConnection API 만 제공했습니다. 이 API는 수준이 낮고 기능이 풍부 하고 사용자 친화적인 것으로 알려져 있지 않습니다 .

따라서 Apache HttpClient , Jetty 및 Spring의 RestTemplate 과 같이 널리 사용되는 일부 타사 라이브러리가 일반적으로 사용되었습니다 .

2. 초기 설정

HTTP 클라이언트 모듈은 JDK 9 의 인큐베이터 모듈로 번들되며 여전히 HTTP/1.1을 용이하게 하는 이전 버전과의 호환성으로 HTTP/2지원합니다 .

이를 사용하려면 애플리케이션을 실행하는 데 필요한 모듈을 나타내는 module-info.java 파일을 사용하여 모듈을 정의해야 합니다.

module com.baeldung.java9.httpclient {   
  requires jdk.incubator.httpclient;
}

3. HTTP 클라이언트 API 개요

HttpURLConnection 과 달리 HTTP 클라이언트는 동기 및 비동기 요청 메커니즘을 제공합니다.

API는 3가지 핵심 클래스로 구성됩니다.

  • HttpRequest HttpClient 를 통해 보낼 요청을 나타냅니다.
  • HttpClient 여러 요청에 공통적인 구성 정보의 컨테이너 역할을 합니다.
  • HttpResponse HttpRequest 호출 의 결과를 나타냅니다.

다음 섹션에서 각각에 대해 더 자세히 살펴보겠습니다. 먼저 요청에 중점을 두겠습니다.

4. HttpRequest

HttpRequest 는 이름에서 알 수 있듯이 보내려는 요청을 나타내는 객체입니다. HttpRequest.Builder를 사용하여 새 인스턴스를 만들 수 있습니다 .

HttpRequest.newBuilder() 를 호출하여 얻을 수 있습니다 . Builder 클래스는 요청을 구성하는 데 사용할 수 있는 많은 메서드를 제공합니다.

우리는 가장 중요한 것들을 다룰 것입니다.

4.1. 설정 URI

요청을 생성할 때 가장 먼저 해야 할 일은 URL을 제공하는 것입니다.

URI 매개변수 와 함께 Builder 용 생성자를 사용 하거나 Builder 인스턴스 에서 uri(URI) 메소드를 호출하는 두 가지 방법으로 이를 수행할 수 있습니다 .

HttpRequest.newBuilder(new URI("https://postman-echo.com/get"))
 
HttpRequest.newBuilder()
  .uri(new URI("https://postman-echo.com/get"))

기본 요청을 생성하기 위해 구성해야 하는 마지막 사항은 HTTP 메서드입니다.

4.2. HTTP 메서드 지정

Builder 에서 메소드 중 하나를 호출하여 요청이 사용할 HTTP 메소드를 정의할 수 있습니다 .

  • 가져 오기()
  • POST(BodyProcessor 본체)
  • PUT(BodyProcessor 본체)
  • DELETE(BodyProcessor 본체)

BodyProcessor 에 대해서는 나중에 자세히 다루 겠습니다 . 이제 매우 간단한 GET 요청 예제를 만들어 보겠습니다 .

HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI("https://postman-echo.com/get"))
  .GET()
  .build();

이 요청에는 HttpClient에 필요한 모든 매개변수가 있습니다. 그러나 때로는 요청에 추가 매개변수를 추가해야 합니다. 다음은 몇 가지 중요한 사항입니다.

  • HTTP 프로토콜의 버전
  • 헤더
  • 타임아웃

4.3. HTTP 프로토콜 버전 설정

API는 HTTP/2 프로토콜을 완전히 활용하고 기본적으로 이를 사용하지만 사용하려는 프로토콜 버전을 정의할 수 있습니다.

HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI("https://postman-echo.com/get"))
  .version(HttpClient.Version.HTTP_2)
  .GET()
  .build();

여기서 언급해야 할 중요한 점은 예를 들어 HTTP/2가 지원되지 않는 경우 클라이언트가 HTTP/1.1로 대체한다는 것입니다.

4.4. 헤더 설정

요청에 추가 헤더를 추가하려는 경우 제공된 빌더 메서드를 사용할 수 있습니다.

다음 두 가지 방법 중 하나로 이를 수행할 수 있습니다.

  • 모든 헤더를 키-값 쌍으로 headers() 메서드에 전달하거나
  • 단일 키-값 헤더에 대해 header() 메서드 사용 :
HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI("https://postman-echo.com/get"))
  .headers("key1", "value1", "key2", "value2")
  .GET()
  .build();

HttpRequest request2 = HttpRequest.newBuilder()
  .uri(new URI("https://postman-echo.com/get"))
  .header("key1", "value1")
  .header("key2", "value2")
  .GET()
  .build();

요청을 사용자 정의하는 데 사용할 수 있는 마지막 유용한 방법은 timeout() 입니다.

4.5. 시간 초과 설정

이제 응답을 기다리는 시간을 정의해 보겠습니다.

설정된 시간이 만료되면 HttpTimeoutException 이 발생합니다. 기본 시간 초과는 무한대로 설정됩니다.

빌더 인스턴스에서 timeout() 메서드를 호출 하여 Duration 객체로 타임아웃을 설정할 수 있습니다 .

HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI("https://postman-echo.com/get"))
  .timeout(Duration.of(10, SECONDS))
  .GET()
  .build();

5. 요청 본문 설정

요청 빌더 메소드인 POST(BodyProcessor body) , PUT(BodyProcessor body)DELETE(BodyProcessor body) 를 사용하여 요청에 본문을 추가할 수 있습니다 .

새 API는 요청 본문 전달을 단순화하는 즉시 사용 가능한 여러 BodyProcessor 구현을 제공합니다 .

  • StringProcessor ( HttpRequest.BodyProcessor.fromString 으로 생성 String 에서 본문 읽기 )
  • InputStreamProcessor ( HttpRequest.BodyProcessor.fromInputStream 으로 생성 InputStream 에서 본문 읽기 )
  • ByteArrayProcessor ( HttpRequest.BodyProcessor.fromByteArray 로 만든 바이트 배열에서 본문 읽기 )
  • FileProcessor ( HttpRequest.BodyProcessor.fromFile 로 생성된 지정된 경로의 파일에서 본문을 읽음 )

본문이 필요하지 않은 경우 HttpRequest.noBody() 를 전달할 수 있습니다 .

HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI("https://postman-echo.com/post"))
  .POST(HttpRequest.noBody())
  .build();

5.1. StringBodyProcessor

BodyProcessor 구현 으로 요청 본문을 설정하는 것은 매우 간단하고 직관적입니다.

예를 들어 간단한 String 을 본문으로 전달하려는 경우 StringBodyProcessor 를 사용할 수 있습니다 .

이미 언급했듯이 이 객체는 fromString() 팩토리 메서드로 생성할 수 있습니다 . 그것은 단지 String 객체를 인수로 취하고 그것 으로부터 본문을 생성합니다:

HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI("https://postman-echo.com/post"))
  .headers("Content-Type", "text/plain;charset=UTF-8")
  .POST(HttpRequest.BodyProcessor.fromString("Sample request body"))
  .build();

5.2. InputStreamBodyProcessor

그렇게 하려면 InputStream공급자 로 전달되어야 하므로(생성을 지연시키기 위해) 위에서 설명한 StringBodyProcessor와 약간 다릅니다 .

그러나 이것은 또한 매우 간단합니다.

byte[] sampleData = "Sample request body".getBytes();
HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI("https://postman-echo.com/post"))
  .headers("Content-Type", "text/plain;charset=UTF-8")
  .POST(HttpRequest.BodyProcessor
   .fromInputStream(() -> new ByteArrayInputStream(sampleData)))
  .build();

여기 에서 간단한 ByteArrayInputStream 을 사용한 방법에 주목 하십시오. 물론 모든 InputStream 구현이 될 수 있습니다 .

5.3. 바이트 배열 프로세서

ByteArrayProcessor 를 사용 하고 바이트 배열을 매개변수로 전달할 수도 있습니다 .

byte[] sampleData = "Sample request body".getBytes();
HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI("https://postman-echo.com/post"))
  .headers("Content-Type", "text/plain;charset=UTF-8")
  .POST(HttpRequest.BodyProcessor.fromByteArray(sampleData))
  .build();

5.4. 파일 프로세서

File로 작업하기 위해 제공된 FileProcessor를 사용할 수 있습니다 . 팩토리 메소드는 파일의 경로를 매개변수로 취하고 내용에서 본문을 작성합니다.

HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI("https://postman-echo.com/post"))
  .headers("Content-Type", "text/plain;charset=UTF-8")
  .POST(HttpRequest.BodyProcessor.fromFile(
    Paths.get("src/test/resources/sample.txt")))
  .build();

HttpRequest 를 생성하는 방법 과 추가 매개변수를 설정하는 방법을 다루었 습니다.

이제 요청을 보내고 응답을 받는 HttpClient 클래스를 자세히 살펴보겠습니다 .

6. Http클라이언트

모든 요청은 HttpClient.newBuilder() 메서드를 사용하거나 HttpClient.newHttpClient() 를 호출 하여 인스턴스화할 수 있는 HttpClient사용하여 전송됩니다 .

요청/응답을 처리하는 데 사용할 수 있는 유용하고 자체 설명적인 방법을 많이 제공합니다.

여기에서 몇 가지를 살펴보겠습니다.

6.1. 프록시 설정

연결에 대한 프록시를 정의할 수 있습니다. Builder 인스턴스 에서 proxy() 메서드를 호출하기만 하면 됩니다 .

HttpResponse<String> response = HttpClient
  .newBuilder()
  .proxy(ProxySelector.getDefault())
  .build()
  .send(request, HttpResponse.BodyHandler.asString());

이 예에서는 기본 시스템 프록시를 사용했습니다.

6.2. 리디렉션 정책 설정

액세스하려는 페이지가 다른 주소로 이동하는 경우가 있습니다.

이 경우 일반적으로 새 URI에 대한 정보와 함께 HTTP 상태 코드 3xx를 받게 됩니다. 적절한 리디렉션 정책을 설정하면 HttpClient 가 자동으로 요청을 새 URI로 리디렉션할 수 있습니다.

BuilderfollowRedirects() 메서드를 사용하여 수행할 수 있습니다 .

HttpResponse<String> response = HttpClient.newBuilder()
  .followRedirects(HttpClient.Redirect.ALWAYS)
  .build()
  .send(request, HttpResponse.BodyHandler.asString());

모든 정책은 enum HttpClient.Redirect 에 정의 및 설명되어 있습니다.

6.3. 연결에 대한 인증자 설정

인증자는 연결에 대한 자격 증명 (HTTP 인증) 협상 객체입니다.

다양한 인증 체계(예: 기본 또는 다이제스트 인증)를 제공합니다. 대부분의 경우 인증 시 서버에 연결하려면 사용자 이름과 암호가 필요합니다.

다음 값의 보유자일 뿐인 PasswordAuthentication 클래스를 사용할 수 있습니다 .

HttpResponse<String> response = HttpClient.newBuilder()
  .authenticator(new Authenticator() {
    @Override
    protected PasswordAuthentication getPasswordAuthentication() {
      return new PasswordAuthentication(
        "username", 
        "password".toCharArray());
    }
}).build()
  .send(request, HttpResponse.BodyHandler.asString());

위의 예에서 사용자 이름과 비밀번호 값을 일반 텍스트로 전달했습니다. 물론 프로덕션 시나리오에서는 달라야 합니다.

모든 요청에 ​​동일한 사용자 이름과 암호를 사용해야 하는 것은 아닙니다. 인증 자 클래스는 다수 제공 하는 getXXX를 (예를 들어, getRequestingSite () ) 값을 제공해야한다 무엇을 찾을 수 있습니다 방법.

이제 새로운 HttpClient 의 가장 유용한 기능 중 하나인 서버에 대한 비동기식 호출을 살펴보겠습니다 .

6.4. 요청 보내기 – 동기화 대 비동기

새로운 HttpClient는 서버에 요청을 보내는 두 가지 가능성을 제공합니다.

  • send(…) – 동기식 (응답이 올 때까지 차단)
  • sendAsync(…) – 비동기식 (응답을 기다리지 않음, 비차단)

지금까지 send( ...) 메서드는 자연스럽게 응답을 기다립니다.

HttpResponse<String> response = HttpClient.newBuilder()
  .build()
  .send(request, HttpResponse.BodyHandler.asString());

이 호출은 HttpResponse 개체를 반환 하고 응답이 이미 있는 경우에만 응용 프로그램 흐름의 다음 명령이 실행될 것이라고 확신합니다.

그러나 특히 많은 양의 데이터를 처리할 때 많은 단점이 있습니다.

그래서, 지금, 우리가 사용할 수있는 가 SendAsync를 (. ..) 메소드 - 반환 CompletableFeature을 <HttpResponse에> - 비동기 요청을 처리 할 수 :

CompletableFuture<HttpResponse<String>> response = HttpClient.newBuilder()
  .build()
  .sendAsync(request, HttpResponse.BodyHandler.asString());

새로운 API는 여러 응답을 처리하고 요청 및 Response body을 스트리밍할 수도 있습니다.

List<URI> targets = Arrays.asList(
  new URI("https://postman-echo.com/get?foo1=bar1"),
  new URI("https://postman-echo.com/get?foo2=bar2"));
HttpClient client = HttpClient.newHttpClient();
List<CompletableFuture<String>> futures = targets.stream()
  .map(target -> client
    .sendAsync(
      HttpRequest.newBuilder(target).GET().build(),
      HttpResponse.BodyHandler.asString())
    .thenApply(response -> response.body()))
  .collect(Collectors.toList());

6.5. 비동기 호출을 위한 실행기 설정

비동기 호출에서 사용할 스레드를 제공 하는 Executor정의할 수도 있습니다.

이 방법으로 예를 들어 요청 처리에 사용되는 스레드 수를 제한할 수 있습니다.

ExecutorService executorService = Executors.newFixedThreadPool(2);

CompletableFuture<HttpResponse<String>> response1 = HttpClient.newBuilder()
  .executor(executorService)
  .build()
  .sendAsync(request, HttpResponse.BodyHandler.asString());

CompletableFuture<HttpResponse<String>> response2 = HttpClient.newBuilder()
  .executor(executorService)
  .build()
  .sendAsync(request, HttpResponse.BodyHandler.asString());

기본적으로 HttpClient 는 실행자 java.util.concurrent.Executors.newCachedThreadPool()을 사용 합니다.

6.6. CookieManager 정의

새로운 API와 빌더를 사용하면 연결에 대한 CookieManager 를 설정하는 것이 간단 합니다. 빌더 메소드인 cookieManager(CookieManager cookieManager) 를 사용하여 클라이언트별 CookieManager 를 정의 할 수 있습니다 .

예를 들어 쿠키를 전혀 허용하지 않는 CookieManager정의해 보겠습니다 .

HttpClient.newBuilder()
  .cookieManager(new CookieManager(null, CookiePolicy.ACCEPT_NONE))
  .build();

CookieManager가 쿠키 저장을 허용하는 경우 HttpClient 에서 CookieManager확인하여 쿠키에 액세스할 수 있습니다 .

httpClient.cookieManager().get().getCookieStore()

이제 Http API의 마지막 클래스인 HttpResponse 에 집중해 보겠습니다 .

7. HttpResponse 객체

대한 HttpResponse 클래스는 서버의 응답을 나타냅니다. 여러 가지 유용한 방법을 제공하지만 가장 중요한 두 가지는 다음과 같습니다.

  • statusCode()응답에 대한 상태 코드(유형 int )를 반환합니다 ( HttpURLConnection 클래스에는 가능한 값이 포함됨 ).
  • body() – 응답에 대한 본문을 반환합니다(반환 유형은 send() 메서드에 전달된 응답 BodyHandler 매개변수 에 따라 다름 )

응답 객체에는 uri() , headers() , trailers()version()같은 다른 유용한 메서드가 있습니다.

7.1. 응답 객체의 URI

응답 객체의 uri() 메서드 는 응답 을 받은 URI반환합니다 .

리디렉션이 발생할 수 있으므로 때때로 요청 개체의 URI다를 수 있습니다.

assertThat(request.uri()
  .toString(), equalTo("http://stackoverflow.com"));
assertThat(response.uri()
  .toString(), equalTo("https://stackoverflow.com/"));

7.2. 응답의 헤더

응답 객체에서 headers() 메서드를 호출하여 응답에서 헤더를 얻을 수 있습니다 .

HttpResponse<String> response = HttpClient.newHttpClient()
  .send(request, HttpResponse.BodyHandler.asString());
HttpHeaders responseHeaders = response.headers();

그것은 반환 HttpHeaders의 반환 형식으로 개체를. 이것은 HTTP 헤더의 읽기 전용 보기를 나타내는 jdk.incubator.http 패키지에 정의된 새로운 유형 입니다.

헤더 값 검색을 단순화하는 몇 가지 유용한 방법이 있습니다.

7.3. 응답에서 예고편 가져오기

HTTP 응답에는 응답 내용 뒤에 포함되는 추가 헤더가 포함될 수 있습니다. 이러한 헤더를 트레일러 헤더라고 합니다.

HttpResponse 에서 trailers() 메서드를 호출하여 얻을 수 있습니다 .

HttpResponse<String> response = HttpClient.newHttpClient()
  .send(request, HttpResponse.BodyHandler.asString());
CompletableFuture<HttpHeaders> trailers = response.trailers();

참고 트레일러 () 메소드의 반환 CompletableFuture의 객체입니다.

7.4. 응답 버전

version() 메서드 는 서버와 통신하는 데 사용된 HTTP 프로토콜 버전을 정의합니다.

HTTP/2를 사용한다고 정의하더라도 서버는 HTTP/1.1을 통해 응답할 수 있음을 기억하십시오.

서버가 응답한 버전은 응답에 지정됩니다.

HttpRequest request = HttpRequest.newBuilder()
  .uri(new URI("https://postman-echo.com/get"))
  .version(HttpClient.Version.HTTP_2)
  .GET()
  .build();
HttpResponse<String> response = HttpClient.newHttpClient()
  .send(request, HttpResponse.BodyHandler.asString());
assertThat(response.version(), equalTo(HttpClient.Version.HTTP_1_1));

8. 자바 11 HTTP 클라이언트

Java 11의 주요 변경 사항은 HTTP/2 및 Web Socket을 구현하는 HTTP 클라이언트 API 의 표준화였습니다 .  Java 초기부터 JDK에 존재했던 레거시 HttpUrlConnection 클래스 를 대체하는 것을 목표로 합니다  .

변경 사항은 JEP 321의 일부로 구현되었습니다.

8.1. JEP 321의 일부로 주요 변경 사항

  1. Java 9에서 배양된 HTTP API가 이제 공식적으로 Java SE API에 통합되었습니다. 새로운  HTTP API  는 java.net.HTTP.* 에서 찾을 수 있습니다  .
  2. 최신 버전의 HTTP 프로토콜은 클라이언트에서 요청을 보내고 서버에서 응답을 받는 전반적인 성능을 향상시키도록 설계되었습니다. 이는 스트림 다중화, 헤더 압축 및 푸시 약속과 같은 여러 변경 사항을 도입하여 달성됩니다.
  3. Java 11부터  API는 이제 완전히 비동기식입니다(이전 HTTP/1.1 구현은 차단됨).  비동기식 호출은 CompletableFuture를 사용하여  구현됩니다 . CompletableFuture 구현은 이전 단계가 완료되면 각 단계를 적용하므로 이 전체 흐름이 비동기식입니다.
  4. 새로운 HTTP 클라이언트 API는 타사 의존성을 추가할 필요 없이 HTTP/2와 같은 최신 웹 기능을 지원하여 HTTP 네트워크 작업을 수행하는 표준 방법을 제공합니다.
  5. 새 API는 HTTP 1.1/2 WebSocket에 대한 기본 지원을 제공합니다. 핵심 기능을 제공하는 핵심 클래스 및 인터페이스는 다음과 같습니다.
  • HttpClient를 클래스, java.net.http.HttpClient
  • HttpRequest에 클래스,  java.net.http.HttpRequest
  • HttpResponse에 <T> 인터페이스  java.net.http.HttpResponse
  • 웹 소켓 인터페이스,  java.net.http.WebSocket

8.2. Java 11 이전 HTTP 클라이언트의 문제

기존  HttpURLConnection  API와 그 구현에는 다음과 같은 많은 문제가 있었습니다.

  • URLConnection API는 더 이상 작동하지 않는 여러 프로토콜(FTP, gopher 등)로 설계되었습니다.
  • API는 HTTP/1.1보다 이전 버전이며 너무 추상적입니다.
  • 차단 모드에서만 작동합니다(즉, 요청/응답당 하나의 스레드).
  • 유지하기가 매우 어렵습니다.

9. Java 11에서 Http 클라이언트의 변경 사항

9.1. 정적 팩토리 클래스 소개

새로운 정적 팩토리 클래스의  BodyPublishers , BodySubscribers,  및 BodyHandlers는  기존의 구현 등이 소개된다 BodyPublisher , BodySubscriberBodyHandler을.

이들은 Response body을 문자열로 처리하거나 본문을 파일로 스트리밍하는 것과 같은 유용한 일반 작업을 수행하는 데 사용됩니다.

예를 들어 Pre Java 11에서는 다음과 같이 해야 했습니다.

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandler.asString());

이제 다음과 같이 단순화할 수 있습니다.

HttpResponse<String> response = client.send(request, BodyHandlers.ofString());

또한 보다 명확하게 하기 위해 정적 메서드의 이름을 표준화했습니다.

예를 들어 fromXxx 와 같은 메소드 이름은 어댑터 로 사용할 때 사용되거나  미리 정의된 핸들러/가입자를 만들 때 ofXxx 와 같은 이름으로 사용됩니다  .

9.2. 일반적인 신체 유형을 위한 유창한 방법

생성된 게시자 및 일반적인 바디 유형을 처리하기 위한 핸들러를 위한 편리한 팩토리 메서드가 도입되었습니다.

예를 들어 바이트, 파일 및 문자열에서 게시자를 만드는 유창한 방법은 다음과 같습니다.

BodyPublishers.ofByteArray
BodyPublishers.ofFile
BodyPublishers.ofString

유사하게, 이러한 공통 바디 유형에서 핸들러를 생성하기 위해 다음을 사용할 수 있습니다.

BodyHandlers.ofByteArray
BodyHandlers.ofString
BodyHandlers.ofFile

9.3. 기타 API 변경 사항

1. 이 새로운 API를 사용하면  폐기(객체 교체) 대신 BodyHandlers.discarding() 및  BodyHandlers.replacing(값)을 사용할 것입니다 .

HttpResponse<?> response1 = HttpClient.newHttpClient()
            .send(request, BodyHandlers.discarding());
HttpResponse<?> response1 = HttpClient.newHttpClient()
            .send(request, BodyHandlers.replacing(value));

2. 새로운 방법  ofLines ()  에  BodyHandlers가 첨가되는  라인의 스트림으로 응답 본체 스트리밍 처리.

3. fromLineSubscriber의 방법으로 첨가  BodyHandlers의  사이에 어댑터로서 사용될 수있다 클래스 BodySubscriber 및 텍스트 기반 Flow.Subscriber 선으로 텍스트 라인을 파싱한다.

4. BodySubscribers 클래스에 새로운 BodySubscriber.mapping  을 추가했습니다. 이  클래스는 주어진 기능을 본문 개체에 적용하여 한 Response body 유형에서 다른 Response body 유형으로 매핑하는 데 사용할 수 있습니다.

5. HttpClient.Redirect 에서 열거형 상수 SAME_PROTOCOL  및  SECURE 정책은 새로운 열거형 NORMAL 로 대체됩니다 .

10. HTTP/2에서 푸시 약속 처리하기

새로운 Http 클라이언트는 PushPromiseHandler 인터페이스를 통해 푸시 약속을 지원합니다

이를 통해 서버는 기본 리소스를 요청하는 동안 클라이언트에 추가 리소스를 "푸시"하여 더 많은 왕복을 절약할 수 있으며 결과적으로 페이지 렌더링 성능이 향상됩니다.

리소스 번들링을 잊게 해주는 것은 실제로 HTTP/2의 다중화 기능입니다. 각 리소스에 대해 서버는 푸시 약속이라고 하는 특별한 요청을 클라이언트에 보냅니다.

수신된 푸시 프라미스 가 있는 경우 지정된 PushPromiseHandler 에 의해 처리됩니다 . null 값 PushPromiseHnadler는 푸시 약속을 거부합니다.

HttpClient를는 과부하 가지고  가 SendAsync의  아래 예와 같이 우리가 이러한 약속을 처리 할 수 방법.

먼저 PushPromiseHandler를 생성해 보겠습니다 .

private static PushPromiseHandler<String> pushPromiseHandler() {
    return (HttpRequest initiatingRequest, 
        HttpRequest pushPromiseRequest, 
        Function<HttpResponse.BodyHandler<String>, 
        CompletableFuture<HttpResponse<String>>> acceptor) -> {
        acceptor.apply(BodyHandlers.ofString())
            .thenAccept(resp -> {
                System.out.println(" Pushed response: " + resp.uri() + ", headers: " + resp.headers());
            });
        System.out.println("Promise request: " + pushPromiseRequest.uri());
        System.out.println("Promise request: " + pushPromiseRequest.headers());
    };
}

다음으로 sendAsync 메서드를 사용 하여 이 푸시 약속을 처리해 보겠습니다 .

httpClient.sendAsync(pageRequest, BodyHandlers.ofString(), pushPromiseHandler())
    .thenAccept(pageResponse -> {
        System.out.println("Page response status code: " + pageResponse.statusCode());
        System.out.println("Page response headers: " + pageResponse.headers());
        String responseBody = pageResponse.body();
        System.out.println(responseBody);
    })
    .join();

11. 결론

이 기사에서는 많은 유연성과 강력한 기능을 제공하는 Java 9의 HttpClient API를 살펴보았습니다 . Java 9의 HttpClient API에 사용되는 전체 코드는 GitHub에서 사용할 수  있습니다 .

또한 Java 9에 도입된 인큐베이팅 HttpClient를 보다 강력한 변경으로 표준화한 Java 11 HttpClient의 새로운 변경 사항을 살펴보았습니다. Java 11 Http Client에 사용되는 코드 조각은 Github 에서도 사용할 수 있습니다 .

참고: 예제에서는 https://postman-echo.com에서 제공하는 샘플 REST 끝점을 사용했습니다 .

HTTPClient footer