简介:
HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。
虽然在 JDK 的 java net包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。
HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,
并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,比如 Apache Jakarta 上很著名的另外
两个开源项目 Cactus 和 HTMLUnit 都使用了 HttpClient。现在HttpClient最新版本为 HttpClient 4.5 (GA) (2015-09-11)
功能介绍:
以下列出的是 HttpClient 提供的主要的功能,要知道更多详细的功能可以参见 HttpClient 的主页。
(1)实现了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)
(2)支持自动转向
(3)支持 HTTPS 协议
(4)支持代理服务器等
(1)实现了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)
(2)支持自动转向
(3)支持 HTTPS 协议
(4)支持代理服务器等
代码实现:
package com.superb.httpclient;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpParams;
import org.apache.log4j.Logger;
public class HttpUtil {
private final static Logger logger = Logger.getLogger(HttpUtil.class);
private static ThreadSafeClientConnManager cm = null;
static {
try {
SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
ctx.init(null, new TrustManager[] { tm }, null);
SSLSocketFactory ssf = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
schemeRegistry.register(new Scheme("https", 443, ssf));
cm = new ThreadSafeClientConnManager(schemeRegistry);
cm.setMaxTotal(400);
cm.setDefaultMaxPerRoute(40);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @description 获取 HTTP Client
* @return
*/
private static HttpClient getHttpClient() {
HttpParams params = new BasicHttpParams();
params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 5000);
return new DefaultHttpClient(cm, params);
}
/**
* @description HTTP POST
* @param url
* @param msg
* @return
*/
public static String post(String url, Map
params) {
logger.info("URLInvoke.post#request message : ");
List
list = new ArrayList
();
if (params != null && !params.isEmpty()) {
for (Map.Entry
entry : params.entrySet()) {
list.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
}
try {
// 实现将请求的参数封装到表单中,即请求体中
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8");
// 使用post方式提交数据
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(entity);
// 执行post请求,并获取服务器端的响应HttpResponse
HttpClient client = getHttpClient();
HttpResponse httpResponse = client.execute(httpPost);
// 获取服务器端返回的状态码和输入流,将输入流转换成字符串
if (httpResponse.getStatusLine().getStatusCode() == 200) {
InputStream inputStream = httpResponse.getEntity().getContent();
return changeInputStream(inputStream, "UTF-8");
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
/*
* // 把从输入流InputStream按指定编码格式encode变成字符串String
*/
public static String changeInputStream(InputStream inputStream, String encode) {
// ByteArrayOutputStream 一般叫做内存流
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] data = new byte[1024];
int len = 0;
String result = "";
if (inputStream != null) {
try {
while ((len = inputStream.read(data)) != -1) {
byteArrayOutputStream.write(data, 0, len);
}
result = new String(byteArrayOutputStream.toByteArray(), encode);
} catch (IOException e) {
e.printStackTrace();
}
}
logger.info("URLInvoke.post#response message : " + result);
return result;
}
/**
* HTTP GET
*
* @param url
* @return
*/
public static String get(String url) {
logger.info("URLInvoke.get#response url : " + url);
HttpResponse httpResponse = null;
InputStream inputStream = null;
String result = "";
// 生成一个请求对象
HttpGet httpGet = new HttpGet(url);
// 生成一个http客户端
HttpClient httpClient = new DefaultHttpClient();
// 使用Http客户端发送请求对象
// httpResponse是服务器返回给我们的响应httpentity是响应的内容
try {
httpResponse = httpClient.execute(httpGet);
HttpEntity httpEntity = httpResponse.getEntity();
inputStream = httpEntity.getContent();
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
String line = "";
while ((line = br.readLine()) != null) {
result += "\n" + line;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
logger.info("URLInvoke.get#response result : " + result);
return result;
}
}
4.0.0
com.superb
maven-1
20160720
jar
maven-1
http://maven.apache.org
UTF-8
junit
junit
3.8.1
test
commons-logging
commons-logging
1.1.1
commons-codec
commons-codec
1.4
commons-httpclient
commons-httpclient
3.0.1
org.slf4j
slf4j-log4j12
1.7.2
org.apache.httpcomponents
httpclient
4.3.5
常见问题:
字符编码
某目标页的编码可能出现在两个地方,第一个地方是服务器返回的http头中,另外一个地方是得到的html/xml页面中。
在http头的Content-Type字段可能会包含
字符编码信息。例如可能返回的头会包含这样子的信息:Content-Type: text/html; charset=UTF-8。
这个头信息表明该页的编码是UTF-8,但是服务器返回的头信息未必与内容能匹配上。比如对于一些双
字节语言国家,
可能服务器返回的编码类型是UTF-8,
但真正的内容却不是UTF-8编码的,因此需要在另外的地方去得到页面的编码信息;
但是如果服务器返回的编码不是UTF-8,而是具体的一些编码,
比如gb2312等,那服务器返回的可能是正确的编码信息。
通过method对象的getResponseCharSet()方法就可以得到http头中的编码信息。
对于象xml或者html这样的文件,
允许作者在页面中直接指定编码类型。比如在html中会有
<meta http-equiv="Content-Type" content="text/html; charset=gb2312"/>这样的标签;
或者在xml中会有<?xml version="1.0" encoding="gb2312"?>这样的标签,在这些情况下,可能与http头中返回的编码信息冲突,
需要用户自己判断到底那种编码类型应该是真正的
编码。
自动转向
根据RFC2616中对自动转向的定义,主要有两种:301和302。301表示永久的移走(Moved Permanently),当返回的是301,
则表示请求的资源已经被移到一个固定的新地方,任何向该地址发起请求都会被转到新的地址上。302表示暂时的转向,
比如在服务器端的servlet程序调用了sendRedirect方法,则在客户端就会得到一个302的代码,这时服务器返回的头信息
中location的值就是sendRedirect转向的目标地址。
HttpClient支持自动转向处理,但是象POST和PUT方式这种要求接受后继服务的请求方式,暂时不支持自动转向,
因此如果碰到POST方式提交后返回的是301或者302的话需要自己处理。就像刚才在POSTMethod中举的例子:
如果想进入登录BBS后的页面,必须重新发起登录的请求,请求的地址可以在头字段location中得到。不过需要注意的是,
有时候location返回的可能是相对路径,因此需要对location返回的值做一些处理才可以发起向新地址的请求。
另外除了在头中包含的信息可能使页面发生重定向外,在页面中也有可能会发生页面的重定向。引起页面自动转发的
标签是:<meta http-equiv="refresh" content="5; url=....">。如果你想在程序中也处理这种情况的话得自己分析页面来实现转向。
需要注意的是,在上面那个标签中url的值也可以是一个
相对地址,如果是这样的话,需要对它做一些处理后才可以转发。
另外还有一些其他常见问题,比如:处理https协议等等