1. 개요

Java HttpClient API는 Java 11에서 도입되었습니다. API는 최신 HTTP 표준의 클라이언트 측을 구현합니다 . 동기 및 비동기 프로그래밍 모델인 HTTP/1.1 및 HTTP/2를 지원합니다.

이를 사용하여 HTTP 요청을 보내고 응답을 검색할 수 있습니다. Java 11 이전에는 기본적인 URLConnection 구현이나 Apache HttpClient 와 같은 타사 라이브러리 에 의존해야 했습니다 .

이 사용방법(예제)에서는 Java HttpClient 를 사용하여 POST 요청을 보내는 방법을 살펴보겠습니다 . 동시 POST 요청뿐만 아니라 동기 및 비동기 POST 요청을 모두 보내는 방법을 보여줍니다. 또한 POST 요청에 인증 매개변수 및 JSON 본문을 추가하는 방법을 확인합니다.

마지막으로 파일을 업로드하고 양식 데이터를 제출하는 방법을 살펴보겠습니다. 따라서 대부분의 일반적인 사용 사례를 다룰 것입니다.

2. POST 요청 준비

HTTP 요청을 보내기 전에 먼저 HttpClient 인스턴스를 만들어야 합니다 .

HttpClient 인스턴스는 newBuilder 메서드를 사용하여 빌더에서 구성 및 생성할 수 있습니다 . 그렇지 않고 구성이 필요하지 않은 경우 newHttpClient 유틸리티 메서드를 사용하여 기본 클라이언트를 만들 수 있습니다.

HttpClient client = HttpClient.newHttpClient();

HttpClient 는 기본적으로 HTTP/2를 사용합니다. 또한 서버가 HTTP/2를 지원하지 않는 경우 자동으로 HTTP/1.1로 다운그레이드됩니다.

이제 빌더에서 HttpRequest 인스턴스를 생성할 준비가 되었습니다 . 나중에 이 요청을 보내기 위해 클라이언트 인스턴스를 사용할 것입니다. POST 요청의 최소 매개변수는 서버 URL, 요청 방법 및 본문입니다.

HttpRequest request = HttpRequest.newBuilder()
  .uri(URI.create(serviceUrl))
  .POST(HttpRequest.BodyPublishers.noBody())
  .build();

BodyPublisher 클래스 를 통해 요청 본문을 제공해야 합니다 . 주문형 요청 본문 스트림을 게시하는 반응형 스트림 게시자입니다. 이 예에서는 요청 본문을 보내지 않는 본문 게시자를 사용했습니다.

3. POST 요청 보내기

이제 POST 요청을 준비했으므로 이를 보내는 다양한 옵션을 살펴보겠습니다.

3.1. 동기적으로

이 기본 전송 방법 을 사용하여 준비된 요청을 보낼 수 있습니다 . 이 메서드는 응답을 받을 때까지 코드를 차단합니다 .

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

BodyHandlers 유틸리티 는 Response body을 문자열 로 처리  하거나 Response body을 파일로 스트리밍하는 것과 같은 다양한 유용한 핸들러를 구현 합니다. 응답이 수신되면 HttpResponse 개체에는 응답 상태, 헤더 및 본문이 포함됩니다.

assertThat(response.statusCode())
  .isEqualTo(200);
assertThat(response.body())
  .isEqualTo("{\"message\":\"ok\"}");

3.2. 비동기적으로

sendAsync 메서드 를 사용하여 이전 예제에서 비동기적으로 동일한 요청을 보낼 수 있습니다. 코드를 차단하는 대신 이 메서드는 즉시 CompletableFuture 인스턴스를 반환합니다 .

CompletableFuture<HttpResponse<String>> futureResponse = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());

CompletableFuture사용 가능해지면 HttpResponse  로 완료됩니다 .

HttpResponse<String> response = futureResponse.get();
assertThat(response.statusCode()).isEqualTo(200);
assertThat(response.body()).isEqualTo("{\"message\":\"ok\"}");

3.3. 동시에

여러 요청을 동시에 발행하고 응답을 기다리기  위해 Streams 를  CompletableFutures 와 결합할 수 있습니다 .

List<CompletableFuture<HttpResponse<String>>> completableFutures = serviceUrls.stream()
  .map(URI::create)
  .map(HttpRequest::newBuilder)
  .map(builder -> builder.POST(HttpRequest.BodyPublishers.noBody()))
  .map(HttpRequest.Builder::build)
  .map(request -> client.sendAsync(request, HttpResponse.BodyHandlers.ofString()))
  .collect(Collectors.toList());

이제 응답을 한 번에 처리할 수 있도록 모든 요청이 완료될 때까지 기다리겠습니다.

CompletableFuture<List<HttpResponse<String>>> combinedFutures = CompletableFuture
  .allOf(completableFutures.toArray(new CompletableFuture[0]))
  .thenApply(future ->
    completableFutures.stream()
      .map(CompletableFuture::join)
      .collect(Collectors.toList()));

