Java HTTP 组件库选型看这篇就够了
最近项目需要利用 Java 重度挪用 HTTP API 接口,于是想着封装一个团队公用的 HTTP client lib. 这个库需要支持以下特性:
在 Java 生态中,固然有数不清的 HTTP client lib 组件库,可是概略可以分为这三类:
HttpURLConnection
尺度库;HttpURLConnection
利用 HttpURLConnection 提倡 HTTP 请求最大的利益是不需要引入特另外依赖,可是利用起来很是繁琐,也缺乏毗连池打点、域名机器节制等特性支持。以提倡一个 HTTP POST 请求为例:
import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; public class HttpUrlConnectionDemo { public static void main(String[] args) throws Exception { String urlString = "https://httpbin.org/post"; String bodyString = "password=e10adc3949ba59abbe56e057f20f883e&username=test3"; URL url = new URL(urlString); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setDoOutput(true); OutputStream os = conn.getOutputStream(); os.write(bodyString.getBytes("utf-8")); os.flush(); os.close(); if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { InputStream is = conn.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line); } System.out.println("rsp:" + sb.toString()); } else { System.out.println("rsp code:" + conn.getResponseCode()); } } }
可以看到,利用 HttpURLConnection 提倡 HTTP 请求是较量原始(low level)的,根基上你可以领略为它就是对网络栈传输层(HTTP 一般为 TCP,HTTP over QUIC 是 UDP)举办了一次浅条理的封装,操纵原语就是在打开的毗连上面写请求 request 与读响应 response. 并且 HttpURLConnection 无法支持 HTTP/2. 显然,官方是知道这些问题的,因此在 Java 9 中,官方在尺度库中引入了一个 high level、支持 HTTP/2 的 HttpClient. 这个库的接口封装就很是主流到位了,提倡一个简朴的 POST 请求:
HttpRequest request = HttpRequest.newBuilder() .uri(new URI("https://postman-echo.com/post")) .headers("Content-Type", "text/plain;charset=UTF-8") .POST(HttpRequest.BodyProcessor.fromString("Sample request body")) .build();
封装的最大特点是链式挪用很是顺滑,支持毗连打点等特性。可是这个库只能在 Java 9 及今后的版本利用,Java 9 和 Java 10 并不是 LTS 维护版本,而接下来的 Java 11 LTS 要在2018.09.25宣布,应用到线上还需要期待一段时间。因此,固然挺喜欢这个自带尺度库(究竟可以不引入三方依赖),但当前是无法在出产情况利用的。
Apache HttpComponents HttpClient
Apache HttpComponents HttpClient 的前身是 Apache Commons HttpClient, 可是 Apache Commons HttpClient 已经遏制开拓,假如你还在利用它,请切换到 Apache HttpComponents HttpClient 上来。
Apache HttpComponents HttpClient 支持的特性很是富厚,完全包围我们的需求,利用起来也很是顺手:
<br />import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.fluent.Request; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.RequestBuilder; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.impl.client.HttpClients; public class HttpComponentsDemo { final static CloseableHttpClient client = HttpClients.createDefault(); // 通例挪用 private String sendPostForm(String url, Map<String, String> params) throws Exception { HttpPost request = new HttpPost(url); // set header request.setHeader("X-Http-Demo", HttpComponentsDemo.class.getSimpleName()); // set params if (params != null) { List<NameValuePair> nameValuePairList = new ArrayList<NameValuePair>(); for (Map.Entry<String, String> entry : params.entrySet()) { nameValuePairList.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } UrlEncodedFormEntity bodyEntity = new UrlEncodedFormEntity(nameValuePairList, "UTF-8"); //System.out.println("body:" + IOUtils.toString(bodyEntity.getContent())); request.setEntity(new UrlEncodedFormEntity(nameValuePairList)); } // send request CloseableHttpResponse response = client.execute(request); // read rsp code System.out.println("rsp code:" + response.getStatusLine().getStatusCode()); // return content String ret = readResponseContent(response.getEntity().getContent()); response.close(); return ret; } // fluent 链式挪用 private String sendGet(String url) throws Exception { return Request.Get(url) .connectTimeout(1000) .socketTimeout(1000) .execute().returnContent().asString(); } private String readResponseContent(InputStream inputStream) throws Exception { if (inputStream == null) { return ""; } ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] buf = new byte[512]; int len; while (inputStream.available() > 0) { len = inputStream.read(buf); out.write(buf, 0, len); } return out.toString(); } public static void main(String[] args) throws Exception { HttpComponentsDemo httpUrlConnectionDemo = new HttpComponentsDemo(); String url = "https://httpbin.org/post"; Map<String, String> params = new HashMap<String, String>(); params.put("foo", "bar中文"); String rsp = httpUrlConnectionDemo.sendPostForm(url, params); System.out.println("http post rsp:" + rsp); url = "https://httpbin.org/get"; System.out.println("http get rsp:" + httpUrlConnectionDemo.sendGet(url)); } }