HttpClient调用外部接口业务别忘了超时配置!公司工具类打包好啦

前言

微信图片_20240109170459.png

        在工作中,往往会遇到需要对接外部接口的业务需求,根据接口文档确定请求IP以及相关的参数数据需求后,通过HttpClient进行第三方外部接口的调用。在这种业务中,也要考虑好请求超时导致的接口报错的处理。为什么呢?就我公司业务而言,我们这边的视频或者说融合通讯,因为厂商多,设备型号杂,经常都有调用不通的情况。今天正常的视频可能明天就不行了,如果后端这里不处理超时,前端如果无法拿到设备信息或者设备ID,页面的报错信息可能反应的就是接口报错了。这显然是不合情理的,为此,我们可以考虑加一个请求超时的参数(在配置文件中读取自定义参数) ,控制一下。本文就HttpClient做一个简单的快速入门,并搭配好我编写的一个完整工具类,希望可以帮助大家在第三方接口调用业务中有所帮助。

实现思路步骤

  1. 创建 HttpClient 对象,可以使用 HttpClients.createDefault() 方法。

  2. 确定请求方法类型

    • 如果是无参数的 GET 请求,则直接使用构造方法 HttpGet(String url) 创建 HttpGet 对象即可;如果是带参数的 GET 请求,则可以先使用 URIBuilder(String url) 创建对象,再调用 addParameter(String param, String value) 或者 setParameter(String param, String value) 方法来设置请求参数,并调用 build() 方法构建一个 URI 对象。只有构造方法 HttpGet(URI uri) 可以创建 HttpGet 对象。
    • 如果是无参数的GET请求,则直接使用构造方法HttpPost(String url)创建HttpPost对象即可; 如果是带参数POST请求,先构建HttpEntity对象并设置请求参数,然后调用setEntity(HttpEntity entity) 创建HttpPost对象。
  3. 加载配置类中的超时配置数据,配置在 RequestConfig 对象中,将该对象配置到具体的HttpPost / HttpGet 对象里,设置好超时规则

  4. 创建 HttpResponse,调用 HttpClient 对象的 execute(HttpUriRequest request) 方法发送请求,该方法返回一个 HttpResponse。调用 HttpResponse 的 getAllHeaders()getHeaders(String name) 等方法可以获取服务器的响应头;调用 HttpResponse 的 getEntity() 方法可以获取 HttpEntity 对象,该对象包装了服务器的响应内容。程序可以通过该对象获取服务器的响应内容。通过调用 getStatusLine().getStatusCode() 可以获取响应状态码。

  5. 通过 e instanceof SocketTimeoutExceptione instanceof ConnectTimeoutException 来抛出请求失败的具体异常,在调用方通过catch (SocketTimeoutException e) 和 catch (SocketTimeoutException e) 来进行捕获,并响应自定义的异常消息

工具类

        本工具类主要涉及了发送GET请求和POST请求的情况,搭配了超时处理机制,同时针对请求参数和请求头参数的配置进行动态配置,满足无参和有参的请求需求。

/**
 * Http请求工具类
 * @author 吴志鑫
 */
public class HttpUtil {

    /**
 	 * 读取配置文件中自定义的超时参数
 	 */
    @Resource
    Propertise propertise;

    private static HttpUtil httpUtil;

    @PostConstruct
    public void init(){
        httpUtil = this;
        httpUtil.propertise = propertise;
    }

    /**
     * 发送Post请求(带请求头数据)
     * @param url 请求url
     * @param params 请求参数map集合
     * @param headers 请求头参数map集合
     * @return 响应对象
     * @param <T> 请求参数值类型
     */
    public static <T> HttpResponse sendPost(String url, Map<String, T> params, Map<String, String> headers) throws ConnectTimeoutException, SocketTimeoutException {
        
        // 创建Httpclient对象
        HttpClient httpClient = HttpClients.createDefault();

        // 配置相关超时参数
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(httpUtil.propertise.getConnectionTimeOut())
                .setSocketTimeout(httpUtil.propertise.getSocketTimeOut()).build();

        // 创建 HttpPost 请求对象,如果是Get请求就用HttpGet
        HttpPost httpPost = new HttpPost(url);

        // 设置超时参数
        httpPost.setConfig(requestConfig);

        if (params != null){
            // 将 params 转换为 JSON 格式的字符串
            String json = new Gson().toJson(params);

            StringEntity requestEntity = new StringEntity(json, ContentType.APPLICATION_JSON);
            httpPost.setEntity(requestEntity);
        }

        if (headers != null && !headers.isEmpty()){
            // 设置请求头
            headers.forEach(httpPost::setHeader);
        }

        // 发送请求并获取响应
        try {
            return httpClient.execute(httpPost);
        } catch (IOException e) {

            // 因为超时导致的抛出超时异常
            if (e instanceof ConnectTimeoutException) {
                throw (ConnectTimeoutException) e;
            }

            // 因为套接字超时时间导致的抛出套接字超时异常
            if (e instanceof SocketTimeoutException) {
                throw (SocketTimeoutException) e;
            }
            throw new RuntimeException(e);
        }
    }

