Apache HttpClient

简介

HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,比如 Apache Jakarta 上很著名的另外两个开源项目 Cactus 和 HTMLUnit 都使用了 HttpClient。

​ 使用HttpClient、可以灵活地发送HTTP请求,并且Apache HttpClient提供了很多自定义的请求内容的API(包括连接超时时间设置等)。方便了开发人员测试接口(基于HTTP协议的),既提高了开发效率,也方便了提高代码的健壮性。(后续的RestTemplate的底层也是HttpClient)因此熟悉掌握HttpClient是很重要的必修内容,掌握了HttpClient后,对于HTTP的协议的了解也会更加深刻。


Apache HttpClient特性

  • 基于标准,纯净的Java语言。实现了HTTP1.0和HTTP1.1
  • 以可扩展的面向对象的结构实现了 HTTP 全部的方法(GET, POST, PUT, DELETE, HEAD, OPTIONS, and TRACE)。
  • 支持 HTTPS 协议。
  • 通过 HTTP 代理建立透明的连接。
  • 利用 CONNECT 方法通过 HTTP 代理建立隧道的 HTTPS 连接。
  • Basic,Digest,NTLMv1,NTLMv2,NTLM2Session,SNPNEGO/Kerberos 认证方案。
  • 插件式的自定义认证方案。
  • 便携可靠的套接字工厂使它更容易的使用第三方解决方案。
  • 连接管理器支持多线程应用。支持设置最大连接数,同时支持设置每个主机的最大连接数,发现并关闭过期的连接。
  • 自动处理 Set-Cookie 中的 Cookie。
  • 插件式的自定义 Cookie 策略。
  • Request 的输出流可以避免流中内容直接缓冲到 Socket 服务器。
  • Response 的输入流可以有效的从 Socket 服务器直接读取相应内容。
  • 在 HTTP 1.0 和 HTTP 1.1 中利用 KeepAlive 保持持久连接。
  • 直接获取服务器发送的 response code 和 headers。
  • 设置连接超时的能力。
  • 实验性的支持 HTTP 1.1 response caching。

使用流程

  • 导入依赖

    <dependencies>
    <dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
    </dependency>
    <dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpmime</artifactId>
    <version>4.5.13</version>
    </dependency>
    </dependencies>
  • 创建HttpClient对象(http客户端对象)

    CloseableHttpClient client = HttpClientBuilder.create().build();//方式一
    CloseableHttpClient client = HttpClients.createDefault();//方式二

    两种方式相同,因为方式二的底层就是方式一。

  • 创建请求方法的实例,并指定URL。因为HttpClient是实现了所有Http请求方法,我们只需要创建请求方法实例(Httpxxx),在方法实例中指定URL即可,例如我们要发送Get请求,就创建HttpGet对象。

  • 如果需要发送带有参数的请求,可调用HttpGet、HttpPost共同的setParam(HttpParams params)来添加参数(该方法已经过时/弃用了);HttpGet推荐使用单独创建uri,构建自己的带参数的get请求的uri。HttpPost请求则是调用setEntity(HttpEntity entity)方法来设置请求参数(可以设置对象)。

  • 发送请求则是HttpClient的工作,调用HttpClientexecute(HttpUriRequeset request)发送请求,该方法会返回一个HttpResponse(响应模板)

  • 调用HttpResponsegetAllHeader()getHeaders(Sring name)等方法可以获取服务器的响应头,除此之外还可以获取状态码等。通过HttpResponsegetEntity()方法可以获取响应的响应实体,该对象包装了服务器的响应内容,程序可以通过该对象获取服务器的响应内容。

  • 最后需要释放资源,关闭HttpClientreseponse的资源连接

使用实例

GET请求

