HttpClient

🟠HttpClient 简介

🟢官网


HttpClient 官网

这里我们使用的是 HttpClient 4.5

在这里插入图片描述

🍂引入相关依赖

<!--  httpclient -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>

🟢使用场景


  • 爬虫
  • 多系统之间的接口交互

🟠使用 JDK 原生 API 发送 Http 请求

/**
 * 使用 jdk 原始 api 请求网页
 */
@Test
void test() throws Exception {
    // 1. 输入需要访问的网址
    String strUrl = "https://www.baidu.com";
    // 2. 转换成 Url
    URL url = new URL(strUrl);
    // 3. 打开连接
    HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

    try (
        // 4. 获取 httpUrlConnection 的输入流
        InputStream inputStream = urlConnection.getInputStream();
        // 转换为字符串
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); // 指定编码方式为 utf8
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
    ) {
		// 读取字符串中的内容
        String line;
        while((line = bufferedReader.readLine()) != null) {
            System.out.println(line);
        }
    }
}

运行结果

在这里插入图片描述
复制运行结果到 VSCode

在这里插入图片描述

运行查看效果

在这里插入图片描述

🟠Get 无参请求

/**
 * 使用 httpclient 发送 get 请求
 */
@Test
void testGet1() {
    // 1. 创建一个可关闭的 httpclient 客户端.
    // 相当于打开的一个浏览器, 用于发送请求
    CloseableHttpClient httpClient = HttpClients.createDefault();
    // 要访问的地址
    String strUrl = "https://www.baidu.com";
    // 2. 构造 get 请求
    HttpGet httpGet = new HttpGet(strUrl);
    // 3. 构造响应对象
    CloseableHttpResponse response = null;

    try {
        // 浏览器发送 get 请求
        response = httpClient.execute(httpGet);
        // 获取响应结果
        HttpEntity entity = response.getEntity();
        // HttpEntity 的工具类
        // 输出响应结果 
        String ret = EntityUtils.toString(entity, StandardCharsets.UTF_8);
        System.out.println(ret);
        // 确保 entity 被消费, 且对应的流被关闭
        EntityUtils.consume(entity);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 关闭 httpclient
        if(httpClient != null) {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 关闭 response
        if(response != null) {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

🟠header

🟢User-Agent


在这里插入图片描述
图片来自网络

🍂当出现诸如这种系统检测亲不是真人行为时, 可以在请求中添加 User-Agent 解决这类问题

// 构造 get 请求
HttpGet httpGet = new HttpGet(strUrl);

// 添加 User-Agent
// 解决 httpclient 被检测不是真人行为问题
httpGet.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36");

在这里插入图片描述

🟢Referer


当我们爬取某个网站的图片时, 可能会出现提示 不是从本网站请求的图片 → 爬取失败. 这种行为被称作防盗链

🍂可以通过添加 Referer 的方式来解决上述问题

// 构造 get 请求
HttpGet httpGet = new HttpGet(strUrl);

// 添加 Referer
// 解决爬取图片失败的问题 -> 网址选择需要爬取对应图片的网址
httpGet.addHeader("Referer", "https://www.baidu.com/");

在这里插入图片描述

🟢完整代码

@Test
void testGet1() {
    // 1. 创建一个可关闭的 httpclient 客户端.
    // 相当于打开的一个浏览器, 用于发送请求
    CloseableHttpClient httpClient = HttpClients.createDefault();
    // 要访问的地址
    String strUrl = "https://www.baidu.com";
    // 2. 构造 get 请求
    HttpGet httpGet = new HttpGet(strUrl);

    // 添加 User-Agent
    // 解决 httpclient 被检测不是真人行为问题
    httpGet.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36");
    // 防盗链
    // 解决爬取图片失败的问题 -> 网址选择需要爬取对应图片的网址
    httpGet.addHeader("Referer", "https://www.baidu.com/");

    // 3. 构造响应对象
    CloseableHttpResponse response = null;

    try {
        // 浏览器发送 get 请求
        response = httpClient.execute(httpGet);
        // 获取响应结果
        HttpEntity entity = response.getEntity();
        // HttpEntity 的工具类
        // 输出响应结果
        String ret = EntityUtils.toString(entity, StandardCharsets.UTF_8);
        System.out.println(ret);
        // 确保 entity 被消费, 且对应的流被关闭
        EntityUtils.consume(entity);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 关闭 httpclient
        if(httpClient != null) {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 关闭 response
        if(response != null) {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

🟠Get 有参请求


🍂url 中含有参数时, 使用 httpclient 发送请求需要进行 encode 操作 → 避免参数中出现特殊字符从而解码失败

在这里插入图片描述

在这里插入图片描述

🟢完整代码

@Test
void testGet1() throws UnsupportedEncodingException {
    // 1. 创建一个可关闭的 httpclient 客户端.
    // 相当于打开的一个浏览器, 用于发送请求
    CloseableHttpClient httpClient = HttpClients.createDefault();

    String password = "bbb| dsa+321";


    // URLEncode, 避免参数中出现特殊字符从而解码失败
    // 如果是浏览器, 浏览器会自动进行 URLEncode 操作
    // 对于 httpclient 则需要手动执行 URLEncode
    password = URLEncoder.encode(password, StandardCharsets.UTF_8.name());


    String strUrl = "http://localhost:8811/httpclient-demo/test1?username=aaa&password=" + password;
    // 2. 构造 get 请求
    HttpGet httpGet = new HttpGet(strUrl);

    // 3. 构造响应对象
    CloseableHttpResponse response = null;

    try {
        // 浏览器发送 get 请求
        response = httpClient.execute(httpGet);

        // 获取响应结果
        HttpEntity entity = response.getEntity();
        // 获取响应的类型
        // System.out.println("ContentType: " + entity.getContentType());

        // HttpEntity 的工具类
        String ret = EntityUtils.toString(entity, StandardCharsets.UTF_8);
        System.out.println(ret);
        // 确保 entity 被消费, 且对应的流被关闭
        EntityUtils.consume(entity);

    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 关闭 httpclient
        if(httpClient != null) {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 关闭 response
        if(response != null) {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

🟠获取响应头及 ContentType

// 获取响应头
Header[] headers = response.getAllHeaders();
for (Header header : headers) {
    System.out.println("响应头: " + header.getName() + " value: " + header.getValue());
}

// 获取响应结果
HttpEntity entity = response.getEntity();
// 获取响应的类型
System.out.println("ContentType: " + entity.getContentType());

在这里插入图片描述

🟢完整代码

@Test
void testGet1() {
    // 1. 创建一个可关闭的 httpclient 客户端.
    // 相当于打开的一个浏览器, 用于发送请求
    CloseableHttpClient httpClient = HttpClients.createDefault();
    // 要访问的地址
    String strUrl = "https://www.baidu.com";
    // 2. 构造 get 请求
    HttpGet httpGet = new HttpGet(strUrl);

    // 3. 构造响应对象
    CloseableHttpResponse response = null;

    try {
        // 浏览器发送 get 请求
        response = httpClient.execute(httpGet);

        // 本次请求的状态
        StatusLine statusLine = response.getStatusLine();
        if(statusLine.getStatusCode() == HttpStatus.SC_OK) { // HttpStatus.SC_OK -> 200
            // 响应成功
            System.out.println("响应成功 | StatusCode: " + statusLine.getStatusCode());

            // 获取响应头
            Header[] headers = response.getAllHeaders();
            for (Header header : headers) {
                System.out.println("响应头: " + header.getName() + " value: " + header.getValue());
            }

            // 获取响应结果
            HttpEntity entity = response.getEntity();
            // 获取响应的类型
            System.out.println("ContentType: " + entity.getContentType());
            // HttpEntity 的工具类
            /*String ret = EntityUtils.toString(entity, StandardCharsets.UTF_8);
            System.out.println(ret);*/
            // 确保 entity 被消费, 且对应的流被关闭
            EntityUtils.consume(entity);
        } else {
            System.out.println("响应失败 | StatusCode: " + statusLine.getStatusCode());
        }


    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 关闭 httpclient
        if(httpClient != null) {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 关闭 response
        if(response != null) {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

🟠保存网络图片到本地


图片链接

在这里插入图片描述


在这里插入图片描述

🟢完整代码

/**
 * 保存网络图片到本地
 */
@Test
void testGet2() {
    // 1. 创建一个可关闭的 httpclient 客户端.
    // 相当于打开的一个浏览器, 用于发送请求
    CloseableHttpClient httpClient = HttpClients.createDefault();

    // 要访问的地址
    String strUrl = "https://www.baidu.com/img/PCfb_5bf082d29588c07f842ccde3f97243ea.png";

    // 2. 构造 get 请求
    HttpGet httpGet = new HttpGet(strUrl);

    // 3. 构造响应对象
    CloseableHttpResponse response = null;

    try {
        // 浏览器发送 get 请求
        response = httpClient.execute(httpGet);

        HttpEntity entity = response.getEntity();

        String contentType = entity.getContentType().getValue();
        // 图片格式可能为 (1)image (2)png (3)jpg...
        String suffix = contentType.substring(contentType.lastIndexOf('/') + 1); // 确定图片的格式
        suffix = "." + suffix;

        // 获取文件的字节流
        // 由于获取的是图片, 二进制数据. 因此使用 toByteArray() 方法
        byte[] byteArray = EntityUtils.toByteArray(entity);
        // 将图片保存到 e 盘
        String localPath = "e:\\aa" + suffix;
        FileOutputStream fileOutputStream = new FileOutputStream(new File(localPath));
        fileOutputStream.write(byteArray);
        // 关闭输出流
        fileOutputStream.close();
        // 确保 entity 被消费, 且对应的流被关闭
        EntityUtils.consume(entity);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(httpClient != null) {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if(response != null) {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

在这里插入图片描述

🟠设置访问代理


在爬虫系统中, 高频率的访问一个网站时, 某些网站会禁止该 IP
针对这种情况我们可以通过设置访问代理来继续爬取内容

免费代理 IP 网站

在这里插入图片描述

// 构造 get 请求
HttpGet httpGet = new HttpGet(strUrl);
// 创建一个代理
String ip = "42.63.65.119";
int port = 80;
HttpHost proxy = new HttpHost(ip, port);
// 配置 get 请求
RequestConfig requestConfig = RequestConfig.custom().setProxy(proxy).build();
httpGet.setConfig(requestConfig);

🟢完整代码

/**
 * 设置访问代理
 */
@Test
void testGet3() {
    // 1. 创建一个可关闭的 httpclient 客户端.
    // 相当于打开的一个浏览器, 用于发送请求
    CloseableHttpClient httpClient = HttpClients.createDefault();
    // 要访问的地址
    String strUrl = "https://www.baidu.com";
    // 2. 构造 get 请求
    HttpGet httpGet = new HttpGet(strUrl);
    // 创建一个代理
    String ip = "42.63.65.119";
    int port = 80;
    HttpHost proxy = new HttpHost(ip, port);
    // 配置 get 请求, 会覆盖全局的默认请求配置
    RequestConfig requestConfig = RequestConfig.custom().setProxy(proxy).build();
    httpGet.setConfig(requestConfig);

    // 3. 构造响应对象
    CloseableHttpResponse response = null;

    try {
        // 浏览器发送 get 请求
        response = httpClient.execute(httpGet);

        // 本次请求的状态
        StatusLine statusLine = response.getStatusLine();
        if(statusLine.getStatusCode() == HttpStatus.SC_OK) { // HttpStatus.SC_OK -> 200
            // 响应成功
            System.out.println("响应成功 | StatusCode: " + statusLine.getStatusCode());

            // 获取响应结果
            HttpEntity entity = response.getEntity();
            System.out.println(EntityUtils.toString(entity, StandardCharsets.UTF_8));
            // 确保 entity 被消费, 且对应的流被关闭
            EntityUtils.consume(entity);
        } else {
            System.out.println("响应失败 | StatusCode: " + statusLine.getStatusCode());
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 关闭 httpclient
        if(httpClient != null) {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 关闭 response
        if(response != null) {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

🟠连接超时 & 读取超时

🟢连接超时


在这里插入图片描述

🟢读取超时


在这里插入图片描述

🟢完整代码

@Test
 void testGet3() {
     // 1. 创建一个可关闭的 httpclient 客户端.
     // 相当于打开的一个浏览器, 用于发送请求
     CloseableHttpClient httpClient = HttpClients.createDefault();
     // 要访问的地址
     // String strUrl = "https://www.baidu.com";
     String strUrl = "https://www.github.com";
     // String strUrl = "https://www.csdn.net/";
     // 2. 构造 get 请求
     HttpGet httpGet = new HttpGet(strUrl);
     // 创建一个代理
     String ip = "42.63.65.119";
     int port = 80;
     HttpHost proxy = new HttpHost(ip, port);
     // 配置 get 请求, 会覆盖全局的默认请求配置
     RequestConfig requestConfig = RequestConfig
             .custom()
             // .setProxy(proxy)
             .setConnectTimeout(5000) // 设置连接超时时间最大为 5000ms -> 5s
             .setSocketTimeout(1) // 设置读取超时时间最大为 1ms -> 0.001s
             .build();

     httpGet.setConfig(requestConfig);

     // 3. 构造响应对象
     CloseableHttpResponse response = null;

     try {
         // 浏览器发送 get 请求
         response = httpClient.execute(httpGet);

         // 本次请求的状态
         StatusLine statusLine = response.getStatusLine();
         if(statusLine.getStatusCode() == HttpStatus.SC_OK) { // HttpStatus.SC_OK -> 200
             // 响应成功
             System.out.println("响应成功 | StatusCode: " + statusLine.getStatusCode());

             // 获取响应结果
             HttpEntity entity = response.getEntity();
             System.out.println(EntityUtils.toString(entity, StandardCharsets.UTF_8));
             // 确保 entity 被消费, 且对应的流被关闭
             EntityUtils.consume(entity);
         } else {
             System.out.println("响应失败 | StatusCode: " + statusLine.getStatusCode());
         }
     } catch (IOException e) {
         e.printStackTrace();
     } finally {
         // 关闭 httpclient
         if(httpClient != null) {
             try {
                 httpClient.close();
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
         // 关闭 response
         if(response != null) {
             try {
                 response.close();
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     }
 }

🟠mime type


mime type → Multipurpose Internet Mail Extensions → 多用途互联网邮件扩展类型

参考手册

🟠Post 请求-表单类型


在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

🟢完整代码

/**
 * 发送 application/x-www-form-urlencoded 类型的请求(表单类型的请求)
 */
@Test
void testPost1() {
    // 创建一个可关闭的 httpclient 客户端
    // 相当于打开一个浏览器, 用于发送请求
    CloseableHttpClient httpClient = HttpClients.createDefault();

    // 输入要访问的网址
    String url = "xxx";

    // 构造 post 请求
    HttpPost post = new HttpPost(url);

    // 设置请求头
    post.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");

    // 为 post 对象设置参数
    /**
     * NameValuePair: <input id = "user-name-label" type="text" name="userName"/>
     * name(userName) 和 input 标签中输入的值构成了一个 NameValuePair 对象
     */
    List<NameValuePair> parameters = new ArrayList<>();
    parameters.add(new BasicNameValuePair("email", "xxx"));
    parameters.add(new BasicNameValuePair("password", "xxx"));

    // 将参数集合设置到 formEntity
    UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters, StandardCharsets.UTF_8);
    post.setEntity(formEntity);

    CloseableHttpResponse response = null;

    try {
        response = httpClient.execute(post);

        HttpEntity entity = response.getEntity();

        System.out.println("ContentType: " + entity.getContentType());

        EntityUtils.toString(entity, StandardCharsets.UTF_8); // 以 utf8 的编码方式输出响应结果
        // 关闭流
        EntityUtils.consume(entity);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(response != null) {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if(httpClient != null) {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

🟠Post 请求-Json 类型


引入 Hutool → String 转 Json

<!--  Hutool -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.16</version>
</dependency>

在这里插入图片描述

🟢完整代码

/**
 * 发送 application/json 类型的请求
 */
@Test
void testPost2() {
    // 创建一个可关闭的 httpclient 客户端
    // 相当于打开一个浏览器, 用于发送请求
    CloseableHttpClient httpClient = HttpClients.createDefault();

    // 输入要访问的网址
    String url = "https://mail.163.com/?msg=authfail#return";

    // 构造 post 请求
    HttpPost post = new HttpPost(url);

    Map<String, String> map = new HashMap<>();
    map.put("name", "bibubibu");
    map.put("password", "123456");
    // 转 Json
    String jsonStr = JSONUtil.toJsonStr(map);

    StringEntity jsonEntity = new StringEntity(jsonStr, StandardCharsets.UTF_8);
    // 设置编码方式
    jsonEntity.setContentEncoding(Consts.UTF_8.name()); // utf-8
    // 设置数据的传输格式
    // jsonEntity.setContentType(new BasicHeader("Content-Type", "application/json; charset=utf-8"));
	jsonEntity.setContentType("application/json; charset=utf-8"); // 效果同上

    post.setEntity(jsonEntity);


    CloseableHttpResponse response = null;

    try {
        response = httpClient.execute(post);

        HttpEntity entity = response.getEntity();

        System.out.println(EntityUtils.toString(entity, StandardCharsets.UTF_8)); // 以 utf8 的编码方式输出响应结果

        // 关闭流
        EntityUtils.consume(entity);
        
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(response != null) {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if(httpClient != null) {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

🟠Post 请求-上传文件


引入 mime → 上传文件

<!--  mime -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpmime</artifactId>
    <version>4.5.13</version>
</dependency>

在这里插入图片描述

🟢完整代码

/**
 * 发送 multipart / form-data 类型上传文件的请求
 */
@Test
void testPost3() {
    // 创建一个可关闭的 httpclient 客户端
    // 相当于打开一个浏览器, 用于发送请求
    CloseableHttpClient httpClient = HttpClients.createDefault();

    // 输入要访问的网址
    String url = "xxx";

    // 构造 post 请求
    HttpPost post = new HttpPost(url);

    // 构造一个 ContentBody 实现类对象
    FileBody contentBody = new FileBody(new File("e:\\t1.txt"));

    MultipartEntityBuilder builder = MultipartEntityBuilder.create();

    // 对于普通的表单字段如果含有中文, 则不能通过 addTextBody, 否则会出现乱码
    StringBody usernameText = new StringBody("bibubibu", ContentType.create("text/plain", StandardCharsets.UTF_8));

    HttpEntity httpEntity = builder.setCharset(StandardCharsets.UTF_8) // 设置编码方式
            .setContentType(ContentType.create("multipart/form-data", Consts.UTF_8)) // 设置数据的传输格式
            .setMode(HttpMultipartMode.BROWSER_COMPATIBLE) // 设置浏览器模式, 确保 multipart 与浏览器的提交表单兼容
            .addPart("filename", contentBody) // 上传文件
            .addBinaryBody("filename", new File("e:\\1.png")) // 上传二进制文件
            // .addTextBody("username", "bibubibu") // 上传对应的参数
            .addPart("username", usernameText)
            .addTextBody("password", "123456") // 上传对应的参数
            .build();

    post.setEntity(httpEntity);

    CloseableHttpResponse response = null;

    try {
        response = httpClient.execute(post);

        HttpEntity entity = response.getEntity();

        System.out.println(EntityUtils.toString(entity, StandardCharsets.UTF_8)); // 以 utf8 的编码方式输出响应结果

        // 关闭流
        EntityUtils.consume(entity);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(response != null) {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if(httpClient != null) {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

🟠绕过 https 安全验证


🍂提出疑问 → 使用 httpclient 可以请求 https, 为何还要绕过 https 安全验证

在这里插入图片描述
这里指的 https 是针对不安全的 https 链接, 而上图中的 https://www.baidu.com 是安全的 https 链接

在这里插入图片描述
因为官方 https 证书是收费且存在有效期的, 如果证书是非官方认证或者证书已过期, 就是不安全的 https 链接

对于这种不安全的 https 链接, 解决方式如下

  • 配置 httpclient 通过认证需要的密钥
  • 配置 httpclient 绕过 https 的安全认证

如果不配置 httpclient, 则会抛出 SSLHandshakeException

🟢完整代码

/**
 * 绕过 https 安全认证
 */
@Test
void testHttps() throws Exception {
    // 1. 创建一个可关闭的 httpclient 客户端.
    // 相当于打开的一个浏览器, 用于发送请求

    // 绕过 https 安全认证
    Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
            .register("http", PlainConnectionSocketFactory.INSTANCE)
            .register("https", trustHttpsCertificates())
            .build();

    PoolingHttpClientConnectionManager pool = new PoolingHttpClientConnectionManager(registry);

    HttpClientBuilder httpClientBuilder = HttpClients.custom().setConnectionManager(pool);

    CloseableHttpClient httpClient = httpClientBuilder.build();

    // 要访问的地址
    String strUrl = "https://www.baidu.com";
    // 2. 构造 get 请求
    HttpGet httpGet = new HttpGet(strUrl);

    // 3. 构造响应对象
    CloseableHttpResponse response = null;

    try {
        // 浏览器发送 get 请求
        response = httpClient.execute(httpGet);

        // 获取响应结果
        HttpEntity entity = response.getEntity();
        // 获取响应的类型
        // System.out.println("ContentType: " + entity.getContentType());

        // HttpEntity 的工具类
        String ret = EntityUtils.toString(entity, StandardCharsets.UTF_8);
        System.out.println(ret);
        // 确保 entity 被消费, 且对应的流被关闭
        EntityUtils.consume(entity);

    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        // 关闭 httpclient
        if(httpClient != null) {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 关闭 response
        if(response != null) {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

// 创建支持安全协议的连接工厂
private ConnectionSocketFactory trustHttpsCertificates() throws Exception {

    // 判断是否信任 url(true, 信任所有的 url)
    SSLContext sslContext = SSLContextBuilder.create()
            .loadTrustMaterial(null, (chain, authType) -> true) // 加载信任的证书
            .build();


    SSLConnectionSocketFactory socketFactory =
            new SSLConnectionSocketFactory(sslContext
                    , new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}
                    , null
                    , NoopHostnameVerifier.INSTANCE);


    return socketFactory;
}

🟠HttpClient 连接池


使用 httpclient 连接池的优点

  • 提高程序的运行速度
  • 连接的可复用性

🟠封装 HttpClientUtils

/**
 * 封装 httpclient 工具类
 */
@Slf4j
public class HttpClientUtils {

    private static final int CONNECT_TIMEOUT = 5 * 1000; // 5000ms -> 5s
    private static final int SOCKET_TIMEOUT = 5 * 1000; // 5000ms -> 5s
    private static final int CONNECT_REQUEST_TIMEOUT = 5 * 1000; // 5000ms -> 5s

    /**
     * 请求配置
     * @return
     */
    private static RequestConfig builderRequestConfig() {
        return RequestConfig.custom()
                .setConnectTimeout(CONNECT_TIMEOUT) // 设置连接到目标地址的超时时间
                .setSocketTimeout(SOCKET_TIMEOUT) // 设置读取到数据的超时时间
                .setConnectionRequestTimeout(CONNECT_REQUEST_TIMEOUT) // 设置获取到连接(httpclient)的超时时间
                .build();
    }

    /**
     * Get 请求
     * @param url, 要访问的地址
     * @param paramMap, 请求头
     * @return
     */
    public static String doGet(String url, Map<String, String> paramMap) {
        // 1. 创建一个可关闭的 httpclient 客户端.
        // 相当于打开的一个浏览器, 用于发送请求
        CloseableHttpClient httpClient = HttpClients.createDefault();

        String ret = "";
        CloseableHttpResponse response = null;

        // 2. 构造 get 请求
        HttpGet get = new HttpGet(url);

        // 设置请求头
        if(paramMap != null) {
            for(Map.Entry<String, String> entry : paramMap.entrySet()) {
                // 添加请求头
                get.addHeader(entry.getKey(), entry.getValue());
            }
        }

        // 设置请求头的另一种方式
        /*try {
            URIBuilder uriBuilder = new URIBuilder(url);
            if(paramMap != null) {
                for(Map.Entry<String, String> entry : paramMap.entrySet()) {
                    // 添加请求头
                    uriBuilder.addParameter(entry.getKey(), entry.getValue());
                }
            }

            URI uri = uriBuilder.build();
            HttpGet httpGet = new HttpGet(uri);

        } catch (Exception e) {
            e.printStackTrace();
        }*/

        try {
            // 3. 发送 Get 请求
            response = httpClient.execute(get);

            // 判断响应状态
            StatusLine statusLine = response.getStatusLine();
            int statusCode = statusLine.getStatusCode();

            if(HttpStatus.SC_OK == statusCode) { // statusCode == 200
                log.info("响应成功, 状态码为: " + statusCode);

                HttpEntity entity = response.getEntity();

                ret = EntityUtils.toString(entity, StandardCharsets.UTF_8);

                // 关闭 entity
                EntityUtils.consume(entity);
            } else {
                log.info("响应失败, 状态码为: " + statusCode);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            extracted(response, httpClient);
        }

        return ret;
    }


    /**
     * Post, 发送 application/x-www-form-urlencoded 类型的请求(表单类型的请求)
     * @param url, 要访问的地址
     * @param paramMap, 参数列表
     * @return
     */
    public static String doPost(String url, Map<String, String> paramMap) {
        // 1. 创建一个可关闭的 httpclient 客户端.
        // 相当于打开的一个浏览器, 用于发送请求
        CloseableHttpClient httpClient = HttpClients.createDefault();

        String ret = "";
        CloseableHttpResponse response = null;

        // 2. 构造 post 请求
        HttpPost post = new HttpPost(url);

        // 设置请求头 -> 确保请求头是 x-www-form-urlencoded 类型
        // post.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");

        if(paramMap != null) {
            List<NameValuePair> parameters = new ArrayList<>();
            for (Map.Entry<String, String> entry : paramMap.entrySet()) {
                parameters.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }

            // 模拟表单
            UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters, StandardCharsets.UTF_8);

            post.setEntity(formEntity);
        }

        // 配置 post 请求
        post.setConfig(builderRequestConfig());

        try {
            // 3. 发送 post 请求
            response = httpClient.execute(post);

            // 判断响应状态
            StatusLine statusLine = response.getStatusLine();
            int statusCode = statusLine.getStatusCode();

            if(HttpStatus.SC_OK == statusCode) { // statusCode == 200
                log.info("响应成功, 状态码为: " + statusCode);

                HttpEntity entity = response.getEntity();

                ret = EntityUtils.toString(entity, StandardCharsets.UTF_8);

                // 关闭 entity
                EntityUtils.consume(entity);
            } else {
                log.info("响应失败, 状态码为: " + statusCode);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            extracted(response, httpClient);
        }

        return ret;
    }

    /**
     * Post, 发送 application/json 类型的请求
     * @param url, 要访问的地址
     * @param paramMap, 参数列表
     * @return
     */
    public static String doPost4Json(String url, Map<String, String> paramMap) {
        // 1. 创建一个可关闭的 httpclient 客户端.
        // 相当于打开的一个浏览器, 用于发送请求
        CloseableHttpClient httpClient = HttpClients.createDefault();

        String ret = "";
        CloseableHttpResponse response = null;

        // 构造 post 请求
        HttpPost post = new HttpPost(url);

        // 设置请求头 -> 确保请求头是 json 类型
        post.addHeader("Content-Type", "application/json; charset=utf-8");

        StringEntity jsonEntity = new StringEntity(JSONUtil.toJsonStr(paramMap), StandardCharsets.UTF_8);

        // 设置编码方式
        jsonEntity.setContentEncoding(Consts.UTF_8.name());
        // 设置请求头
        // jsonEntity.setContentType("application/json; charset=utf-8"); // 效果等同于 post.addHeader("Content-Type", "application/json; charset=utf-8");

        post.setEntity(jsonEntity);

        // 配置 post 请求
        post.setConfig(builderRequestConfig());

        try {
            // 3. 发送 post 请求
            response = httpClient.execute(post);

            // 判断响应状态
            StatusLine statusLine = response.getStatusLine();
            int statusCode = statusLine.getStatusCode();

            if(HttpStatus.SC_OK == statusCode) { // statusCode == 200
                log.info("响应成功, 状态码为: " + statusCode);

                HttpEntity entity = response.getEntity();

                ret = EntityUtils.toString(entity, StandardCharsets.UTF_8);

                // 关闭 entity
                EntityUtils.consume(entity);
            } else {
                log.info("响应失败, 状态码为: " + statusCode);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            extracted(response, httpClient);
        }

        return ret;
    }

    private static void extracted(CloseableHttpResponse response, CloseableHttpClient httpClient) {
        if(response != null) {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if(httpClient != null) {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 21
    评论
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值