麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 開發 > Java > 正文

Java11新特性之HttpClient小試牛刀

2024-07-14 08:42:25
字體:
來源:轉載
供稿:網友

本文主要研究一下Java11的HttpClient的基本使用。

變化

  1. 從java9的jdk.incubator.httpclient模塊遷移到java.net.http模塊,包名由jdk.incubator.http改為java.net.http
  2. 原來的諸如HttpResponse.BodyHandler.asString()方法變更為HttpResponse.BodyHandlers.ofString(),變化一為BodyHandler改為BodyHandlers,變化二為asXXX()之類的方法改為ofXXX(),由as改為of

實例

設置超時時間

 @Test public void testTimeout() throws IOException, InterruptedException {  //1.set connect timeout  HttpClient client = HttpClient.newBuilder()    .connectTimeout(Duration.ofMillis(5000))    .followRedirects(HttpClient.Redirect.NORMAL)    .build();  //2.set read timeout  HttpRequest request = HttpRequest.newBuilder()    .uri(URI.create("http://openjdk.java.net/"))    .timeout(Duration.ofMillis(5009))    .build();  HttpResponse<String> response =    client.send(request, HttpResponse.BodyHandlers.ofString());  System.out.println(response.body()); }

HttpConnectTimeoutException實例

Caused by: java.net.http.HttpConnectTimeoutException: HTTP connect timed out at java.net.http/jdk.internal.net.http.ResponseTimerEvent.handle(ResponseTimerEvent.java:68) at java.net.http/jdk.internal.net.http.HttpClientImpl.purgeTimeoutsAndReturnNextDeadline(HttpClientImpl.java:1248) at java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.run(HttpClientImpl.java:877)Caused by: java.net.ConnectException: HTTP connect timed out at java.net.http/jdk.internal.net.http.ResponseTimerEvent.handle(ResponseTimerEvent.java:69) ... 2 more

HttpTimeoutException實例

java.net.http.HttpTimeoutException: request timed out at java.net.http/jdk.internal.net.http.HttpClientImpl.send(HttpClientImpl.java:559) at java.net.http/jdk.internal.net.http.HttpClientFacade.send(HttpClientFacade.java:119) at com.example.HttpClientTest.testTimeout(HttpClientTest.java:40)

設置authenticator

 @Test public void testBasicAuth() throws IOException, InterruptedException {  HttpClient client = HttpClient.newBuilder()    .connectTimeout(Duration.ofMillis(5000))    .authenticator(new Authenticator() {     @Override     protected PasswordAuthentication getPasswordAuthentication() {      return new PasswordAuthentication("admin","password".toCharArray());     }    })    .build();  HttpRequest request = HttpRequest.newBuilder()    .uri(URI.create("http://localhost:8080/json/info"))    .timeout(Duration.ofMillis(5009))    .build();  HttpResponse<String> response =    client.send(request, HttpResponse.BodyHandlers.ofString());  System.out.println(response.statusCode());  System.out.println(response.body()); }
  1. authenticator可以用來設置HTTP authentication,比如Basic authentication
  2. 雖然Basic authentication也可以自己設置header,不過通過authenticator省得自己去構造header

設置header

 @Test public void testCookies() throws IOException, InterruptedException {  HttpClient client = HttpClient.newBuilder()    .connectTimeout(Duration.ofMillis(5000))    .build();  HttpRequest request = HttpRequest.newBuilder()    .uri(URI.create("http://localhost:8080/json/cookie"))    .header("Cookie","JSESSIONID=4f994730-32d7-4e22-a18b-25667ddeb636; userId=java11")    .timeout(Duration.ofMillis(5009))    .build();  HttpResponse<String> response =    client.send(request, HttpResponse.BodyHandlers.ofString());  System.out.println(response.statusCode());  System.out.println(response.body()); }

通過request可以自己設置header

GET

同步

 @Test public void testSyncGet() throws IOException, InterruptedException {  HttpClient client = HttpClient.newHttpClient();  HttpRequest request = HttpRequest.newBuilder()    .uri(URI.create("https://www.baidu.com"))    .build();  HttpResponse<String> response =    client.send(request, HttpResponse.BodyHandlers.ofString());  System.out.println(response.body()); }

異步

 @Test public void testAsyncGet() throws ExecutionException, InterruptedException {  HttpClient client = HttpClient.newHttpClient();  HttpRequest request = HttpRequest.newBuilder()    .uri(URI.create("https://www.baidu.com"))    .build();  CompletableFuture<String> result = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())    .thenApply(HttpResponse::body);  System.out.println(result.get()); }

