apache HttpClient学习笔记

介绍

Python提供了urllib和urllib2两个模块可以很方便的实现http服务的调用,无论是post请求还是get请求。目前使用Java开发时,apache提供的httpclient也提供了类似的功能,现整理成文。

一个简单的Http服务调用,需要具备如下对象:

  • 一个HttpClient对象:用来执行request
  • 一个HttpRequest对象:添加url,请求参数,header等信息,传递为HttpClient进行执行
  • 一个RequestConfig:设置connect,socket等超时时间
  • 一个HttpResponse对象:获取请求的返回结果

本文提供了2种实现方法,分别依赖httpclient和fluent-hc。

gradle依赖

利用gradle加载apache的相关依赖

//build.gradle

dependencies {
    compile("org.apache.httpcomponents:httpclient:4.5.2")
    compile("org.apache.httpcomponents:fluent-hc:4.5.2")
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

HttpClient实现

接口定义

为解耦考虑,我们首先定义一个HttpClient的接口和方法。后续的实现通过实现接口的方式。

package hello.core.httpadapter;
import org.apache.http.Header;
import java.util.Map;

/**
 * Created on 2018/8/13.
 *
 * @author [email protected]
 */
public interface HttpServerAdapter {

    String get(String url) throws Exception;
    String post(String url, Map params) throws Exception;
}

方法一

通过依赖httpclient实现HttpServerAdapter接口中的get和post方法。代码如下:

package hello.core.httpadapter;

import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Created on 2018/8/13.
 *
 * @author [email protected]
 */
public class HttpClientAdapterImpl implements HttpServerAdapter {

    /** timeout 参数定义 */
    private static final int CONNECT_REQUEST_TIMEOUT = 3000;
    private static final int CONNECT_TIMEOUT = 3000;
    private static final int SOCKET_TIMEOUT = 3000;

    /** httpClient定义  */
    private static CloseableHttpClient httpClient;

    /** 使用同一个request设置  */
    private static RequestConfig requestConfig;

    /**
    * 初始化httpclient和requestConfig
    */
    static {
        httpClient = HttpClients.createDefault();
        requestConfig = RequestConfig.custom()
                .setConnectionRequestTimeout(CONNECT_REQUEST_TIMEOUT)
                .setConnectTimeout(CONNECT_TIMEOUT)
                .setSocketTimeout(SOCKET_TIMEOUT).build();
    }

    @Override
    public String get(String url) throws Exception {
        HttpGet httpGet = new HttpGet(url);
        httpGet.setConfig(requestConfig);
        return doQuery(httpGet);
    }

    @Override
    public String post(String url, Map params) throws Exception {
        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(map2NameValuePair(params), Consts.UTF_8);
        HttpPost httpPost = new HttpPost(url);
        httpPost.setEntity(entity);
        httpPost.setConfig(requestConfig);
        return doQuery(httpPost);
    }

    /**
     * 执行request获取返回结果的字符串内容
     *     官方文档中提出response和entity的close操作,需注意。
     *
     * @param requestBase 请求
     * @return a string
     */
    private String doQuery(HttpRequestBase requestBase) {
        CloseableHttpResponse response = null;
        HttpEntity entity = null;
        try {
            try {
                response = httpClient.execute(requestBase);
                entity = response.getEntity();
                if (entity != null) {
                    return EntityUtils.toString(entity, Consts.UTF_8);
                }
            } catch (IOException io) {
                LOG.error(io.getMessage());
            } finally {
                EntityUtils.consume(entity);
                if (response != null) {
                    response.close();
                }
            }
        } catch (Exception e) {
            LOG.error(e.getMessage());
        }
        return null;
    }

    /**
     * 参数转换为NameValuePair
     *
     * @param params 请求参数
     * @return NameValuePair集合
     */
    private List map2NameValuePair(Map params) {
        List pairs = new ArrayList<>();
        if (params == null) {
            return pairs;
        }
        for (Map.Entry item : params.entrySet()) {
            pairs.add(new BasicNameValuePair(item.getKey(), item.getValue()));
        }
        return pairs;
    }

    private static final Logger LOG = LoggerFactory.getLogger(HttpClientAdapterImpl.class);

}

方法二

通过依赖fluent-hc实现HttpServerAdapter接口中的get和post方法。代码如下:

package hello.core.httpadapter;

import org.apache.http.*;
import org.apache.http.client.fluent.Request;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 利用org.apache.http.client.fluent.Request实现POST和GET
 *
 * Created on 2018/8/13.
 *
 * @author [email protected]
 */
public class HttpServerAdapterImpl implements HttpServerAdapter {

    @Override
    public String get(String url) throws Exception {
        return doQuery(Request.Get(url));
    }

    @Override
    public String post(String url, Map params) throws Exception {
        return doQuery(Request.Post(url).bodyForm(map2NameValuePair(params), Consts.UTF_8));
    }

    /**
     * 执行request获取返回结果的字符串内容
     *
     * @param request 请求
     * @return a string
     */
    private String doQuery(Request request) {
        String result = "";
        HttpEntity entity = null;
        try {
            try {
                HttpResponse response = request.socketTimeout(STOCK_TIMEOUT).connectTimeout(CONNECT_TIMEOUT).
                        execute().returnResponse();
                entity = response.getEntity();
                if (entity != null) {
                    result = EntityUtils.toString(entity, Consts.UTF_8);
                }
            } catch (IOException io) {
                LOG.error(io.getMessage());
            } finally {
                EntityUtils.consume(entity);
            }
        } catch (IOException ex) {
            LOG.error(ex.getMessage());
        }
        return result;
    }

    /**
     * 参数转换为NameValuePair
     *  TODO: 函数冗余,提取到common
     *
     * @param params 请求参数
     * @return NameValuePair集合
     */
    private List map2NameValuePair(Map params) {
        List pairs = new ArrayList<>();
        if (params == null) {
            return pairs;
        }
        for (Map.Entry item : params.entrySet()) {
            pairs.add(new BasicNameValuePair(item.getKey(), item.getValue()));
        }
        return pairs;
    }

    /** timeout 设置 */
    private static final int STOCK_TIMEOUT = 3000;
    private static final int CONNECT_TIMEOUT = 3000;
    private static final Logger LOG = LoggerFactory.getLogger(HttpServerAdapterImpl.class);
}

单测

根据上述内容我们已经实现的简单的http服务调用,现在来测试下代码是否可行。构造单元测试如下:

package hello.core.httpadapter;

import org.junit.Assert;
import org.junit.Test;

/**
 * HttpServerAdapter单测
 *
 * Created on 2018/8/13.
 *
 * @author [email protected]
 */
public class HttpServerAdapterTest {

    /** server */
    private HttpServerAdapter serverAdapter = new HttpServerAdapterImpl();

    /** client */
    private HttpServerAdapter clientAdapter = new HttpClientAdapterImpl();

    @Test
    public void get() throws Exception {
        String url = "https://hc.apache.org/httpcomponents-client-ga/examples.html";
        String result = serverAdapter.get(url);
        System.out.println("server get request, get result size: " + result.length());

        String r2 = clientAdapter.get(url);
        Assert.assertNotNull(r2);
        System.out.println("client get request, get result size: " + result.length());
    }

    @Test
    public void post() throws Exception {
        String url = "https://hc.apache.org/httpcomponents-client-ga/examples.html";
        String result = serverAdapter.post(url, null);
        Assert.assertNotNull(result);
        System.out.println("server post request, post result size: " + result.length());

        String r2 = clientAdapter.post(url, null);
        Assert.assertNotNull(r2);
        System.out.println("client post request, post result size: " + result.length());
    }

}

输出结果

//get
server get request, get result size: 15362
client get request, get result size: 15362

// post
server post request, post result size: 15362
client post request, post result size: 15362

总结

apache提供的httpclient很方面的实现http服务的调用,对比httpclient和fluent-hc的实现,fluent-hc的实现更加简单明了。此处只是介绍了http服务调用的简单实现,更多的细节和问题还需要在实际使用的发现,如多线程的调用,安全性问题,请求头,代理设置等。

参数文献

https://hc.apache.org/httpcomponents-client-ga/

你可能感兴趣的:(Java开发笔记)