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

前言

在工作中,往往会遇到需要对接外部接口的业务需求,根据接口文档确定请求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请求的情况,搭配了超时处理机制,同时针对请求参数和请求头参数的配置进行动态配置,满足无参和有参的请求需求。

java 复制代码
/**
 * 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);
        }
    }
}

超时参数配置类

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

kotlin 复制代码
/**
 * 请求超时数据配置类
 * @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;
    }
}

调用例子

typescript 复制代码
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 请求的开发工作会有更深入的理解。

相关推荐
酷讯网络_240870160几秒前
【全开源】Java多语言tiktok跨境商城TikTok内嵌商城送搭建教程
java·开发语言·开源
蓝天星空42 分钟前
spring cloud gateway 3
java·spring cloud
罗政1 小时前
PDF书籍《手写调用链监控APM系统-Java版》第9章 插件与链路的结合:Mysql插件实现
java·mysql·pdf
一根稻草君1 小时前
利用poi写一个工具类导出逐级合并的单元格的Excel(通用)
java·excel
kirito学长-Java1 小时前
springboot/ssm网上宠物店系统Java代码编写web宠物用品商城项目
java·spring boot·后端
海绵波波1071 小时前
flask后端开发(9):ORM模型外键+迁移ORM模型
后端·python·flask
余生H1 小时前
前端Python应用指南(二)深入Flask:理解Flask的应用结构与模块化设计
前端·后端·python·flask·全栈
木头没有瓜1 小时前
ruoyi 请求参数类型不匹配,参数[giftId]要求类型为:‘java.lang.Long‘,但输入值为:‘orderGiftUnionList
android·java·okhttp
奋斗的老史1 小时前
Spring Retry + Redis Watch实现高并发乐观锁
java·redis·spring
high20111 小时前
【Java 基础】-- ArrayList 和 Linkedlist
java·开发语言