Java实现HTTP请求的几种方式-CloseableHttpClient(三)

这篇具有很好参考价值的文章主要介绍了Java实现HTTP请求的几种方式-CloseableHttpClient(三)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

通过Apache封装好的CloseableHttpClient

CloseableHttpClient是在HttpClient的基础上修改更新而来的,这里还涉及到请求头token的设置(请求验证),利用fastjson转换请求或返回结果字符串为json格式,当然上面两种方式也是可以设置请求头token、json的,这里只在下面说明。

引入jar包

<!--CloseableHttpClient-->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>
<!--这是我习惯使用的jackson包,也可以使用其他的-->
<!--由于我是在springboot基础上开发,所以不用写version-->
<dependency>
     <groupId>com.fasterxml.jackson.core</groupId>
     <artifactId>jackson-databind</artifactId>
 </dependency>

Post方式请求

基于第一章的测试接口,建立以下程序

  1. 创建客户端
  2. 设置HttpPost对象,包括URL,请求头
  3. 执行方法
  4. 响应体转化

请求代码:

@Service
@Slf4j
public class CloseableHttpClientService{
	private static String tokenString = "";
    private static String AUTH_TOKEN_EXPIRED = "AUTH_TOKEN_EXPIRED";
    private final CloseableHttpClient httpClient;
    private final ObjectMapper objectMapper;


	//构造客户端对象
	    public CloseableHttpClientService(ObjectMapper objectMapper){
        //1.生成HttpClient对象并设置参数
        this.objectMapper = objectMapper;
        //设置客户端,如果无参数设置可以不用调setDefaultRequestConfig方法
        RequestConfig build = RequestConfig.custom()
                .setSocketTimeout(6000)
                .setConnectTimeout(6000).build();
        this.httpClient=HttpClientBuilder.create().setDefaultRequestConfig(build).build();
    }
	
 	/**
     * 以post方式调用第三方接口
     * @param url
     * @param param
     * @return
     */
    public <R> R doPost(String url, Object param,Class<R> returnType) throws IOException {
        String json = "";
        if(Objects.nonNull(param)){
            json = objectMapper.writeValueAsString(param);
        }
        HttpPost httpPost = new HttpPost(url);
        if (null != tokenString && tokenString.equals("")) {
            tokenString = getToken();
        }
        //Authorization的header头,用于token验证使用
        httpPost.addHeader("Authorization", tokenString);
        httpPost.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36");

        StringBuilder res= new StringBuilder();

        //设置编码UTF-8,不然会出现问号
        StringEntity se = new StringEntity(json,"UTF-8");
        //经过验证,无用
        //se.setContentEncoding("UTF-8");
        //发送json数据需要设置contentType,否则提供者端提示请求类型not support
        se.setContentType("application/json");
        //设置请求参数
        httpPost.setEntity(se);
        //客户端设置了默认值,局部就无需再设置,除非有额外要求
        /*RequestConfig build = RequestConfig.custom().setSocketTimeout(40000).setConnectTimeout(40000).build();
        httpPost.setConfig(build);*/

        HttpResponse response = httpClient.execute(httpPost);
        if (Objects.nonNull(response) && response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            //返回json格式
            res.append(EntityUtils.toString(response.getEntity(), "UTF-8"));
        }
        //这里不需要手动调用httpClient.close(),因为EntityUtils.toString方法中会自动调用输入流的close方法
        //直接调用httpClient.close()反而会再第二次调用时出现Collection Pool shut down的错误
        R r = objectMapper.readValue(res.toString(), returnType);
        return r;
    }

