前言
在工作中,往往会遇到需要对接外部接口的业务需求,根据接口文档确定请求IP以及相关的参数数据需求后,通过HttpClient进行第三方外部接口的调用。在这种业务中,也要考虑好请求超时导致的接口报错的处理。为什么呢?就我公司业务而言,我们这边的视频或者说融合通讯,因为厂商多,设备型号杂,经常都有调用不通的情况。今天正常的视频可能明天就不行了,如果后端这里不处理超时,前端如果无法拿到设备信息或者设备ID,页面的报错信息可能反应的就是接口报错了。这显然是不合情理的,为此,我们可以考虑加一个请求超时的参数(在配置文件中读取自定义参数) ,控制一下。本文就HttpClient做一个简单的快速入门,并搭配好我编写的一个完整工具类,希望可以帮助大家在第三方接口调用业务中有所帮助。
实现思路步骤
-
创建
HttpClient
对象,可以使用HttpClients.createDefault()
方法。 -
确定请求方法类型
- 如果是无参数的 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对象。
- 如果是无参数的 GET 请求,则直接使用构造方法
-
加载配置类中的超时配置数据,配置在
RequestConfig
对象中,将该对象配置到具体的HttpPost / HttpGet 对象里,设置好超时规则 -
创建
HttpResponse
,调用 HttpClient 对象的execute(HttpUriRequest request)
方法发送请求,该方法返回一个HttpResponse
。调用 HttpResponse 的getAllHeaders()
、getHeaders(String name)
等方法可以获取服务器的响应头;调用 HttpResponse 的getEntity()
方法可以获取HttpEntity
对象,该对象包装了服务器的响应内容。程序可以通过该对象获取服务器的响应内容。通过调用getStatusLine().getStatusCode()
可以获取响应状态码。 -
通过
e instanceof SocketTimeoutException
和e 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 请求的开发工作会有更深入的理解。