    /**
     * 发送GET请求
     * @param url 请求路径
     * @param params 请求参数的map集合
     * @return 响应对象
     */
    public static HttpResponse sendGet(String url, Map<String, String> params, Map<String, String> headers) throws ConnectTimeoutException, SocketTimeoutException {

        // 创建Httpclient对象
        HttpClient httpClient = HttpClients.createDefault();

        String finalUrl = url;
        if(params != null){
            try {
                // 构建带有参数的 URI
                URIBuilder builder = new URIBuilder(url);
                params.forEach(builder::addParameter);
                finalUrl = builder.build().toString();
            } catch (URISyntaxException e) {
                throw new RuntimeException(e);
            }
        }

        // 创建 HttpPost 请求对象,如果是Get请求就用HttpGet
        HttpGet httpGet = new HttpGet(finalUrl);

        // 配置相关超时参数
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(httpUtil.propertise.getConnectionTimeOut())
                .setSocketTimeout(httpUtil.propertise.getSocketTimeOut()).build();

        // 设置超时参数
        httpGet.setConfig(requestConfig);

        if (headers != null && !headers.isEmpty()){
            // 设置请求头
            headers.forEach(httpGet::setHeader);
        }

        // 发送请求并获取响应
        try {
            // 发送请求并获取响应
            return httpClient.execute(httpGet);
        } catch (IOException e) {

            // 因为超时导致的抛出超时异常
            if (e instanceof ConnectTimeoutException) {
                throw (ConnectTimeoutException) e;
            }

            // 因为套接字超时时间导致的抛出套接字超时异常
            if (e instanceof SocketTimeoutException) {
                throw (SocketTimeoutException) e;
            }
            throw new RuntimeException(e);
        }
    }
}

超时参数配置类

        一般这种关键参数都是自定义进行调控,所以我们需要将其暴露到配置文件中,这样子才能减少关键配置的耦合性

/**
 * 请求超时数据配置类
 * @author 吴志鑫
 */
@Component
public class Propertise {

    /**
     * 建立连接的最大等待时间
     */
    private Integer connectionTimeOut;

    /**
     * 从服务器读取数据的最大等待时间
     */
    private Integer socketTimeOut;

    @Autowired
    public Propertise(@Value("${http-request-connection-timeout}") Integer connectionTimeOut
                    , @Value("${http-request-socket-timeout}") Integer socketTimeOut) {
        if (connectionTimeOut == null) {
            throw new IllegalArgumentException("http-request-connection-timeout cannot be null");
        }
        if (socketTimeOut == null) {
            throw new IllegalArgumentException("http-request-socket-timeout cannot be null");
        }
        this.connectionTimeOut = connectionTimeOut;
        this.socketTimeOut = socketTimeOut;
    }

    public Integer getConnectionTimeOut() {
        return connectionTimeOut;
    }

    public Integer getSocketTimeOut() {
        return socketTimeOut;
    }
}

调用例子