allOfjoin 메서드 를 사용하여 모든 응답을 결합 했으므로 응답을 보유 하는 새로운 CompletableFuture 를 얻습니다.

List<HttpResponse<String>> responses = combinedFutures.get();
responses.forEach((response) -> {
  assertThat(response.statusCode()).isEqualTo(200);
  assertThat(response.body()).isEqualTo("{\"message\":\"ok\"}");
});

4. 인증 매개변수 추가

모든 요청에 ​​대한 HTTP 인증을 위해 클라이언트 수준에서 인증자를 설정할 수 있습니다 .

HttpClient client = HttpClient.newBuilder()
  .authenticator(new Authenticator() {
    @Override
    protected PasswordAuthentication getPasswordAuthentication() {
      return new PasswordAuthentication(
        "baeldung",
        "123456".toCharArray());
      }
  })
  .build();

그러나 HttpClient 는 서버에서 WWW-Authenticate 헤더 로 인증 을 받을 때까지 기본 자격 증명을 보내지 않습니다 .

이를 우회하기 위해 항상 기본 인증 헤더를 수동으로 생성하고 보낼 수 있습니다.

HttpRequest request = HttpRequest.newBuilder()
  .uri(URI.create(serviceUrl))
  .POST(HttpRequest.BodyPublishers.noBody())
  .header("Authorization", "Basic " + 
    Base64.getEncoder().encodeToString(("baeldung:123456").getBytes()))
  .build();

5. 바디 추가

지금까지의 예에서는 POST 요청에 본문을 추가하지 않았습니다. 그러나 POST 메서드는 일반적으로 요청 본문을 통해 서버에 데이터를 보내는 데 사용됩니다 .

5.1. JSON 본문

BodyPublishers 유틸리티 는 문자열 또는 파일 에서 요청 본문을 게시하는 것과 같은 다양한 유용한 게시자를 구현 합니다. UTF-8 문자 집합을 사용하여 변환된 JSON 데이터를  String 으로 게시 할 수 있습니다 .

HttpRequest request = HttpRequest.newBuilder()
  .uri(URI.create(serviceUrl))
  .POST(HttpRequest.BodyPublishers.ofString("{\"action\":\"hello\"}"))
  .build();

5.2. 파일 업로드

HttpClient 를 통해 업로드하는 데 사용할 수 있는 임시 파일 을 만들어 보겠습니다 .

Path file = tempDir.resolve("temp.txt");
List<String> lines = Arrays.asList("1", "2", "3");
Files.write(file, lines);

HttpClient 는 POST 본문에 파일을 추가하기 위한 별도의 메서드인 BodyPublishers.ofFile을 제공합니다 . 임시 파일을 메소드 매개변수로 추가하기만 하면 API가 나머지를 처리합니다.

HttpRequest request = HttpRequest.newBuilder()
  .uri(URI.create(serviceUrl))
  .POST(HttpRequest.BodyPublishers.ofFile(file))
  .build();

5.3. 양식 제출

파일과 달리 HttpClient 는 양식 데이터를 게시하기 위한 별도의 방법을 제공하지 않습니다. 따라서 다시 BodyPublishers.ofString 메서드 를 사용해야 합니다 .

Map<String, String> formData = new HashMap<>();
formData.put("username", "baeldung");
formData.put("message", "hello");

HttpRequest request = HttpRequest.newBuilder()
  .uri(URI.create(serviceUrl))
  .POST(HttpRequest.BodyPublishers.ofString(getFormDataAsString(formData)))
  .build();

그러나 사용자 지정 구현을 사용하여 양식 데이터를 Map 에서 String 으로 변환해야 합니다.

private static String getFormDataAsString(Map<String, String> formData) {
    StringBuilder formBodyBuilder = new StringBuilder();
    for (Map.Entry<String, String> singleEntry : formData.entrySet()) {
        if (formBodyBuilder.length() > 0) {
            formBodyBuilder.append("&");
        }
        formBodyBuilder.append(URLEncoder.encode(singleEntry.getKey(), StandardCharsets.UTF_8));
        formBodyBuilder.append("=");
        formBodyBuilder.append(URLEncoder.encode(singleEntry.getValue(), StandardCharsets.UTF_8));
    }
    return formBodyBuilder.toString();
}

6. 결론

이 기사에서는 Java 11에 도입된 Java HttpClient API를 사용하여 POST 요청을 전송하는 방법 살펴보았습니다 .

HttpClient 인스턴스 를 만들고 POST 요청을 준비하는 방법을 배웠습니다 . 준비된 요청을 동기식, 비동기식 및 동시에 보내는 방법을 살펴보았습니다. 다음으로 기본 인증 매개변수를 추가하는 방법도 살펴보았습니다.

마지막으로 POST 요청에 본문을 추가하는 방법을 살펴보았습니다. JSON 페이로드, 파일 업로드 및 양식 데이터 제출을 다루었습니다.

항상 그렇듯이 전체 소스 코드는 GitHub에서 사용할 수 있습니다 .

Generic footer banner