	/**
     * 获取第三方接口的token
     */
    public String getToken() {
        String token = "";
        Map<String,String> object = new HashMap<>();
        object.put("userName", "xxx");
        object.put("password", "xxx");

        HttpPost httpPost = new HttpPost("http://localhost:8082/nacos-service-provider/auth/login");
        httpPost.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36");
        try {
            StringEntity se = new StringEntity(objectMapper.writeValueAsString(object));
            se.setContentEncoding("UTF-8");
            //发送json数据需要设置contentType
            se.setContentType("application/json");
            //设置请求参数
            httpPost.setEntity(se);
            HttpResponse response = httpClient.execute(httpPost);
            log.info(String.valueOf(response));
            StringBuilder res= new StringBuilder();
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                //返回json格式
                res.append(EntityUtils.toString(response.getEntity(), "UTF-8"));
            }
            JsonResult jsonResult = objectMapper.readValue(res.toString(), JsonResult.class);
            if (Objects.isNull(jsonResult) || !ReturnCode.SUCCESS.getCode().equals(jsonResult.getCode())) {
                if (Objects.isNull(jsonResult)) {
                    throw new BizException(ReturnCode.ERROR);
                } else {
                    throw new BizException(jsonResult.getCode(), jsonResult.getMessage());
                }
            }
            LoginResponse loginResponse = objectMapper.convertValue(jsonResult.getData(), LoginResponse.class);
            //这里可以把返回的结果按照自定义的返回数据结果,把string转换成自定义类
            //ResultTokenBO result = JSONObject.parseObject(response, ResultTokenBO.class);
            //把response转为jsonObject
            /*JSONObject result = (JSONObject) JSONObject.parseObject(String.valueOf(response));
            if (result.containsKey("token")) {
                token = result.getString("token");
            }*/
            token = loginResponse.getToken();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return token;
    }
}

测试程序:

/**
     * 使用HttpURLConnection发送Post请求
     */
    @SneakyThrows
    public TestHttpAccessResponse sendHttpRequestByCloseableHttpClient() {
        TestHttpAccessRequest request = new TestHttpAccessRequest();
        request.setAge(16);
        request.setName("刘伞");
        request.setAddress("佛山市");

        String httpUrl = "http://localhost:8082/nacos-service-provider/testHttpAccess";
//        String httpUrl = "http://198.168.22.11:8085/nacos-service-provider/testHttpAccess";

        /**
         * 如果是List<T>这种带泛型的对象,则需要使用TypeReference<类型> typeRef = new TypeReference...
         * 注意日期的类型,需要事前设置类型转换器
         */
        JsonResult jsonResult = closeableHttpClientService.doPost(httpUrl, request, JsonResult.class);
        if (Objects.isNull(jsonResult) || !ReturnCode.SUCCESS.getCode().equals(jsonResult.getCode())) {
            if (Objects.isNull(jsonResult)) {
                throw new BizException(ReturnCode.ERROR);
            } else {
                throw new BizException(jsonResult.getCode(), jsonResult.getMessage());
            }
        }


        /**
         * 由于我做了统一的返回体JsonResult,所以还需要再转一遍
         * 这里不封装进去是因为,除了JsonResult可能还会有其他的返回体
         */
        TestHttpAccessResponse response = objectMapper.convertValue(jsonResult.getData(), TestHttpAccessResponse.class);
        return response;
    }

运行结果:
Java实现HTTP请求的几种方式-CloseableHttpClient(三)

关于设置连接超时时间connectTimeout不能设置超过20秒的问题

设置connectTimeout有两种设置方法,一种是设置在客户端级别的,一种是设置在当次请求的。
如果当次请求HttpPost/HttpGet没有设置连接超时时间,那么将会使用客户端级别的值,不需要copy

我在设置连接超时时间时,发现了一个现象,connectTimeout最多只能20秒,超过20秒就会在20秒的时候抛出异常
当我设置了40秒连接超时,结果到20秒之后就抛出了org.apache.http.conn.HttpHostConnectException
如果是设置20秒以内的超时时间,就会抛出org.apache.http.conn.ConnectTimeoutException:

设置的timeout是不变的,追溯到PlainConnectionSocketFactory的代码,猜想应该是Socket内部的优化