POST表單

 @Test public void testPostForm() throws IOException, InterruptedException {  HttpClient client = HttpClient.newBuilder().build();  HttpRequest request = HttpRequest.newBuilder()    .uri(URI.create("http://www.w3school.com.cn/demo/demo_form.asp"))    .header("Content-Type","application/x-www-form-urlencoded")    .POST(HttpRequest.BodyPublishers.ofString("name1=value1&name2=value2"))    .build();  HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());  System.out.println(response.statusCode()); }

header指定內容是表單類型,然后通過BodyPublishers.ofString傳遞表單數據,需要自己構建表單參數

POST JSON

 @Test public void testPostJsonGetJson() throws ExecutionException, InterruptedException, JsonProcessingException {  ObjectMapper objectMapper = new ObjectMapper();  StockDto dto = new StockDto();  dto.setName("hj");  dto.setSymbol("hj");  dto.setType(StockDto.StockType.SH);  String requestBody = objectMapper    .writerWithDefaultPrettyPrinter()    .writeValueAsString(dto);  HttpRequest request = HttpRequest.newBuilder(URI.create("http://localhost:8080/json/demo"))    .header("Content-Type", "application/json")    .POST(HttpRequest.BodyPublishers.ofString(requestBody))    .build();  CompletableFuture<StockDto> result = HttpClient.newHttpClient()    .sendAsync(request, HttpResponse.BodyHandlers.ofString())    .thenApply(HttpResponse::body)    .thenApply(body -> {     try {      return objectMapper.readValue(body,StockDto.class);     } catch (IOException e) {      return new StockDto();     }    });  System.out.println(result.get()); }

post json的話,body自己json化為string,然后header指定是json格式

