【SpringBoot整合系列】HttpClient远程访问的示例
前言
- 使用Apache的HttpClient库,添加Apache HttpClient的依赖。
- 工具类的封装。通常,工具类需要处理GET、POST请求,可能还有其他方法如PUT、DELETE。需要设计一个工具类,提供静态方法,可以发送请求,并处理响应。同时要考虑连接超时、读取超时的设置,以及请求头的配置,比如Content-Type、User-Agent等
- 然后,需要处理请求参数的封装。对于GET请求,参数通常是查询字符串;对于POST,可能需要表单参数或者JSON body。因此,工具类可能需要支持不同的参数类型,比如使用Map<String, String>来传递表单参数,或者直接传入一个JSON字符串作为请求体。
- 异常处理也是关键。网络请求可能会抛出IOException等异常,工具类需要捕获这些异常,并可能转换成自定义异常,或者返回null,或者记录日志,具体取决于设计选择。需要灵活处理错误,所以可能需要将异常抛出让调用者处理,或者在工具类内部处理并返回错误信息。
- 另外,响应处理方面,需要将HttpEntity转换为字符串,并正确关闭连接,确保资源释放。使用EntityUtils.toString时要注意字符编码,通常使用UTF-8。同时,需要关闭HttpResponse和HttpClient,或者使用try-with-resources来自动管理资源,但Apache HttpClient 4.x版本的response关闭可能需要注意,需要确保正确关闭流。
注意事项
-
工具类可能包含sendGet和sendPost方法。例如,sendGet接收URL和参数Map,构造带查询参数的URL;sendPost接收URL和可能的表单参数或JSON body,并设置合适的Content-Type。此外,可能需要设置请求头,比如设置JSON的Content-Type为application/json。
-
工具类的设计要尽量简洁,可配置。比如,可以允许自定义连接超时时间、读取超时时间,或者设置默认的请求头。可能使用HttpClient的配置,比如RequestConfig来设置超时参数。
-
可能的陷阱包括:未正确处理参数编码,比如在GET请求中,参数需要进行URL编码;在POST发送表单时,需要将参数转换为UrlEncodedFormEntity;发送JSON时,要使用StringEntity,并设置Content-Type为application/json。
-
需要注意线程安全,HttpClient通常建议重用实例,所以工具类中可能使用静态的CloseableHttpClient实例,并通过静态代码块初始化,确保线程安全。但需确认是否每次请求需要新的实例,或者可以共享。根据Apache HttpClient文档,建议重复使用同一个实例,因为它是线程安全的,并且管理连接池更高效。所以工具类中可以创建一个单例的HttpClient实例。
-
另外,工具类可能需要处理不同的Content-Type,比如POST发送JSON时,需要设置对应的头信息,并处理不同的数据体。因此,sendPost可能需要参数来指定发送的数据类型,或者通过方法重载来处理不同情况。
-
例如,提供sendPostForm用于发送表单,sendPostJson用于发送JSON。或者,在方法中传递一个contentType参数,并根据类型构造不同的HttpEntity。
-
或者,更灵活的方式是让调用者传入一个HttpEntity对象,这样工具类更通用,但使用起来可能不够方便。所以需要在易用性和灵活性之间权衡。
-
这里工具类可以提供两个方法:一个处理表单参数(Map类型),另一个处理JSON字符串。
-
整体思路
-
引入Apache HttpClient依赖。
-
创建HttpClientUtil类,封装创建HttpClient实例的方法,配置超时等参数。
-
实现sendGet方法,处理参数拼接和编码。
-
实现sendPost方法,处理表单或JSON数据的提交。
-
处理响应,将结果转换为字符串。
-
异常处理,关闭资源。
-
提供使用示例。
代码示例
Maven依赖
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
HttpClientUtil
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.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
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 java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class HttpClientUtil {
// 超时配置(单位:毫秒)
private static final int CONNECT_TIMEOUT = 5000;
private static final int SOCKET_TIMEOUT = 5000;
// 创建可复用的HttpClient实例
private static final CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectTimeout(CONNECT_TIMEOUT)
.setSocketTimeout(SOCKET_TIMEOUT)
.build())
.build();
/**
* 发送GET请求
* @param url 请求地址
* @param params 请求参数
* @return 响应内容
*/
public static String sendGet(String url, Map<String, String> params) {
HttpGet httpGet = new HttpGet(buildUri(url, params));
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
return handleResponse(response);
} catch (IOException e) {
throw new RuntimeException("GET请求失败", e);
}
}
/**
* 发送POST表单请求(application/x-www-form-urlencoded)
* @param url 请求地址
* @param formData 表单数据
* @return 响应内容
*/
public static String sendPostForm(String url, Map<String, String> formData) {
HttpPost httpPost = new HttpPost(url);
List<NameValuePair> params = new ArrayList<>();
formData.forEach((k, v) -> params.add(new BasicNameValuePair(k, v)));
httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
return executePost(httpPost);
}
/**
* 发送POST JSON请求(application/json)
* @param url 请求地址
* @param json JSON字符串
* @return 响应内容
*/
public static String sendPostJson(String url, String json) {
HttpPost httpPost = new HttpPost(url);
StringEntity entity = new StringEntity(json, "UTF-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
return executePost(httpPost);
}
private static String executePost(HttpPost httpPost) {
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
return handleResponse(response);
} catch (IOException e) {
throw new RuntimeException("POST请求失败", e);
}
}
private static URI buildUri(String url, Map<String, String> params) {
try {
URIBuilder uriBuilder = new URIBuilder(url);
if (params != null) {
params.forEach(uriBuilder::addParameter);
}
return uriBuilder.build();
} catch (URISyntaxException e) {
throw new IllegalArgumentException("URL格式错误", e);
}
}
private static String handleResponse(CloseableHttpResponse response) throws IOException {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != 200) {
throw new RuntimeException("HTTP响应异常,状态码:" + statusCode);
}
HttpEntity entity = response.getEntity();
return entity != null ? EntityUtils.toString(entity, "UTF-8") : null;
}
}
使用示例
import java.util.HashMap;
import java.util.Map;
public class Example {
public static void main(String[] args) {
// GET请求示例
String getUrl = "https://api.example.com/data";
Map<String, String> params = new HashMap<>();
params.put("key1", "value1");
params.put("key2", "value2");
String getResponse = HttpClientUtil.sendGet(getUrl, params);
System.out.println("GET响应内容:" + getResponse);
// POST表单示例
String postFormUrl = "https://api.example.com/submit";
Map<String, String> formData = new HashMap<>();
formData.put("username", "testuser");
formData.put("password", "testpass");
String postFormResponse = HttpClientUtil.sendPostForm(postFormUrl, formData);
System.out.println("POST表单响应:" + postFormResponse);
// POST JSON示例
String postJsonUrl = "https://api.example.com/submit-json";
String jsonPayload = "{\"name\":\"John\", \"age\":30}";
String postJsonResponse = HttpClientUtil.sendPostJson(postJsonUrl, jsonPayload);
System.out.println("POST JSON响应:" + postJsonResponse);
}
}
总结
工具类特点:
- 线程安全: 使用单个共享的HttpClient实例,复用连接池资源
- 超时配置: 统一设置5秒连接/读取超时
- 编码处理: 统一使用UTF-8编码
- 异常处理: 将检查异常转换为运行时异常,简化调用
- 支持多种请求:
- GET请求(带查询参数)
- POST表单(application/x-www-form-urlencoded)
- POST JSON(application/json)
- 响应处理: 自动验证状态码并转换响应内容
注意事项:
- 根据实际情况调整超时时间配置
- 生产环境建议添加日志记录
- 需要处理其他状态码(如3xx重定向)时可扩展handleResponse方法
- 如需添加自定义请求头,可扩展方法参数
- 注意及时释放资源,使用try-with-resources自动关闭响应
可根据实际需求进一步扩展支持:
- 文件上传
- HTTPS配置
- 代理设置
- 请求重试机制
- 更完善的异常处理等