@Override
    public Socket connectSocket(
            final int connectTimeout,
            final Socket socket,
            final HttpHost host,
            final InetSocketAddress remoteAddress,
            final InetSocketAddress localAddress,
            final HttpContext context) throws IOException {
        final Socket sock = socket != null ? socket : createSocket(context);
        if (localAddress != null) {
            sock.bind(localAddress);
        }
        try {
            sock.connect(remoteAddress, connectTimeout);
        } catch (final IOException ex) {
            try {
                sock.close();
            } catch (final IOException ignore) {
            }
            throw ex;
        }
        return sock;
    }

Get方式请求

基于第一章的测试接口和上面doPost的例子,建立以下程序

  1. 创建客户端
  2. 拼接URL参数
  3. 设置HttpGet对象,包括URL,请求头
  4. 执行方法
  5. 响应体转化

请求方法:

    /**
     * 以get方式调用第三方接口
     * @param url
     * @return
     */
    public <R> R doGet(String url,Object param,Class<R> returnType) throws IOException, IllegalAccessException {
        //拼接url参数
        if(Objects.nonNull(param)){
            List<String> params = new ArrayList<>();
            Class<?> clazz = param.getClass();
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                declaredField.setAccessible(true);
                Object o = declaredField.get(param);
                if (declaredField.getType().equals(String.class)) {
                    //这里拼接的时候注意要使用URL编码
                    String s = (String) declaredField.get(param);
                    s = URLEncoder.encode(s);
                    o = s;
                }
                params.add(declaredField.getName() + "=" + o);
            }
            String paramStr = params.stream().collect(Collectors.joining("&"));
            url = url+"?"+paramStr;
        }


        //创建HttpClient对象
        CloseableHttpClient httpClient = HttpClientBuilder.create().build();
        HttpGet httpGet = new HttpGet(url);
        if (null != tokenString && !tokenString.equals("")) {
            tokenString = getToken();
        }
        //api_gateway_auth_token自定义header头,用于token验证使用
        httpGet.addHeader("Authorization",tokenString);
        //httpGet.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36");

        HttpResponse response = httpClient.execute(httpGet);
        StringBuilder res= new StringBuilder();
        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            //返回json格式
            res.append(EntityUtils.toString(response.getEntity()));
        }
        R r = objectMapper.readValue(res.toString(), returnType);
        return r;
    }

测试程序:

/**
     * 使用HttpURLConnection发送Post请求
     */
    @SneakyThrows
    public TestHttpAccessResponse sendHttpGetRequestByCloseableHttpClient() {
        TestHttpAccessRequest request = new TestHttpAccessRequest();
        request.setAge(16);
        request.setName("刘伞");
        request.setAddress("佛山市");

        String httpUrl = "http://localhost:8082/nacos-service-provider/testHttpAccessGet";

        /**
         * 如果是List<T>这种带泛型的对象,则需要使用TypeReference<类型> typeRef = new TypeReference...
         * 注意日期的类型,需要事前设置类型转换器
         */
        JsonResult jsonResult = closeableHttpClientService.doGet(httpUrl, request, JsonResult.class);
        if (Objects.isNull(jsonResult) || !ReturnCode.SUCCESS.getCode().equals(jsonResult.getCode())) {
            if (Objects.isNull(jsonResult)) {
                throw new BizException(ReturnCode.ERROR);
            } else {
                throw new BizException(jsonResult.getCode(), jsonResult.getMessage());
            }
        }


        /**
         * 由于我做了统一的返回体JsonResult,所以还需要再转一遍
         * 这里不封装进去是因为,除了JsonResult可能还会有其他的返回体
         */
        TestHttpAccessResponse response = objectMapper.convertValue(jsonResult.getData(), TestHttpAccessResponse.class);
        return response;
    }

结果:
Java实现HTTP请求的几种方式-CloseableHttpClient(三)

问题1:Connection Pool Shut down