public class GetClient {
/**
* GET---无参测试
*/
public void doGetTestOne(){
//获得http客户端
CloseableHttpClient client = HttpClientBuilder.create().build();
//创建get请求
HttpGet httpGet = new HttpGet("https://www.cuit.edu.cn/");
//响应模型
CloseableHttpResponse response = null;
try{
//有客户端指定get请求
response = client.execute(httpGet);
//从响应模型中获取响应体
HttpEntity responseEntity = response.getEntity();
System.out.println("响应状态为:"+response.getStatusLine());
if (responseEntity!=null){
System.out.println("响应内容长度为:"+responseEntity.getContentLength());
System.out.println("响应内容为:"+ EntityUtils.toString(responseEntity));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try{
//释放资源
if(client!=null){
client.close();
}
if (response!=null){
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

/**
* GET--有参测试
*/
public void doGetTestWayOne(){
//获得http客户端
CloseableHttpClient client = HttpClientBuilder.create().build();

//参数
StringBuilder params = new StringBuilder();
try{
params.append("name=").append(URLEncoder.encode("&","utf-8")).append("&").append("age=24");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}

//创建Get请求
HttpGet httpGet = new HttpGet("https://www.cuit.edu.cn/?" + params);
//响应模型
CloseableHttpResponse response = null;
try{
//配置信息
RequestConfig config = RequestConfig.custom()
//设置连接超时时间
.setConnectTimeout(5000)
//设置请求超时时间
.setConnectionRequestTimeout(5000)
//socket读写超时时间
.setSocketTimeout(5000)
//设置是否允许重定向(默认为true)
.setRedirectsEnabled(true)
.build();
//将上面的配置信息配入Get请求中
httpGet.setConfig(config);

//由客户端执行Get请求
response = client.execute(httpGet);

//从响应模型中获取响应实体
HttpEntity responseEntity = response.getEntity();
System.out.println("响应状态为:"+response.getStatusLine());
if (responseEntity!=null){
System.out.println("响应内容长度为:"+responseEntity.getContentLength());
System.out.println("响应内容为:"+ EntityUtils.toString(responseEntity,"utf-8"));
}

} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(client!=null){
client.close();
}
if (response!=null){
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}

}

public void doGetTestWayTwo(){
CloseableHttpClient client = HttpClientBuilder.create().build();
URI uri = null;
try{
ArrayList<NameValuePair> params = new ArrayList<>();
params.add(new BasicNameValuePair("name","10"));
params.add(new BasicNameValuePair("age","18"));
//这里设置uri信息,并将参数集合放入uri;
//注: 这里也支持一个键值对一个键值对地往里面方setParameter(String key, String value)
uri = new URIBuilder().setScheme("http").setHost("www.cuit.edu.cn")
// .setPort(12345).setPath("/xw")
.setParameters(params).build();
System.out.println(uri);

} catch (URISyntaxException e) {
e.printStackTrace();
}
//创建httpGet请求
HttpGet httpGet = new HttpGet(uri);

//创建响应模型
CloseableHttpResponse response = null;
try{
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(5000)
.setConnectionRequestTimeout(5000)
.setSocketTimeout(5000)
.setRedirectsEnabled(true).build();

httpGet.setConfig(config);
response = client.execute(httpGet);

HttpEntity entity = response.getEntity();
System.out.println("响应状态码:"+response.getStatusLine());

System.out.println("响应内容长度:"+entity.getContentLength());
System.out.println("响应内容:"+EntityUtils.toString(entity,"utf-8"));
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//测试
public static void main(String[] args) {
//new GetClient().doGetTestOne();
//new GetClient().doGetTestWayOne();
new GetClient().doGetTestWayTwo();
}
}

POST请求

public class PostClient {

/**
* POST 无参
*/
public void doPostTestOne(){
//获得http客户端
CloseableHttpClient client = HttpClientBuilder.create().build();
//创建Post请求
HttpPost httpPost = new HttpPost("https://www.cuit.edu.cn/index.htm");
//创建响应模型
CloseableHttpResponse response = null;
try{
response = client.execute(httpPost);
HttpEntity entity = response.getEntity();
System.out.println("响应状态码:"+response.getStatusLine());
System.out.println("响应内容长度:"+entity.getContentLength());
System.out.println("响应内容:"+ EntityUtils.toString(entity,"utf-8"));
} catch (IOException e) {
e.printStackTrace();
}finally {
try{
if (client!=null){
client.close();
}
if (response!=null){
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}

}

/**
* POST 有参
*/
public void doPostTestFour(){
//获的http客户端
CloseableHttpClient client = HttpClientBuilder.create().build();

//参数
StringBuilder params = new StringBuilder();
try{
//字符数据最好encoding; 这样一来, 某些特殊字符才嗯那个传过去(如:某人的名字就是"&",不encoding,传不过去)
params.append("phone=").append(URLEncoder.encode("admin","utf-8"));
params.append("&");
params.append("password=admin");

} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}

//创建post请求
HttpPost httpPost = new HttpPost("https://www.cuit.edu.cn/?" + params);

//设置ContentType(注:如果只是传入普通参数的话,ContentType不一定非要用application/json)
httpPost.setHeader("Content-Type","application/json;charset=utf-8");
//响应模型
CloseableHttpResponse response = null;
try{
response = client.execute(httpPost);
HttpEntity entity = response.getEntity();
System.out.println("状态码:"+response.getStatusLine());
System.out.println("响应内容长度: "+entity.getContentLength());
System.out.println("响应内容: "+entity);

} catch (IOException e) {
e.printStackTrace();
}
}

/**
* POST---有参测试(对象参数)
*/
public void doPostTestTwo(){
//获取Http客户端
CloseableHttpClient client = HttpClientBuilder.create().build();

//创建Post请求
HttpPost httpPost = new HttpPost("https://www.cuit.edu.cn/index.htm");
//User user = new User();
//user.setName("潘晓婷");
//user.setAge(18);
//user.setGender("女");
//user.setMotto("姿势要优雅~");
// 我这里利用阿里的fastjson,将Object转换为json字符串;
// (需要导入com.alibaba.fastjson.JSON包)
//String jsonString = JSON.toJSONString(user);
String jsonString = "";
StringEntity stringEntity = new StringEntity(jsonString, "utf-8");
httpPost.setEntity(stringEntity);
httpPost.setHeader("Content-Type", "application/json;charset=utf8");
CloseableHttpResponse response = null;
try{
response = client.execute(httpPost);
HttpEntity entity = response.getEntity();
System.out.println("状态码:"+response.getStatusLine());
System.out.println("响应内容长度:"+entity.getContentLength());
System.out.println("响应内容:"+EntityUtils.toString(entity,"utf-8"));
} catch (IOException e) {
e.printStackTrace();
} finally {
try{
if (client!=null){
client.close();
}
if (response!=null){
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}

}
public static void main(String[] args) {
//new PostClient().doPostTestOne();
//new PostClient().doPostTestFour();
new PostClient().doPostTestTwo();
}
}

上传文件

public class FileClient {
/**
* 发送文件
注:如果想要灵活方便的传输文件的话,
* 除了引入org.apache.httpcomponents基本的httpclient依赖外
* 再额外引入org.apache.httpcomponents的httpmime依赖。
* 追注:即便不引入httpmime依赖,也是能传输文件的,不过功能不够强大。
*/
public void test4(){

CloseableHttpClient client = HttpClientBuilder.create().build();
HttpPost httpPost = new HttpPost("https://www.cuit.edu.cn/file");

CloseableHttpResponse response = null;
try{
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
//第一个文件
String fileKey = "files";
File file1 = new File("D:\\图片\\《Valorant》3440x1440带鱼屏游戏壁纸_彼岸图网.jpg");
/*
防止服务端收到的文件名乱码. 我们这里可以先将文件名URLEncode, 然后服务端拿到文件后URLDecode
文件名其实是放在请求头的Content-Disposition中 如其值form-data; name="files"; filename="头像.jpg"
*/
multipartEntityBuilder.addBinaryBody(fileKey,file1, ContentType.DEFAULT_BINARY, URLEncoder.encode(file1.getName(),"utf-8"));

//其他参数(注:自定义contentType,设置UTF-8是为了防止服务端拿到的参数出现乱码)
ContentType contentType = ContentType.create("text/plain", StandardCharsets.UTF_8);
multipartEntityBuilder.addTextBody("name","等沙利文",contentType);
multipartEntityBuilder.addTextBody("age","25",contentType);

HttpEntity httpEntity = multipartEntityBuilder.build();
httpPost.setEntity(httpEntity);
response = client.execute(httpPost);
HttpEntity entity = response.getEntity();
if (entity!=null){
System.out.println("状态码:"+response.getStatusLine());
System.out.println("响应内容长度:"+entity.getContentLength());
System.out.println("响应内容:"+ EntityUtils.toString(entity,"utf-8"));
}
/*
File file1 = new File("D:\\图片\\XXX.jpg");
multipartEntityBuilder.addBinaryBody(fileKey,file1);
*/

} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (client!=null){
client.close();
}
if (response!=null){
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

public static void main(String[] args) {
new FileClient().test4();
}
}

Stream请求

public class StreamClient {
/**
* 发送流
*/
public void test5(){
CloseableHttpClient client = HttpClientBuilder.create().build();
HttpPost httpPost = new HttpPost("https://www.cuit.edu.cn/index.htm");
CloseableHttpResponse response = null;
try {
ByteArrayInputStream is = new ByteArrayInputStream("流啊流~".getBytes(StandardCharsets.UTF_8));
InputStreamEntity ise = new InputStreamEntity(is);
httpPost.setEntity(ise);

response = client.execute(httpPost);
HttpEntity entity = response.getEntity();
System.out.println("响应状态码:"+response.getStatusLine());
if (entity!=null){
System.out.println("响应内容长度:"+entity.getContentLength());
System.out.println("响应内容:"+ EntityUtils.toString(entity,StandardCharsets.UTF_8));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (client!=null){
client.close();
}
if (response!=null){
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

public static void main(String[] args) {
new StreamClient().test5();
}
}

-END-