Python提供了urllib和urllib2两个模块可以很方便的实现http服务的调用,无论是post请求还是get请求。目前使用Java开发时,apache提供的httpclient也提供了类似的功能,现整理成文。
一个简单的Http服务调用,需要具备如下对象:
本文提供了2种实现方法,分别依赖httpclient和fluent-hc。
利用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的接口和方法。后续的实现通过实现接口的方式。
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/