文件上傳

 @Test public void testUploadFile() throws IOException, InterruptedException, URISyntaxException {  HttpClient client = HttpClient.newHttpClient();  Path path = Path.of(getClass().getClassLoader().getResource("body.txt").toURI());  File file = path.toFile();  String multipartFormDataBoundary = "Java11HttpClientFormBoundary";  org.apache.http.HttpEntity multipartEntity = MultipartEntityBuilder.create()    .addPart("file", new FileBody(file, ContentType.DEFAULT_BINARY))    .setBoundary(multipartFormDataBoundary) //要設置,否則阻塞    .build();  HttpRequest request = HttpRequest.newBuilder()    .uri(URI.create("http://localhost:8080/file/upload"))    .header("Content-Type", "multipart/form-data; boundary=" + multipartFormDataBoundary)    .POST(HttpRequest.BodyPublishers.ofInputStream(() -> {     try {      return multipartEntity.getContent();     } catch (IOException e) {      e.printStackTrace();      throw new RuntimeException(e);     }    }))    .build();  HttpResponse<String> response =    client.send(request, HttpResponse.BodyHandlers.ofString());  System.out.println(response.body()); }
  1. 官方的HttpClient并沒有提供類似WebClient那種現成的BodyInserters.fromMultipartData方法,因此這里需要自己轉換
  2. 這里使用org.apache.httpcomponents(httpclient及httpmime)的MultipartEntityBuilder構建multipartEntity,最后通過HttpRequest.BodyPublishers.ofInputStream來傳遞內容
  3. 這里header要指定Content-Type值為multipart/form-data以及boundary的值,否則服務端可能無法解析

文件下載

 @Test public void testAsyncDownload() throws ExecutionException, InterruptedException {  HttpClient client = HttpClient.newHttpClient();  HttpRequest request = HttpRequest.newBuilder()    .uri(URI.create("http://localhost:8080/file/download"))    .build();  CompletableFuture<Path> result = client.sendAsync(request, HttpResponse.BodyHandlers.ofFile(Paths.get("/tmp/body.txt")))    .thenApply(HttpResponse::body);  System.out.println(result.get()); }

使用HttpResponse.BodyHandlers.ofFile來接收文件

并發請求

 @Test public void testConcurrentRequests(){  HttpClient client = HttpClient.newHttpClient();  List<String> urls = List.of("http://www.baidu.com","http://www.alibaba.com/","http://www.tencent.com");  List<HttpRequest> requests = urls.stream()    .map(url -> HttpRequest.newBuilder(URI.create(url)))    .map(reqBuilder -> reqBuilder.build())    .collect(Collectors.toList());  List<CompletableFuture<HttpResponse<String>>> futures = requests.stream()    .map(request -> client.sendAsync(request, HttpResponse.BodyHandlers.ofString()))    .collect(Collectors.toList());  futures.stream()    .forEach(e -> e.whenComplete((resp,err) -> {     if(err != null){      err.printStackTrace();     }else{      System.out.println(resp.body());      System.out.println(resp.statusCode());     }    }));  CompletableFuture.allOf(futures    .toArray(CompletableFuture<?>[]::new))    .join(); }
  • sendAsync方法返回的是CompletableFuture,可以方便地進行轉換、組合等操作
  • 這里使用CompletableFuture.allOf組合在一起,最后調用join等待所有future完成

錯誤處理

 @Test public void testHandleException() throws ExecutionException, InterruptedException {  HttpClient client = HttpClient.newBuilder()    .connectTimeout(Duration.ofMillis(5000))    .build();  HttpRequest request = HttpRequest.newBuilder()    .uri(URI.create("https://twitter.com"))    .build();  CompletableFuture<String> result = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())//    .whenComplete((resp,err) -> {//     if(err != null){//      err.printStackTrace();//     }else{//      System.out.println(resp.body());//      System.out.println(resp.statusCode());//     }//    })    .thenApply(HttpResponse::body)    .exceptionally(err -> {     err.printStackTrace();     return "fallback";    });  System.out.println(result.get()); }
  • HttpClient異步請求返回的是CompletableFuture<HttpResponse<T>>,其自帶exceptionally方法可以用來做fallback處理
  • 另外值得注意的是HttpClient不像WebClient那樣,它沒有對4xx或5xx的狀態碼拋出異常,需要自己根據情況來處理,手動檢測狀態碼拋出異常或者返回其他內容

HTTP2

 @Test public void testHttp2() throws URISyntaxException {  HttpClient.newBuilder()    .followRedirects(HttpClient.Redirect.NEVER)    .version(HttpClient.Version.HTTP_2)    .build()    .sendAsync(HttpRequest.newBuilder()        .uri(new URI("https://http2.akamai.com/demo"))        .GET()        .build(),      HttpResponse.BodyHandlers.ofString())    .whenComplete((resp,t) -> {     if(t != null){      t.printStackTrace();     }else{      System.out.println(resp.version());      System.out.println(resp.statusCode());     }    }).join(); }

執行之后可以看到返回的response的version為HTTP_2

WebSocket

 @Test public void testWebSocket() throws InterruptedException {  HttpClient client = HttpClient.newHttpClient();  WebSocket webSocket = client.newWebSocketBuilder()    .buildAsync(URI.create("ws://localhost:8080/echo"), new WebSocket.Listener() {     @Override     public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {      // request one more      webSocket.request(1);      // Print the message when it's available      return CompletableFuture.completedFuture(data)        .thenAccept(System.out::println);     }    }).join();  webSocket.sendText("hello ", false);  webSocket.sendText("world ",true);  TimeUnit.SECONDS.sleep(10);  webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "ok").join(); }
  • HttpClient支持HTTP2,也包含了WebSocket,通過newWebSocketBuilder去構造WebSocket
  • 傳入listener進行接收消息,要發消息的話,使用WebSocket來發送,關閉使用sendClose方法

reactive streams

HttpClient本身就是reactive的,支持reactive streams,這里舉ResponseSubscribers.ByteArraySubscriber的源碼看看:
java.net.http/jdk/internal/net/http/ResponseSubscribers.java

public static class ByteArraySubscriber<T> implements BodySubscriber<T> {  private final Function<byte[], T> finisher;  private final CompletableFuture<T> result = new MinimalFuture<>();  private final List<ByteBuffer> received = new ArrayList<>();  private volatile Flow.Subscription subscription;  public ByteArraySubscriber(Function<byte[],T> finisher) {   this.finisher = finisher;  }  @Override  public void onSubscribe(Flow.Subscription subscription) {   if (this.subscription != null) {    subscription.cancel();    return;   }   this.subscription = subscription;   // We can handle whatever you've got   subscription.request(Long.MAX_VALUE);  }  @Override  public void onNext(List<ByteBuffer> items) {   // incoming buffers are allocated by http client internally,   // and won't be used anywhere except this place.   // So it's free simply to store them for further processing.   assert Utils.hasRemaining(items);   received.addAll(items);  }  @Override  public void onError(Throwable throwable) {   received.clear();   result.completeExceptionally(throwable);  }  static private byte[] join(List<ByteBuffer> bytes) {   int size = Utils.remaining(bytes, Integer.MAX_VALUE);   byte[] res = new byte[size];   int from = 0;   for (ByteBuffer b : bytes) {    int l = b.remaining();    b.get(res, from, l);    from += l;   }   return res;  }  @Override  public void onComplete() {   try {    result.complete(finisher.apply(join(received)));    received.clear();   } catch (IllegalArgumentException e) {    result.completeExceptionally(e);   }  }  @Override  public CompletionStage<T> getBody() {   return result;  } }
  1. BodySubscriber接口繼承了Flow.Subscriber<List<ByteBuffer>>接口
  2. 這里的Subscription來自Flow類,該類是java9引入的,里頭包含了支持Reactive Streams的實現

小結

HttpClient在Java11從incubator變為正式版,相對于傳統的HttpUrlConnection其提升可不是一點半點,不僅支持異步,也支持reactive streams,同時也支持了HTTP2以及WebSocket,非常值得大家使用。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: av在线播放观看 | 久久国产精品久久久久久久久久 | 99国语露脸久久精品国产ktv | 亚洲精品一区中文字幕 | 女教师~淫辱の动漫在线 | 九九热精| 国产成人高清成人av片在线看 | 久久久精品视频国产 | 日本成年免费网站 | 97风流梦电影| 国产激情网 | 亚洲精中文字幕二区三区 | 精品在线观看一区二区三区 | 久久精品艹 | 日本精品视频一区二区三区四区 | 一区二区三区四区高清视频 | 国产亚洲精品美女久久久 | a级毛片免费观看在线播放 日本aaa一级片 | 热re91久久精品国产99热 | 精品一区二区三区日本 | jizzjizz中国人少妇中文 | 成人毛片100免费观看 | 国产高潮失禁喷水爽到抽搐视频 | 国产一级毛片视频在线! | 日韩精品中文字幕一区 | 国产精品伦视频看免费三 | v天堂在线视频 | 视频一区二区不卡 | 手机在线看片国产 | 免费三级大片 | asiass极品裸体女pics | 国产一区精品在线观看 | 久久国产免费视频 | 亚洲一区 国产精品 | 深夜小视频在线观看 | 国产99精品视频 | 2019亚洲日韩新视频 | 久久国产精品久久精品国产演员表 | 激情九九| 日日噜噜噜噜久久久精品毛片 | 97伦理|