JEP 321: HTTP Client (Standard)

JEP 321: HTTP Client (Standard)

JDK 11에서 JDK 9에 도입 되고 JDK 10에서 업데이트 된 JEP110(HTTP 클라이언트 API(Incubator))를 표준화합니다 .

이 JEP는 JDK 9에서 잠복기 API로 소개되고 JDK 10에서 업데이트 된 HTTP 클라이언트 API를 표준화 할 것을 제안합니다. 잠복기 API는 상당한 개선을 가져온 여러 차례의 피드백을 받았지만 여전히 높은 수준에서 대체로 동일합니다. API는 CompletableFutures를 통해 non-blocking 요청 및 응답 의미론을 제공하며 , 종속 동작을 트리거하기 위해 연결될 수 있습니다. 요청 및 응답 본문의 역 압력 및 흐름 제어 는 API 의 플랫폼의 반응 스트림 지원을 통해 java.util.concurrent.Flow제공됩니다.

JDK 9와 JDK 10에서 배양하는 동안 구현은 거의 완전히 다시 작성되었습니다. 구현은 이제 완전히 비동기 적입니다 (이전 HTTP / 1.1 구현이 차단되었습니다). RX Flow 개념의 사용은 구현으로 밀어 넣어졌으며 HTTP / 2를 지원하는 데 필요한 여러 가지 기존 사용자 정의 개념이 제거되었습니다. 사용자 수준 요청 게시자 및 응답 구독자에서 기본 소켓까지 데이터 흐름을보다 쉽게 ​​추적 할 수 있습니다. 이것은 코드의 개념과 복잡성의 수를 크게 줄이고 HTTP / 1.1과 HTTP / 2 사이의 재사용 가능성을 극대화합니다.

표준 API의 모듈 이름과 패키지 이름이됩니다 java.net.http

jdk 10 이후의 변경사항

미리 정의의 구현 BodyPublisher, BodyHandler그리고 BodySubscriber정적 팩토리 메소드를 통해 생성은,의 복수로 명명 규칙에 따라 비 인스턴스화 유틸리티 팩토리 클래스를 분리하기 위해 밖으로 이동되었습니다. 이렇게하면 상대적으로 작은 인터페이스의 가독성이 향상됩니다.

정적 팩토리 메소드의 이름은 다음과 같은 광범위한 범주로 업데이트되었습니다.

  • fromXxx: Adapters from standard Subscriber, e.g. takes a Flow.Subscriber returns a BodySubscriber.
  • ofXxx: Factories that create a new pre-defined Body[Publisher|Handler|Subscriber] that perform useful common tasks, such as handling the response body as a String, or streaming the body to a File.
  • other: Combinators (takes a BodySubscriber returns a BodySubscriber) and other useful operations.

일반적인 시나리오에서 사용성을 향상시키기 위해 몇 가지 BodyHandler및 해당 BodySubscriber사항이 추가되었습니다.

  • discard(Object replacement)응답 바디를 무시 / 무시하고 주어진 교체를 허용 함. 피드백은 혼란 스러울 수 있다고 지적했습니다. 그것은 제거되었고 두 개의 분리 된 핸들러로 대체되었습니다 : 1) discarding(), 2) replacing(Object replacement).
  • 추가 ofLines()반환 BodyHandler<Stream>하는로 응답 본문의 스트리밍을 지원하는 Stream라인, 라인별로 라인. 그것과 비슷한 의미를 제공합니다 BufferedReader.lines().
  • 추가 fromLineSubscriber​된 Flow.Subscriber것은 String라인 에 대한 응답 본문의 적용을 지원합니다 .
  • BodySubscriber.mapping하나의 응답 본문 유형에서 다른 응답 본문 유형으로 범용 매핑을 추가 했습니다.

푸시 약속 지원은 API에 대한 영향을 줄이고 정기적 인 요청 / 응답에 더 많이 반영되도록 다시 작업되었습니다. 특히 MultiSubscriber와 MultiResultMap는 제거되었습니다. 푸시 약속은 ​​이제 PushPromiseHandler보내기 작업 중에 선택적으로 제공되는 기능 인터페이스를 통해 처리 됩니다.

HttpClient.Redirect정책은 대체함으로써, 단순화되었습니다 SAME_PROTOCOL와 SECURE함께, 정책 NORMAL. 이전에 명명 된 이름 SECURE은 실제로 적절하게 명명되지 않았으며 이름 이 변경되어야한다는 사실이 관찰되었습니다. 이는 NORMAL대부분의 일반적인 경우에 적합 할 가능성이 높기 때문입니다. 을 감안할 때 새로 전술, 이름, NORMAL, SAME_PROTOCOL이상한 이름이 나타납니다, 아마도 혼란 및 사용 가능성이 없습니다.

WebSocket.MessagePart제거 되었어. 이 열거 형은 수신 측에서 메시지 전달이 완료되었는지 여부를 나타 내기 위해 사용되었습니다. 이 목적을 위해 간단한 부울을 사용하는 송신 측과 비대칭입니다. 또한 수신 된 메시지를 단순한 부울 값으로 처리하면 수신 코드 논리가 크게 줄어들고 단순화된다는 것이 관찰되었습니다. WHOLE앞서 말한 이점과 주요 목적 중 하나로 전달되는 메시지의 결정은 MessagePart자체 무게를 지니지 않는 것으로 판명되었습니다.

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
69
70
71
72
73
74
75
76
77
78

import java.io.IOException;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class HttpClientExamples {

public static void main(String[] args) throws IOException, InterruptedException {
syncRequest();
asyncRequest();
postData();
basicAuth();
}

private static void syncRequest() throws IOException, InterruptedException {
var request = HttpRequest.newBuilder()
.uri(URI.create("https://winterbe.com"))
.build();
var client = HttpClient.newHttpClient();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
}

private static void asyncRequest() {
var request = HttpRequest.newBuilder()
.uri(URI.create("https://winterbe.com"))
.build();
var client = HttpClient.newHttpClient();
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println);
}

private static void postData() throws IOException, InterruptedException {
var request = HttpRequest.newBuilder()
.uri(URI.create("https://postman-echo.com/post"))
.timeout(Duration.ofSeconds(30))
.version(HttpClient.Version.HTTP_2)
.header("Content-Type", "text/plain")
.POST(HttpRequest.BodyPublishers.ofString("Hi there!"))
.build();
var client = HttpClient.newHttpClient();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode()); // 200
}

private static void basicAuth() throws IOException, InterruptedException {
var client = HttpClient.newHttpClient();

var request1 = HttpRequest.newBuilder()
.uri(URI.create("https://postman-echo.com/basic-auth"))
.build();
var response1 = client.send(request1, HttpResponse.BodyHandlers.ofString());
System.out.println(response1.statusCode()); // 401

var authClient = HttpClient
.newBuilder()
.authenticator(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("postman", "password".toCharArray());
}
})
.build();
var request2 = HttpRequest.newBuilder()
.uri(URI.create("https://postman-echo.com/basic-auth"))
.build();
var response2 = authClient.send(request2, HttpResponse.BodyHandlers.ofString());
System.out.println(response2.statusCode()); // 200
}

}

참조