当前位置 博文首页 > 沉默王二:Duang!Duang!Duang!直击痛点的一款 HTTP 客户端框

    沉默王二:Duang!Duang!Duang!直击痛点的一款 HTTP 客户端框

    作者:[db:作者] 时间:2021-07-07 22:07

    大家好,我是二哥呀!

    今天来给大家推荐一款直击痛点的 HTTP 客户端框架,可以超高效率地完成和第三方接口的对接。

    在介绍本篇的主角之前,我们先来了解下 Java 生态中的 HTTP 组件库,大致可以分为三类:

    • JDK 自带的 HttpURLConnection 标准库;
    • Apache HttpComponents HttpClient;
    • OkHttp。

    使用 HttpURLConnection 发起 HTTP 请求最大的优点是不需要引入额外的依赖,但是使用起来非常繁琐,也缺乏连接池管理、域名机械控制等特性支持。

    使用标准库的最大好处就是不需要引入额外的依赖,但使用起来比较繁琐,就像直接使用 JDBC 连接数据库那样,需要很多模板代码。来发起一个简单的 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("响应内容:" + sb.toString());
            } else {
                System.out.println("响应码:" + conn.getResponseCode());
            }
        }
    }
    

    HttpURLConnection 发起的 HTTP 请求比较原始,基本上算是对网络传输层的一次浅层次的封装;有了 HttpURLConnection 对象后,就可以获取到输出流,然后把要发送的内容发送出去;再通过输入流读取到服务器端响应的内容;最后打印。

    不过 HttpURLConnection 不支持 HTTP/2.0,为了解决这个问题,Java 9 的时候官方的标准库增加了一个更高级别的 HttpClient,再发起 POST 请求就显得高大上多了,不仅支持异步,还支持顺滑的链式调用。

    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 支持的特性也非常丰富:

    • 基于标准、纯净的Java语言,实现了HTTP1.0和HTTP1.1;
    • 以可扩展的面向对象的结构实现了HTTP全部的方法;
    • 支持加密的HTTPS协议(HTTP通过SSL协议);
    • Request的输出流可以避免流中内容体直接从socket缓冲到服务器;
    • Response的输入流可以有效的从socket服务器直接读取相应内容。
    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 是一个执行效率比较高的 HTTP 客户端:

    • 支持 HTTP/2.0,当多个请求对应同一个 Host 地址时,可共用同一个 Socket;
    • 连接池可减少请求延迟;
    • 支持 GZIP 压缩,减少网络传输的数据大小;
    • 支持 Response 数据缓存,避免重复网络请求;
    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 的,屏蔽了不同细节的 HTTP 组件库所带来的所有差异。

    Forest 的字面意思是森林的意思,更内涵点的话,可以拆成For和Rest两个单词,也就是“为了Rest”(Rest为一种基于HTTP的架构风格)。 而合起来就是森林,森林由很多树木花草组成(可以理解为各种不同的服务),它们表面上看独立,实则在地下根茎交错纵横、相互连接依存,这样看就有点现代分布式服务化的味道了。 最后,这两个单词反过来读就像是Resultful。

    项目地址:

    https://gitee.com/dromara/forest

    虽然 star 数还不是很多,但 star 趋势图正在趋于爬坡阶段,大家可以拿来作为一个练手项目,我觉得还是不错的选择

    Forest 本身是处理前端过程的框架,是对后端 HTTP API 框架的进一步封装。

    前端部分:

    • 通过RPC方式去发送HTTP请求, 方便解耦
    • 支持GET, HEAD, POST等所有请求方法
    • 支持Spring和Springboot集成
    • JSON字符串到Java对象的自动化解析
    • XML文本到Java对象的自动化解析
    • 支持灵活的模板表达式
    • 支持拦截器处理请求的各个生命周期
    • 支持自定义注解

    后端部分:

    • 支持OkHttp
    • 支持Httpclient

    Forest 容易上手,不需要调用HTTP底层接口,而是像 Dubbo 那样的 RPC 框架一样,只需要定义接口、调用接口即可。几分钟内就可完成请求的定义、发送、接收响应、数据解析、错误处理、日志打印等过程。

    配置轻量,遵循约定优于配置的原则,只需在需要的时候进行配置,不配置也不会影响Forest请求的正常调用。

    简单优雅,将 HTTP 请求细节封装成 Java 接口 + 注解的形式,不必再关心发送 HTTP 请求的具体过程。使得 HTTP 请求信息与业务代码解耦,方便管理大量 HTTP 的 URL、Header、Body 等信息。

    扩展灵活,允许自定义拦截器、甚至是自定义注解,以此来扩展Forest的能力。

    Forest 不需要我们编写具体的 HTTP 调用过程,只需要定义一个接口,然后通过 Forest 注解将 HTTP 请求的信息添加到接口的方法上即可。请求发送方通过调用定义的接口就能自动发送请求和接受请求的响应。

    Forest 之所以能做到这样,是因为它将定义好的接口通过动态代理的方式生成了一个具体的实现类,然后组织、验证 HTTP 请求信息,绑定动态数据,转换数据形式,SSL 验证签名,调用后端 HTTP API执行实际请求,等待响应,失败重试,转换响应数据到 Java 类型等脏活累活都由这动态代理的实现类给包了。

    废话就不再多说,直接开始实战。

    第一步,添加 Maven 依赖。

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

    第二步,构建 HTTP 请求。

    在 Forest 中,所有的 HTTP 请求信息都要绑定到某一个接口的方法上,不需要编写具体的代码去发送请求。请求发送方通过调用事先定义好 HTTP 请求信息的接口方法。

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

    通过 @Post 注解,将上面的ForRestClient接口中的 simplePost() 方法绑定了一个 HTTP 请求,使用 POST 方式,可以使用@Body注解修饰参数的方式,将传入参数的数据绑定到 HTTP 请求体中。然后将请求响应的数据以String的方式返回给调用者。

    第三步,调用接口。

    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);
    
            // 调用Forest请求接口,并获取响应返回结果
            String result = myClient.simplePost("二哥");
            System.out.println(result);
        }
    }
    

    ForestConfiguration为 Forest 的全局配置对象类,所有的 Forest 的全局基本配置信息由此类进行管理。

    可以来看一下运行后的日志信息:

    {
      "args": {}, 
      "data": ""
    
    下一篇:没有了