public static void main(String[] args) {

        // 请求的 URL
        String url = "https://httpbin.org/post";

        // 请求参数
        Map<String, String> formparams = new HashMap<>(2);
        formparams.put("clientId", "953251617");
        formparams.put("clientSecret", "Cmcc#8888");

        HttpResponse httpResponse = null;
        try {
            httpResponse = HttpUtil.sendPost(url, formparams, null);
        } catch (ConnectTimeoutException e) {

            // 自定义返回连接超时的错误消息
            String errorMessage = "连接超时,请重试或联系管理员";

            // 返回错误消息给调用者..........

            throw new RuntimeException(e);
        } catch (SocketTimeoutException e) {
            // 自定义返回套接字超时的错误消息
            String errorMessage = "套接字超时,请重试或联系管理员";

            // 返回错误消息给调用者..........

            throw new RuntimeException(e);
        }

        HttpEntity responseEntity = httpResponse.getEntity();
        String responseString = null;
        try {
            responseString = EntityUtils.toString(responseEntity,"UTF-8");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        // 处理响应结果
        System.out.println(responseString);

    }

总结

        本文主要讲解了 HttpGet 请求响应的一般步骤。在使用 HttpClient 进行 HttpGet 请求时,我们需要先创建 HttpClient 对象,然后根据请求是否带参数选择不同的方式创建 HttpGet 对象。接着,通过执行 execute(HttpUriRequest request) 方法发送请求,获取 HttpResponse 对象,并通过该对象获取服务器的响应头和响应内容。最后需要释放连接。通过本文的介绍,读者可以了解到 HttpGet 请求的基本操作流程,对于使用 HttpClient 进行 HttpGet 请求的开发工作会有更深入的理解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/305342.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python教程37:使用turtle画一个戴帽子的皮卡丘

---------------turtle源码集合--------------- Python教程36&#xff1a;海龟画图turtle写春联 Python源码35&#xff1a;海龟画图turtle画中国结 Python源码31&#xff1a;海龟画图turtle画七道彩虹 Python源码30&#xff1a;海龟画图turtle画紫色的小熊 Python源码29&a…

在Ubuntu中检查内存的五个命令,总有一种适合你

序言 作为Ubuntu用户,尤其是管理员,我们需要检查系统使用了多少内存资源,以及有多少是可用的。我们还知道,大多数管理任务最好从Linux命令行完成,而不是从图形用户界面完成。例如,服务器通常在shell上工作,没有图形用户界面。由于控制服务器上的内存资源是最重要的,因…

平衡搜索二叉树(AVL树)

前言 二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单支树&#xff0c;查 找元素相当于在顺序表中搜索元素&#xff0c;效率低下。因此&#xff0c;两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在1962年 发明了一种解决上述…

体元法--体积计算

文章目录 环境&#xff1a;1.1 体元法介绍&#xff1a;2.1 python代码3.1 可视化 环境&#xff1a; Open3D 1.1 体元法介绍&#xff1a; 用一个个体素去占据点云&#xff0c;然后对所有体素求和 2.1 python代码 conda activete deeplabv3plus(环境名称–安装好open3D的) py…

【Python机器学习】决策树集成——随机森林

理论知识&#xff1a; 集成是合并多个机器学习模型来构建更强大模型法方法。 随机森林本质上是许多决策树的集合&#xff0c;其中每棵树都和其他数略有不同&#xff0c;随机森林背后的思想是&#xff1a;每棵树的预测可能都比较好&#xff0c;但是可能对部分数据过拟合&#…

若依项目的table列表中对每一个字段增加排序按钮(单体版和前后端分离版)

一、目标:每一个字段都添加上下箭头用来排序 只需要更改前端代码,不需要更改后端代码,后面会讲解原理 二、单体版实现方式: 1.在options中添加sortable:true 2.在需要排序的字段中添加sortable:true 三、前后端分离版 1.el-table上添加@sort-change=“handleSortChange”…

MySQL的导入导出及备份

一.准备导入之前 二.navicat导入导出 ​编辑 三.MySQLdump命令导入导出 四.load data file命令的导入导出 五.远程备份 六. 思维导图 一.准备导入之前 需要注意&#xff1a; 在导出和导入之前&#xff0c;确保你有足够的权限。在进行导入操作之前&#xff0c;确保目标数据…

Tensorflow2.0笔记 - 创建tensor

tensor创建可以基于numpy&#xff0c;list或者tensorflow本身的API。 笔记直接上代码&#xff1a; import tensorflow as tf import numpy as np import matplotlib.pyplot as plttf.__version__#通过numpy创建tensor tensor0 tf.convert_to_tensor(np.ones([2,3])) print(te…

GitHub 一周热点汇总 第4期 (2024/01/01-01/06)

GitHub一周热点汇总第四期 (2023/12/24-12/30)&#xff0c;梳理每周热门的GitHub项目&#xff0c;了解热点技术趋势&#xff0c;掌握前沿科技方向&#xff0c;发掘更多商机。2024年到了&#xff0c;希望所有的朋友们都能万事顺遂。 说明一下&#xff0c;有时候本周的热点项目会…

null和undefined的区别

null 和 undefined 是 JavaScript 中的两个基础类型特殊值。它们都表示“空”&#xff0c;但是有一些区别。 一、null 在 JavaScript 内部&#xff0c;null 被视为一个表示空值或缺少值的对象指针。在计算机内存中&#xff0c;它通常被表示为一个指向内存空间的空指针。这意味…

源码开发实践:搭建企业培训APP的技术难题及解决方案

在企业培训源码开发实践中&#xff0c;各位开发者可能遇到各种各样的问题&#xff0c;本文将深入探讨这些挑战&#xff0c;并提供解决方案&#xff0c;助力你顺利搭建企业培训APP。 1.多平台兼容性 企业中员工使用的设备多种多样&#xff0c;包括iOS、Android等不同操作系统。…

电力监控系统在数据中心应用

摘 要&#xff1a;在电力系统的运行过程中&#xff0c;变电站作为整个电力系统的核心&#xff0c;在保证电力系统可靠的运行方面起着至关重要的作用&#xff0c;基于此需对变电站监控系统的特点进行分析&#xff0c;结合变电站监控系统的功能需求&#xff0c;对变电站电力监控系…

BitMap解析(一)

文章目录 前言数据结构添加与删除操作 JDK中BitSet源码解析重要成员属性初始化添加数据清除数据获取数据size和length方法集合操作&#xff1a;与、或、异或 前言 为什么称为bitmap&#xff1f; bitmap不仅仅存储介质以及数据结构不同于hashmap&#xff0c;存储的key和value也…

Spring MVC概述及入门

MVC介绍 MVC是一种设计模式&#xff0c;将软件按照模型、视图、控制器来划分&#xff1a; M&#xff1a;&#xff08;Model&#xff09;模型层&#xff0c;指工程中的JavaBean&#xff0c;作用是处理数据 数据模型&#xff1a;User、Student&#xff0c;装数据 业务模型&#…

C++ Primer 第五版 中文版 阅读笔记 + 个人思考

C Primer 第五版 中文版 阅读笔记 个人思考 第 10 章 泛型算法10.1 概述练习10.1练习10.2 第 10 章 泛型算法 泛型的体现&#xff1a;容器类型&#xff08;包括内置数组&#xff09;&#xff0c;元素类型&#xff0c;元素操作方法。 顺序容器定义的操作&#xff1a;insert&a…

Android-多线程

线程是进程中可独立执行的最小单位&#xff0c;也是 CPU 资源&#xff08;时间片&#xff09;分配的基本单位&#xff0c;同一个进程中的线程可以共享进程中的资源&#xff0c;如内存空间和文件句柄。线程有一些基本的属性&#xff0c;如id、name、以及priority。 id&#xff1…

API调试怎么做?Apipost快速上手

前言 Apipost是一款支持 RESTful API、SOAP API、GraphQL API等多种API类型&#xff0c;支持 HTTPS、WebSocket、gRPC多种通信协议的API调试工具。除此之外&#xff0c;Apipost 还提供了自动化测试、团队协作、等多种功能。这些丰富的功能简化了工作流程&#xff0c;提高了研发…

antv/x6_2.0学习使用(四、边)

一、添加边 节点和边都有共同的基类 Cell&#xff0c;除了从 Cell 继承属性外&#xff0c;还支持以下选项。 属性名类型默认值描述sourceTerminalData-源节点或起始点targetTerminalData-目标节点或目标点verticesPoint.PointLike[]-路径点routerRouterData-路由connectorCon…

网络流量分析与故障分析

1.网络流量实时分析 网络监控 也snmp协议 交换机和服务器打开 snmp就ok了 MRTG或者是prgt 用于对网络流量进行实时监测&#xff0c;可以及时了解服务器和交换机的流量&#xff0c;防止因流量过大而导致服务器瘫痪或网络拥塞。 原理 通过snmp监控 是一个…

MES/MOM标准之ISA-95基础内容介绍

ISA-95 简称S95&#xff0c;也有称作SP95。ISA-95 是企业系统与控制系统集成国际标准&#xff0c;由国际自动化学会(ISA&#xff0c;International Society of Automation) 在1995年投票通过。该标准的开发过程是由 ANSI(美国国家标准协会) 监督并保证其过程是正确的。ISA-95不…