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

发布时间:2024年01月09日

前言

微信图片_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 请求的开发工作会有更深入的理解。

文章来源:https://blog.csdn.net/weixin_73077810/article/details/135485549
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。