在SpringBoot中如何使用HttpClient实现HTTP请求
作者:sco5282
SpringBoot使用HttpClient实现HTTP请求
越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。虽然在 JDK 的 java.net 包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。
HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。
新建一个 SpringBoot
工程,引入 httpclient
的 POM 依赖:
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.6</version> </dependency>
总结:
public class HttpClientUtil { // GET请求 public static String get(String url, JSONObject params) { CloseableHttpClient httpClient = HttpClientBuilder.create().build(); String sendUrl = url; // 拼接参数 if (Objects.nonNull(params) && params.size() > 0) { sendUrl = connectParams(url, params); } HttpGet httpGet = new HttpGet(sendUrl); CloseableHttpResponse response = null; try { response = httpClient.execute(httpGet); HttpEntity httpEntity = response.getEntity(); if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode() && null != httpEntity) { return EntityUtils.toString(httpEntity); } } catch (IOException e) { e.printStackTrace(); } finally { try { close(httpClient, response); } catch (IOException e) { e.printStackTrace(); } } throw new JeecgBootException("调用GET请求失败!"); } /** * @Description: POST 请求 * @Author: zzc * @Date: 2022-12-16 16:48 * @param url: * @param params: * @param requestBody: json 串 * @return: java.lang.String **/ public static String post(String url, JSONObject params, String requestBody) { CloseableHttpClient httpClient = HttpClientBuilder.create().build(); String sendUrl = url; // 1.拼接参数 if (Objects.nonNull(params) && params.size() > 0) { sendUrl = connectParams(url, params); } HttpPost httpPost = new HttpPost(sendUrl); httpPost.setHeader("Content-Type", "application/json;charset=utf8"); CloseableHttpResponse response = null; try { // 2.设置request-body if (StringUtils.isNotBlank(requestBody)) { ByteArrayEntity entity = new ByteArrayEntity(requestBody.getBytes(StandardCharsets.UTF_8)); entity.setContentType("application/json"); httpPost.setEntity(entity); } response = httpClient.execute(httpPost); HttpEntity httpEntity = response.getEntity(); if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode() && null != httpEntity) { return EntityUtils.toString(httpEntity); } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { close(httpClient, response); } catch (IOException e) { e.printStackTrace(); } } throw new JeecgBootException("调用POST请求失败!"); } private static String connectParams(String url, JSONObject params) { StringBuffer buffer = new StringBuffer(); buffer.append(url).append("?"); params.forEach((x, y) -> buffer.append(x).append("=").append(y).append("&")); buffer.deleteCharAt(buffer.length() - 1); return buffer.toString(); } public static void close(CloseableHttpClient httpClient, CloseableHttpResponse httpResponse) throws IOException{ if (null != httpClient) { httpClient.close(); } if (null != httpResponse) { httpResponse.close(); } } }
详细使用示例
GET 无参
调用接口:
http://localhost:8080/http/listUsers
没有入参。
HttpClientController#doGetNoParams()
:GET请求接口不带参数
@RestController @RequestMapping("/httpClient") public class HttpClientController { @Autowired private HttpClientService httpClientService; // GET请求接口不带参数 @GetMapping("/doGetNoParams") public String doGetNoParams() { return httpClientService.doGetNoParams(); } }
HttpClientServiceImpl#doGetNoParams()
:GET 请求接口不带参数
@Slf4j @Service public class HttpClientServiceImpl implements HttpClientService { // GET请求接口不带参数 @Override public String doGetNoParams() { String result = HttpClientUtil.doGetNoParams(); log.info("【发送GET请求】返回结果为:{}", result); if (!StringUtil.isBlank(result)) { TaskCenterUtil taskCenterUtil = TaskCenterUtil.getTaskCenterUtil(); taskCenterUtil.submitTask(() -> { log.info("【子线程】开启了一个新线程,当前线程名为:{}", Thread.currentThread().getName()); return null; }); } log.info("【主线程】当前线程名为:{}", Thread.currentThread().getName()); return result; } }
说明:
- 当 HTTP 调用成功后,通过线程池开一个子线程,去异步执行任务;主线程继续向下执行
HttpClientUtil#doGetNoParams()
:GET请求接口不带参数
@Slf4j public class HttpClientUtil { // 服务器ip public static final String IP = "http://localhost"; // 端口 public static final String PORT = ":8080"; // GET请求接口不带参数 public static final String GET_URL_NO_PARAMS = IP + PORT + "/http/listUsers"; // GET请求接口不带参数 public static String doGetNoParams() { CloseableHttpClient httpClient = HttpClientBuilder.create().build(); // 创建 GET 请求 HttpGet httpGet = new HttpGet(GET_URL_NO_PARAMS); httpGet.setHeader("Accept-Encoding", "identity"); log.info("【发送GET请求】请求地址为:{}", GET_URL_NO_PARAMS); CloseableHttpResponse httpResponse = null; try { httpResponse = httpClient.execute(httpGet); HttpEntity httpEntity = httpResponse.getEntity(); log.info("【发送GET请求】成功,相应状态为:{}", httpResponse.getStatusLine()); if (HttpStatus.SC_OK == httpResponse.getStatusLine().getStatusCode() && null != httpEntity) { String result = EntityUtils.toString(httpEntity); log.info("【发送GET请求】成功,响应内容为:{}", result); return result; } } catch (IOException e) { log.error("【发送GET请求】失败,执行发送请求时,出现IO异常,异常信息为:{}", e); return null; } finally { try { close(httpClient, httpResponse); } catch (IOException e) { log.error("【发送GET请求】失败,关闭流时,出现IO异常,异常信息为:{}", e); } } return null; } }
说明:
- 通过类
HttpGet
发起 GET 请求。
HttpGet
:有 3 个构造方法
public class HttpGet extends HttpRequestBase { public HttpGet() { } public HttpGet(URI uri) { this.setURI(uri); } public HttpGet(String uri) { this.setURI(URI.create(uri)); } ... }
这里使用了 public HttpGet(String uri);
方式来构造 HttpGet
实例。
HttpClientUtil#close()
:关闭流
// 关闭流 public static void close(CloseableHttpClient httpClient, CloseableHttpResponse httpResponse) throws IOException{ if (null != httpClient) { httpClient.close(); } if (null != httpResponse) { httpResponse.close(); } }
TaskCenterUtil
:线程池工具类
public class TaskCenterUtil { public static Integer CORE_POOL_SIZE = 10; public static Integer MAX_NUM_POOL_SIZE = 10; public static Integer MAX_MESSAGE_SIZE = 100; public static Long KEEP_ALIVE_TIME = 60L; private ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_NUM_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS, new LinkedBlockingQueue<>(MAX_MESSAGE_SIZE), new ThreadPoolExecutor.CallerRunsPolicy()); private TaskCenterUtil() {} private static TaskCenterUtil taskCenterUtil = new TaskCenterUtil(); public static TaskCenterUtil getTaskCenterUtil() { return taskCenterUtil; } // 提交任务 public void submitTask(Callable task) { poolExecutor.submit(task); } }
POSTMAN 调用:
控制台打印日志:
GET 有参
- 方式一:使用
public HttpGet(String uri);
方式来构造HttpGet
实例。即:使用 url 字符串来拼接参数。 - 方式二:使用
public HttpGet(URI uri);
方式来构造HttpGet
实例。
调用接口:
http://localhost:8080/http/getUserById?id=1
入参:
id=1
HttpClientController#doGetParams()
:GET请求接口带参数
@GetMapping("/doGetParams") public String doGetParams(String id) { return httpClientService.doGetParams(id); }
HttpClientServiceImpl
:
@Override public String doGetParams(String id) { String result = HttpClientUtil.doGetParams(id); log.info("【发送GET请求】返回结果为:{}", result); return result; }
HttpClientUtil#doGetParams()
:GET请求接口带参数
@Slf4j public class HttpClientUtil { // GET请求接口带参数 public static final String GET_URL_PARAMS = IP + PORT + "/http/getUserById"; // 入参名称 public static final String URL_PARAMS_ID = "id"; // http 协议 public static final String SCHEME_HTTP = "http"; // 主机 public static final String LOCAL_HOST = "localhost"; // 请求接口路径 public static final String GET_URL_PARAMS_PATH = "/http/getUserById"; // 端口 public static final Integer LOCAL_PORT = 8080; // GET请求接口带参数 public static String doGetParams(String id) { CloseableHttpClient httpClient = HttpClientBuilder.create().build(); // 不同方式获取 HttpGet // 方式一: HttpGet httpGet = getStrHttpGet(GET_URL_PARAMS, id); // 方式二: //HttpGet httpGet = getUrlHttpGet(id); // 获取请求头配置信息 RequestConfig requestConfig = HttpClientConfig.getRequestConfig(); httpGet.setConfig(requestConfig); CloseableHttpResponse response = null; try { response = httpClient.execute(httpGet); HttpEntity httpEntity = response.getEntity(); log.info("【发送GET请求】成功,相应状态为:{}", response.getStatusLine()); if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode() && null != httpEntity) { String result = EntityUtils.toString(httpEntity); log.info("【发送GET请求】成功,响应内容为:{}", result); return result; } } catch (IOException e) { log.error("【发送GET请求】失败,执行发送请求时,出现IO异常,异常信息为:{}", e); return null; } finally { try { close(httpClient, response); } catch (IOException e) { log.error("【发送GET请求】失败,关闭流时,出现IO异常,异常信息为:{}", e); } } return null; } }
getStrHttpGet()
:方式一:url拼接参数
public static HttpGet getStrHttpGet(String url, String id) { StringBuilder builder = new StringBuilder(); // url 拼接参数 /http/getUserById?id=1 String strUrl = builder.append(url).append("?").append(URL_PARAMS_ID).append("=").append(id).toString(); log.info("【发送GET请求】请求地址为:{}", strUrl); HttpGet httpGet = new HttpGet(strUrl); return httpGet; }
getUrlHttpGet()
:方式二:URI对象
public static HttpGet getUrlHttpGet(String id) { // 将参数键值对放入集合中 List<NameValuePair> params = new ArrayList<>(); params.add(new BasicNameValuePair(URL_PARAMS_ID, id)); try { URI uri = new URIBuilder() .setScheme(SCHEME_HTTP) .setHost(LOCAL_HOST) .setPort(LOCAL_PORT) .setPath(GET_URL_PARAMS_PATH) .setParameters(params).build(); return new HttpGet(uri); } catch (URISyntaxException e) { log.error("【发送GET请求】构建URI失败,失败信息为:{}", e); } return null; }
说明:
- 方式一是使用
public HttpGet(String uri);
方式来构造HttpGet
实例 - 方式二是使用
public HttpGet(URL uri);
方式来构造HttpGet
实例。即:使用 url 字符串来拼接参数。
POSTMAN 调用:
POST 无参
调用接口:
http://localhost:8080/http/listUserList
入参:无
HttpClientController#doPostNoParams()
:POST请求接口不带参数
@PostMapping("/doPostNoParams") public String doPostNoParams() { return httpClientService.doPostNoParams(); }
HttpClientServiceImpl#doPostNoParams()
:
@Override public String doPostNoParams() { String result = HttpClientUtil.doPostNoParams(); log.info("【发送POST请求】返回结果为:{}", result); return result; }
HttpClientUtil
:
public class HttpClientUtil { // POST请求接口不带参数 public static final String POST_URL_NO_PARAMS = IP + PORT + "/http/listUserList"; // POST请求接口带参数 public static final String POST_URL_PARAMS = IP + PORT + "/http/getUserVoById"; // POST请求接口带参数 -- 对象参数 public static final String POST_URL_PARAMS_OBJECT = IP + PORT + "/http/listUsers"; // POST请求接口不带参数 public static String doPostNoParams() { CloseableHttpClient httpClient = HttpClientBuilder.create().build(); HttpPost httpPost = new HttpPost(POST_URL_NO_PARAMS); log.info("【发送POST请求】请求地址为:{}", POST_URL_NO_PARAMS); CloseableHttpResponse response = null; try { response = httpClient.execute(httpPost); HttpEntity httpEntity = response.getEntity(); log.info("【发送POST请求】成功,相应状态为:{}", response.getStatusLine()); if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode() && null != httpEntity) { String result = EntityUtils.toString(httpEntity); log.info("【发送POST请求】成功,响应内容为:{}", result); return result; } } catch (IOException e) { log.error("【发送POST请求】失败,执行发送请求时,出现IO异常,异常信息为:{}", e); return null; } finally { try { close(httpClient, response); } catch (IOException e) { log.error("【发送POST请求】失败,关闭流时,出现IO异常,异常信息为:{}", e); } } return null; } }
POST 有参
- 参数是:普通参数。方式与GET一样即可,直接在 url 后缀上拼接参数
- 参数是:对象。将参数以请求体 request-body 的方式进行请求
- 参数是:普通参数+对象。普通参数 直接在 url 后缀上拼接参数;对象 以请求体 request-body 的方式进行请求
普通参数
请求接口:
http://localhost:8080/http/getUserVoById?id=1
对象参数
请求接口:
http://localhost:8080/http/listUsers
入参 UserVo:
{ "id": 1 }
即:这个接口可以随便写
@PostMapping("/listUsers") public List<UserVo> listUsers(@RequestBody UserVo userVo) { return httpService.listUsers(); }
HttpClientController#doPostParams()
:POST请求接口带参数
@PostMapping("/doPostParams") public String doPostParams(String id) { return httpClientService.doPostParams(id); }
HttpClientServiceImpl#doPostParams()
:
@Override public String doPostParams(String id) { String result = HttpClientUtil.doPostParams(id); log.info("【发送POST请求】返回结果为:{}", result); return result; }
HttpClientUtil#doPostParams()
:
public static String doPostParams(String id) { CloseableHttpClient httpClient = HttpClientBuilder.create().build(); // 参数是普通参数 HttpPost httpPost = getStrHttpPost(POST_URL_PARAMS, id); // 参数是对象 //HttpPost httpPost = getObjectHttpPost(id); // 设置ContentType(注:如果只是传普通参数的话,ContentType不一定非要用application/json) httpPost.setHeader("Content-Type", "application/json;charset=utf8"); CloseableHttpResponse response = null; try { response = httpClient.execute(httpPost); HttpEntity httpEntity = response.getEntity(); log.info("【发送POST请求】成功,相应状态为:{}", response.getStatusLine()); if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode() && null != httpEntity) { String result = EntityUtils.toString(httpEntity); log.info("【发送POST请求】成功,响应内容为:{}", result); return result; } } catch (IOException e) { log.error("【发送POST请求】失败,执行发送请求时,出现IO异常,异常信息为:{}", e); return null; } finally { try { close(httpClient, response); } catch (IOException e) { log.error("【发送POST请求】失败,关闭流时,出现IO异常,异常信息为:{}", e); } } return null; }
getStrHttpPost()
:POST请求有参:普通参数
public static HttpPost getStrHttpPost(String url, String id) { StringBuilder builder = new StringBuilder(); // url 拼接参数 /http/getUserVoById?id=1 String strUrl = builder.append(url).append("?").append(URL_PARAMS_ID).append("=").append(id).toString(); log.info("【发送POST请求】请求地址为:{}", strUrl); HttpPost httpPost = new HttpPost(strUrl); return httpPost; }
getObjectHttpPost()
:POST请求有参:对象参数
public static HttpPost getObjectHttpPost(String id) { HttpPost httpPost = new HttpPost(POST_URL_PARAMS_OBJECT); log.info("【发送POST请求】请求地址为:{}", POST_URL_PARAMS_OBJECT); UserVo userVo = new UserVo(); userVo.setId(id); // 将JAVA对象转换为Json字符串 String jsonString = JSON.toJSONString(userVo); StringEntity stringEntity = new StringEntity(jsonString, "UTF-8"); // post请求是将参数放在请求体里面传过去的 httpPost.setEntity(stringEntity); return httpPost; }
普通参数 + 对象
// params:name=zzc&age=17 marshal:json 串 public static String post(String url, String params, String marshal) { CloseableHttpClient httpClient = HttpClientBuilder.create().build(); String strUrl = url + "?" + params; HttpPost httpPost = new HttpPost(strUrl); httpPost.setHeader("Content-Type", "application/json;charset=utf8"); CloseableHttpResponse response = null; try { // 设置 requst-body 参数 ByteArrayEntity entity = new ByteArrayEntity(marshal.getBytes("UTF-8")); entity.setContentType("application/json"); httpPost.setEntity(entity); response = httpClient.execute(httpPost); HttpEntity httpEntity = response.getEntity(); if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode() && null != httpEntity) { String result = EntityUtils.toString(httpEntity); return result; } } catch (Exception e) { e.printStackTrace(); } finally { try { close(httpClient, response); } catch (IOException e) { e.printStackTrace(); } } return null; }
表单提交
public static String post(String url, Map<String, String> params) { CloseableHttpClient httpClient = HttpClientBuilder.create().build(); HttpPost httpPost = new HttpPost(url); httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded"); // 参数 List<NameValuePair> nameValuePairs = new ArrayList<>(); if (MapUtils.isNotEmpty(params)) { params.forEach((x, y) -> { nameValuePairs.add(new BasicNameValuePair(x, y)); }); } CloseableHttpResponse response = null; try { httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs)); response = httpClient.execute(httpPost); HttpEntity httpEntity = response.getEntity(); if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode() && null != httpEntity) { return EntityUtils.toString(httpEntity); } } catch (IOException e) { e.printStackTrace(); } finally { try { close(httpClient, response); } catch (IOException e) { e.printStackTrace(); } } throw new JeecgBootException("调用accessToken API失败"); }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。