这个问题是出现在,当我使用httpClient.close()方法之后,用相同的httpClient实例进行第二次请求就提示错误了。
CloseableHttpClient 采用的连接池管理,无需在业务代码中进行close资源回收。

问题2:返回出现中文问号或乱码的原因

首先设置StringEntity的编码有两种方式:

//第一种方式:后端无法解码
StringEntity stringEntity = new StringEntity(json);
stringEntity.setContentType("UTF-8");
 
//第二种方式:请求成功
StringEntity stringEntity = new StringEntity(json,"UTF-8");

这里我先说出我的实践结果:方式一出现问号,方式二可行。
先来看看这两种方式有什么不同。

第一种方式:

	//这里设置了contentEncoding 属性
    public void setContentEncoding(final String ceString) {
        Header h = null;
        if (ceString != null) {
            h = new BasicHeader(HTTP.CONTENT_ENCODING, ceString);
        }
        //这里调用了上层的方法
        setContentEncoding(h);
    }

上层AbstractHttpEntity方法

    public void setContentEncoding(final Header contentEncoding) {
        this.contentEncoding = contentEncoding;
    }

第二种方式:

	//这里设置了contentType 属性
    public StringEntity(final String string, final String charset)
            throws UnsupportedCharsetException {
        this(string, ContentType.create(ContentType.TEXT_PLAIN.getMimeType(), charset));
    }
    
    public StringEntity(final String string, final ContentType contentType) throws UnsupportedCharsetException {
        super();
        Args.notNull(string, "Source string");
        Charset charset = contentType != null ? contentType.getCharset() : null;
        if (charset == null) {
            charset = HTTP.DEF_CONTENT_CHARSET;
        }
        this.content = string.getBytes(charset);
        if (contentType != null) {
            setContentType(contentType.toString());
        }
    }

上层AbstractHttpEntity方法

    public void setContentType(final String ctString) {
        Header h = null;
        if (ctString != null) {
            h = new BasicHeader(HTTP.CONTENT_TYPE, ctString);
        }
        setContentType(h);
    }

    public void setContentType(final Header contentType) {
        this.contentType = contentType;
    }

方式一设置属性contentEncoding
方式二设置属性contentType

而这两个属性的区别就在于,将HttpResponse的内容解析出Json的时候,我这边使用了EntityUtils.toString这个方法来解析,这个方法里面是从contentType 获取到编码的设置的,如果没有设置,将会使用默认编码"ISO-8859-1"

private static String toString(
            final HttpEntity entity,
            final ContentType contentType) throws IOException {
        final InputStream inStream = entity.getContent();
        if (inStream == null) {
            return null;
        }
        try {
            //....
            //这里使用charset
            Charset charset = null;
            if (contentType != null) {
                charset = contentType.getCharset();
                if (charset == null) {
                    final ContentType defaultContentType = ContentType.getByMimeType(contentType.getMimeType());
                    charset = defaultContentType != null ? defaultContentType.getCharset() : null;
                }
            }
            if (charset == null) {
                charset = HTTP.DEF_CONTENT_CHARSET;
            }
           //...
        } finally {
            inStream.close();
        }
    }

所以如果没有使用方式二设置charset的话,就会设置为默认编码

其他阅读

Java实现HTTP请求的几种方式-HttpURLConnection(一)
Java实现HTTP请求的几种方式-Apache HttpClient(二)
Java实现HTTP请求的几种方式-RestTemplate(四)
Java实现HTTP请求的几种方式-OKHttp(五)文章来源地址https://www.toymoban.com/news/detail-418952.html

