<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          直擊痛點的一款 HTTP 客戶端框架!

          共 15510字,需瀏覽 32分鐘

           ·

          2021-06-03 02:09

          大家好,我是二哥呀!

          今天來給大家推薦一款直擊痛點的 HTTP 客戶端框架,可以超高效率地完成和第三方接口的對接。

          在介紹本篇的主角之前,我們先來了解下 Java 生態(tài)中的 HTTP 組件庫,大致可以分為三類:

          • JDK 自帶的 HttpURLConnection 標(biāo)準(zhǔn)庫;
          • Apache HttpComponents HttpClient;
          • OkHttp。

          使用 HttpURLConnection 發(fā)起 HTTP 請求最大的優(yōu)點是不需要引入額外的依賴,但是使用起來非常繁瑣,也缺乏連接池管理、域名機械控制等特性支持。

          使用標(biāo)準(zhǔn)庫的最大好處就是不需要引入額外的依賴,但使用起來比較繁瑣,就像直接使用 JDBC 連接數(shù)據(jù)庫那樣,需要很多模板代碼。來發(fā)起一個簡單的 HTTP POST 請求吧。

          public class HttpUrlConnectionDemo {
              public static void main(String[] args) throws IOException {
                  String urlString = "https://httpbin.org/post";
                  String bodyString = "password=123";

                  URL url = new URL(urlString);
                  HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                  conn.setRequestMethod("POST");
                  conn.setDoOutput(true);

                  OutputStream os = conn.getOutputStream();
                  os.write(bodyString.getBytes("utf-8"));
                  os.flush();
                  os.close();

                  if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
                      InputStream is = conn.getInputStream();
                      BufferedReader reader = new BufferedReader(new InputStreamReader(is));
                      StringBuilder sb = new StringBuilder();
                      String line;
                      while ((line = reader.readLine()) != null) {
                          sb.append(line);
                      }
                      System.out.println("響應(yīng)內(nèi)容:" + sb.toString());
                  } else {
                      System.out.println("響應(yīng)碼:" + conn.getResponseCode());
                  }
              }
          }

          HttpURLConnection 發(fā)起的 HTTP 請求比較原始,基本上算是對網(wǎng)絡(luò)傳輸層的一次淺層次的封裝;有了 HttpURLConnection 對象后,就可以獲取到輸出流,然后把要發(fā)送的內(nèi)容發(fā)送出去;再通過輸入流讀取到服務(wù)器端響應(yīng)的內(nèi)容;最后打印。

          不過 HttpURLConnection 不支持 HTTP/2.0,為了解決這個問題,Java 9 的時候官方的標(biāo)準(zhǔn)庫增加了一個更高級別的 HttpClient,再發(fā)起 POST 請求就顯得高大上多了,不僅支持異步,還支持順滑的鏈?zhǔn)秸{(diào)用。

          public class HttpClientDemo {
              public static void main(String[] args) throws URISyntaxException {
                  HttpClient client = HttpClient.newHttpClient();
                  HttpRequest request = HttpRequest.newBuilder()
                          .uri(new URI("https://postman-echo.com/post"))
                          .headers("Content-Type""text/plain;charset=UTF-8")
                          .POST(HttpRequest.BodyPublishers.ofString("二哥牛逼"))
                          .build();
                  client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
                          .thenApply(HttpResponse::body)
                          .thenAccept(System.out::println)
                          .join();
              }
          }

          Apache HttpComponents HttpClient 支持的特性也非常豐富:

          • 基于標(biāo)準(zhǔn)、純凈的Java語言,實現(xiàn)了HTTP1.0和HTTP1.1;
          • 以可擴展的面向?qū)ο蟮慕Y(jié)構(gòu)實現(xiàn)了HTTP全部的方法;
          • 支持加密的HTTPS協(xié)議(HTTP通過SSL協(xié)議);
          • Request的輸出流可以避免流中內(nèi)容體直接從socket緩沖到服務(wù)器;
          • Response的輸入流可以有效的從socket服務(wù)器直接讀取相應(yīng)內(nèi)容。
          public class HttpComponentsDemo {
              public static void main(String[] args) throws IOException, IOException, ParseException {
                  try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
                      HttpPost httpPost = new HttpPost("http://httpbin.org/post");
                      List<NameValuePair> nvps = new ArrayList<>();
                      nvps.add(new BasicNameValuePair("name""二哥"));
                      httpPost.setEntity(new UrlEncodedFormEntity(nvps, Charset.forName("UTF-8")));

                      try (CloseableHttpResponse response2 = httpclient.execute(httpPost)) {
                          System.out.println(response2.getCode() + " " + EntityUtils.toString(response2.getEntity()));
                      }
                  }
              }
          }

          OkHttp 是一個執(zhí)行效率比較高的 HTTP 客戶端:

          • 支持 HTTP/2.0,當(dāng)多個請求對應(yīng)同一個 Host 地址時,可共用同一個 Socket;
          • 連接池可減少請求延遲;
          • 支持 GZIP 壓縮,減少網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)大小;
          • 支持 Response 數(shù)據(jù)緩存,避免重復(fù)網(wǎng)絡(luò)請求;
          public class OkHttpPostDemo {
              public static final MediaType JSON = MediaType.get("application/json; charset=utf-8");

              OkHttpClient client = new OkHttpClient();

              String post(String url, String json) throws IOException {
                  RequestBody body = RequestBody.create(json, JSON);
                  Request request = new Request.Builder()
                          .url(url)
                          .post(body)
                          .build();
                  try (Response response = client.newCall(request).execute()) {
                      return response.body().string();
                  }
              }

              public static void main(String[] args) throws IOException {
                  OkHttpPostDemo example = new OkHttpPostDemo();
                  String json = "{'name':'二哥'}";
                  String response = example.post("https://httpbin.org/post", json);
                  System.out.println(response);
              }
          }

          那今天介紹的這款輕量級的 HTTP 客戶端框架 Forest,正是基于 Httpclient和OkHttp 的,屏蔽了不同細節(jié)的 HTTP 組件庫所帶來的所有差異。

          Forest 的字面意思是森林的意思,更內(nèi)涵點的話,可以拆成For和Rest兩個單詞,也就是“為了Rest”(Rest為一種基于HTTP的架構(gòu)風(fēng)格)。而合起來就是森林,森林由很多樹木花草組成(可以理解為各種不同的服務(wù)),它們表面上看獨立,實則在地下根莖交錯縱橫、相互連接依存,這樣看就有點現(xiàn)代分布式服務(wù)化的味道了。最后,這兩個單詞反過來讀就像是Resultful。

          項目地址:

          https://gitee.com/dromara/forest

          雖然 star 數(shù)還不是很多,但 star 趨勢圖正在趨于爬坡階段,大家可以拿來作為一個練手項目,我覺得還是不錯的選擇

          Forest 本身是處理前端過程的框架,是對后端 HTTP API 框架的進一步封裝。

          前端部分:

          • 通過RPC方式去發(fā)送HTTP請求, 方便解耦
          • 支持GET, HEAD, POST等所有請求方法
          • 支持Spring和Springboot集成
          • JSON字符串到Java對象的自動化解析
          • XML文本到Java對象的自動化解析
          • 支持靈活的模板表達式
          • 支持攔截器處理請求的各個生命周期
          • 支持自定義注解

          后端部分:

          • 支持OkHttp
          • 支持Httpclient

          Forest 容易上手,不需要調(diào)用HTTP底層接口,而是像 Dubbo 那樣的 RPC 框架一樣,只需要定義接口、調(diào)用接口即可。幾分鐘內(nèi)就可完成請求的定義、發(fā)送、接收響應(yīng)、數(shù)據(jù)解析、錯誤處理、日志打印等過程。

          配置輕量,遵循約定優(yōu)于配置的原則,只需在需要的時候進行配置,不配置也不會影響Forest請求的正常調(diào)用。

          簡單優(yōu)雅,將 HTTP 請求細節(jié)封裝成 Java 接口 + 注解的形式,不必再關(guān)心發(fā)送 HTTP 請求的具體過程。使得 HTTP 請求信息與業(yè)務(wù)代碼解耦,方便管理大量 HTTP 的 URL、Header、Body 等信息。

          擴展靈活,允許自定義攔截器、甚至是自定義注解,以此來擴展Forest的能力。

          Forest 不需要我們編寫具體的 HTTP 調(diào)用過程,只需要定義一個接口,然后通過 Forest 注解將 HTTP 請求的信息添加到接口的方法上即可。請求發(fā)送方通過調(diào)用定義的接口就能自動發(fā)送請求和接受請求的響應(yīng)。

          Forest 之所以能做到這樣,是因為它將定義好的接口通過動態(tài)代理的方式生成了一個具體的實現(xiàn)類,然后組織、驗證 HTTP 請求信息,綁定動態(tài)數(shù)據(jù),轉(zhuǎn)換數(shù)據(jù)形式,SSL 驗證簽名,調(diào)用后端 HTTP API執(zhí)行實際請求,等待響應(yīng),失敗重試,轉(zhuǎn)換響應(yīng)數(shù)據(jù)到 Java 類型等臟活累活都由這動態(tài)代理的實現(xiàn)類給包了。

          廢話就不再多說,直接開始實戰(zhàn)。

          第一步,添加  Maven 依賴。

          <dependency>
              <groupId>com.dtflys.forest</groupId>
              <artifactId>forest-core</artifactId>
              <version>1.5.1</version>
          </dependency>

          第二步,構(gòu)建 HTTP 請求。

          在 Forest 中,所有的 HTTP 請求信息都要綁定到某一個接口的方法上,不需要編寫具體的代碼去發(fā)送請求。請求發(fā)送方通過調(diào)用事先定義好 HTTP 請求信息的接口方法。

          public interface ForRestClient {
              @Request(
                      url = "http://httpbin.org/post",
                      type = "POST"
              )
              String simplePost(@Body("name") String name);
          }

          通過 @Post 注解,將上面的ForRestClient接口中的 simplePost() 方法綁定了一個 HTTP 請求,使用 POST 方式,可以使用@Body注解修飾參數(shù)的方式,將傳入?yún)?shù)的數(shù)據(jù)綁定到 HTTP 請求體中。然后將請求響應(yīng)的數(shù)據(jù)以String的方式返回給調(diào)用者。

          第三步,調(diào)用接口。

          public class ForRestDemo {
              public static void main(String[] args) {
                  // 實例化Forest配置對象
                  ForestConfiguration configuration = ForestConfiguration.configuration();
                  configuration.setBackendName("httpclient");

                  // 通過Forest配置對象實例化Forest請求接口
                  ForRestClient myClient = configuration.createInstance(ForRestClient.class);

                  // 調(diào)用Forest請求接口,并獲取響應(yīng)返回結(jié)果
                  String result = myClient.simplePost("二哥");
                  System.out.println(result);
              }
          }

          ForestConfiguration為 Forest 的全局配置對象類,所有的 Forest 的全局基本配置信息由此類進行管理。

          可以來看一下運行后的日志信息:

          {
            "args": {}, 
            "data"""
            "files": {}, 
            "form": {
              "name""\u4e8c\u54e5"
            }, 
            "headers": {
              "Content-Length""23"
              "Content-Type""application/x-www-form-urlencoded"
              "Host""httpbin.org"
              "User-Agent""Apache-HttpClient/4.5.2 (Java/11.0.8)"
              "X-Amzn-Trace-Id""Root=1-60b533aa-58b41e4967803d99593c53a0"
            }, 
            "json"null
            "origin""161.81.21.32"
            "url""http://httpbin.org/post"
          }

          此時,一個簡單的 Forest 上手小栗子就跑通了。

          如果是 Spring Boot 項目的話,就不需要 ForestConfiguration 了,只需要在啟動類或者配置類上添加 @ForestScan 注解就可以了。

          @SpringBootApplication
          @Configuration
          @ForestScan(basePackages = "com.yoursite.client")
          public class MyApp {
           ...
          }

          Forest 除了支持GET和POST,也支持其他幾種 HTTP 請求方式,比如PUT、HEAD、 OPTIONS、DELETE。只需要在構(gòu)建接口的時候使用對應(yīng)的注解就可以了,比如說 PUT:

          // PUT請求
          @Put("http://localhost:8080/hello")
          String simplePut();

          在POST和PUT請求方法中,通常使用 HTTP 請求體進行數(shù)據(jù)傳輸,在 Forest 中,可以使用 @Body@JSONBody@XMLBody 等多種方式設(shè)置請求體數(shù)據(jù)。

          /**
           * 直接修飾一個JSON字符串
           */

          @Post("http://localhost:8080/hello/user")
          String helloUser(@JSONBody String userJson);

          Forest 請求會自動將響應(yīng)的返回數(shù)據(jù)反序列化成對應(yīng)的數(shù)據(jù)類型,分兩步走。

          第一步:定義dataType屬性

          dataType屬性指定了該請求響應(yīng)返回的數(shù)據(jù)類型,可選的數(shù)據(jù)類型有三種: text, json, xml,默認為 text。

          /**
           * dataType為json或xml時,F(xiàn)orest會進行相應(yīng)的反序列化
           */

          @Request(
              url = "http://localhost:8080/text/data",
              dataType = "json"
          )
          Map getData();

          第二步:指定反序列化的目標(biāo)類型

          反序列化需要一個目標(biāo)類型,而該類型其實就是方法的返回值類型,如返回值為String就會反序列成String字符串,返回值為Map就會反序列化成一個HashMap對象,也可以指定為自定義的Class類型。

          如果有這樣一個 User 類:

          public class User {
              private String username;
              private String score;
              
              // Setter和Getter ...
          }

          返回的數(shù)據(jù)為 JSON 字符串:

          {"username":  "Foo""score":  "82"}

          那請求接口就應(yīng)該定義成這樣:

          /**
           * dataType屬性指明了返回的數(shù)據(jù)類型為JSON
           */

          @Get(
              url = "http://localhost:8080/user?id=${0}",
              dataType = "json"
          )
          User getUser(Integer id)

          另外,大家需要了解一下 Gzip,它是現(xiàn)在一種流行的文件壓縮算法,有相當(dāng)廣泛的應(yīng)用范圍。尤其是當(dāng)Gzip用來壓縮存文本文件的時候效果尤為明顯,大概能減少70%以上的文件大小。很多 HTTP 服務(wù)器都支持 Gzip,比如 Tomcat,經(jīng)過這些服務(wù)壓縮過的數(shù)據(jù)可以降低網(wǎng)絡(luò)傳輸?shù)牧髁浚岣呖蛻舳说捻憫?yīng)速度。

          Forest從1.5.2-BETA版本開始支持Gzip的解壓,其解壓的方式也很簡單,在方法或接口類上加上 @DecompressGzip 注解即可。

          /**
           * 為請求方法添加Gzip解壓能力
           */

          @Get("/transaction")
          @DecompressGzip
          String transaction(String infno);

          更重要的一點是,F(xiàn)orest 可以通過設(shè)置@Request注解的async屬性為true來實現(xiàn)異步請求。

          @Request(
                  url = "http://localhost:8080/hello/user?username=${0}",
                  async = true,
                  headers = {"Accept:text/plain"}
          )
          void asyncGet(String username, OnSuccess<String> onSuccess);

          異步請求時,通過 OnSuccess<T> 回調(diào)函數(shù)來接受響應(yīng)數(shù)據(jù),而不是通過接口方法的返回值,所以這里的返回值類型一般會定義為void。

          調(diào)用該接口方法時,可以通過下面的方式:

          myClient.send("foo", (String resText, ForestRequest request, ForestResponse response) -> {
                  // 成功響應(yīng)回調(diào)
                  System.out.println(resText);    
              },
              (ForestRuntimeException ex, ForestRequest request, ForestResponse response) -> {
                  // 異常回調(diào)
                  System.out.println(ex.getMessage());
              });

          除了上面提到的這些功能,F(xiàn)orset 還支持更高級的用法:

          • HTTPS
          • 文件上傳下載
          • 攔截器
          • 使用代理
          • 自定義注解

          大家可以去看一下 Forset 的官方文檔,然后在本地實踐一下,還是能學(xué)到不少知識的,尤其是 HTTPS 和文件上傳下載這塊,只需要簡單的配置就能完成,我個人感覺還是挺值得去學(xué)習(xí)和借鑒的。

          開源精神難能可貴,好的開源需要大家的添磚加瓦和支持。希望這篇文章能給大家在選擇 HTTP 客戶端框架時帶來一個新的選擇,對,就是 Forest。

          這篇文章不僅介紹了 Forest 這個輕量級的 HTTP 客戶端框架,還回顧了它的底層實現(xiàn):HttpClient 和 OkHttp,希望能對大家有所幫助。

          PS:今天是六一兒童節(jié),也是六月份的第一天,記得給自己買個小神童吃哈~~~~

          我是二哥呀,我們下期見~

          瀏覽 74
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  婷婷激情五月天丁香 | 欧美高清猛交xxx黑人猛交性乱 | 国内一级免费黄色视频在线网展览器的封 | 亚洲图片小说区 | 男人的天堂成人网站 |