到了这里,关于Java实现HTTP请求的几种方式-CloseableHttpClient(三)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 构造http请求的几种方式(附源码)

    博主个人社区:开发与算法学习社区 博主个人主页:Killing Vibe的博客 欢迎大家加入,一起交流学习~~ form (表单) 是 HTML 中的一个常用标签. 可以用于给服务器发送 GET 或者 POST 请求. form 的重要参数: action: 构造的 HTTP 请求的 URL 是什么. method: 构造的 HTTP 请求的 方法 是 GET 还是

    2023年04月09日
    浏览(37)
  • hutool Http 工具发送POST请求的几种方式。

    Hutool是一个Java工具库,提供了丰富的功能模块,包括HTTP请求发送。以下是使用Hutool发送POST请求的几种方式: 使用HttpUtil.post方法: 使用HttpUtil.post方法可以发送简单的POST请求,示例如下: 使用HttpUtil.createPost方法: 使用HttpUtil.createPost方法可以创建一个HttpPost对象,然后设置

    2024年02月13日
    浏览(30)
  • hutool Http 工具发送POST请求的几种方式

            目录 依赖 🍊Maven 🍐Gradle 实践 hutool源码 hutool调用实践 其它自定义项         本质上,HttpUtil中的get和post工具方法都是HttpRequest对象的封装,因此如果想更加灵活操作Http请求,可以使用HttpRequest。今天咱们就列举一下hutool工具中常用的几种发送post请求的方式。

    2024年02月08日
    浏览(44)
  • http请求和响应格式说明,http的get和post请求方式说明,http的请求体body的几种数据格式

    一个HTTP请求报文由 请求行(request line)、请求头部(header)、空行和请求数据 4个部分组成, 请求报文的一般格式 1、第一行必须是一个请求行(request-line),用来说明请求类型,要访问的资源以及所使用的HTTP版本 2、紧接着是一个请求头(header),用来说明服务器要使用的附加信息

    2024年02月02日
    浏览(45)
  • 【Http协议④】常见的几种构造http请求的方式,form表单构造,Ajax构造,postman构造

    前言: 大家好,我是 良辰丫 ,这篇文章我将协同大家一起去学习几种构造http请求的方式.💞💞💞 🧑个人主页:良辰针不戳 📖所属专栏:javaEE初阶 🍎励志语句:生活也许会让我们遍体鳞伤,但最终这些伤口会成为我们一辈子的财富。 💦期待大家三连,关注,点赞,收藏。

    2024年02月06日
    浏览(41)
  • java中几种http请求方式

    在Java中,发送HTTP请求的方式主要有以下几种: 使用 java.net.HttpURLConnection 类: HttpURLConnection是Java中用于发送HTTP请求和接收HTTP响应的类。它是java.net包中的一部分,基于Java的网络编程API。 HttpURLConnection的一些常用参数和方法如下: 优点:这是Java标准库提供的方法,不需要额

    2024年02月05日
    浏览(29)
  • Java实现异步的几种方式

    普通线程实现异步,但频繁创建、销毁线程比较耗资源,所以一般交给线程池执行 结果: Future异步 和普通线程实现异步区别不大,只是使用Future是要获取执行后的返回值 结果: Spring的@Async异步 使用@Async注解实现异步的前提是需要在启动类上标注@EnableAsync来开启异步配置

    2024年02月04日
    浏览(47)
  • Java如何实现下载文件的几种方式

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/Boy_Martin/article/details/126058565

    2024年02月13日
    浏览(48)
  • java 实现开启异步线程的几种方式

    在Java中,有多种方式可以实现异步线程以避免在主线程中执行耗时操作导致界面卡顿的问题。以下是几种常用的方式: 使用 Thread 类:可以使用 Thread 类来创建一个新的线程,并在其 run() 方法中执行耗时操作。例如: 使用 Runnable 接口:可以通过实现 Runnable 接口并在其中实现

    2024年02月14日
    浏览(34)
  • Java实现字符串排序的几种方式

    创建实体类(此处引入了lombok) 一、使用List集合中自带的sort方法(字符串的位数保持一致,不一致的情况可以在左边补0,也可以使用String.format()方法补全) 1、在对象排序中使用 2、在字符串排序中使用 二、使用Stream流(字符串的位数保持一致,不一致的情况可以在左边补

    2024年02月11日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包