使用HttpClient发起请求
2023/6/13大约 6 分钟
HttpClient 概述
HttpClient 是 apache 组织下面的一个用于处理 HTTP 请求和响应的开源工具,是一套类库。
HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。
HttpClient 相比传统 JDK 自带的 URLConnection,增加了易用性和灵活性,它不仅是客户端发送 HTTP 请求变得容易,而且也方便了开发人员测试接口(基于 HTTP 协议的),即提高了开发的效率,也方便提高代码的健壮性。
使用JDK中的API
HttpClient 是对 JDK 中 java.net.* 包下面的有关基础类库的封装,了解底层有助于更好地学习使用。
URL
使用 URL 直接获取输入流:
try {
String urlString = "http://localhost:8080/cms/";
URL url = new URL(urlString); //代表了一个网址
InputStream is = url.openStream(); //获得网页的内容
// 将InputStream转换为Reader,并使用缓冲读取,提高效率,同时可以按行读取内容
BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8"));
String line = null;
while((line = br.readLine()) != null){
System.out.println(line);
}
is.close();
} catch (Exception e) {
e.printStackTrace();
}URLConnection
使用 URLConnection 通过代理访问:
try {
String urlString = "http://www.ibm.com/developerworks/cn/java/j-javaroundtable/index.html";
URL url = new URL(urlString); //代表了一个网址
//首先创建HTTP代理,指定代理的地址和端口
Proxy proxy = new Proxy(
Proxy.Type.HTTP,
new InetSocketAddress("79.120.193.53",80)
);
/**
* 打开一个连接对象
* 可以通过这个对象,在真正发起请求之前,设置一些其它的信息
* 比如:代理服务器等
*/
URLConnection conn = url.openConnection(proxy); // 通过代理的方式获取连接
InputStream is = conn.getInputStream(); // 获得网页的内容
// 将InputStream转换为Reader,并使用缓冲读取,提高效率,同时可以按行读取内容
BufferedReader br = new BufferedReader(new InputStreamReader(is,"UTF-8"));
String line = null;
while((line = br.readLine()) != null){
System.out.println(line);
}
is.close();
} catch (Exception e) {
e.printStackTrace();
}- 可以设置 HTTP 或 SOCKS 代理,只要指定不同的代理类型即可!
HttpURLConnection
HttpURLConnection 是 URLConnection 的子类,它提供了更多与 HTTP 有关的处理方法。
try {
String urlString =
"http://localhost:8080/cms/backend/main.jsp";
URL url = new URL(urlString); //代表了一个网址
//设置是否自动进行重定向,缺省这个值为true
HttpURLConnection.setFollowRedirects(false);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
//设置请求方法
conn.setRequestMethod("GET");
// 获取响应码
int code = conn.getResponseCode();
System.out.println("服务器响应代码为:"+code);
// 获取输入流
InputStream is = conn.getInputStream();
//将InputStream转换为Reader,并使用缓冲读取,提高效率,同时可以按行读取内容
BufferedReader br = new BufferedReader(
new InputStreamReader(is,"UTF-8")
);
String line = null;
while((line = br.readLine()) != null){
System.out.println(line);
}
is.close();
} catch (Exception e) {
e.printStackTrace();
}- 设置了不自动重定向后,如果返回的是 3XX,则输入流将读取不到任何内容。
代理请求:
/**
* 使用原生JDK实现的请求转发
* @param exchange
* @param targetUrl
* @throws IOException
*/
public static void forwardRequest(HttpExchange exchange, String targetUrl) throws IOException {
try {
// 创建代理服务器的连接
URL url = new URL(targetUrl);
HttpURLConnection proxyConnection = (HttpURLConnection) url.openConnection();
// 复制请求方法
String requestMethod = exchange.getRequestMethod();
proxyConnection.setRequestMethod(requestMethod);
// 复制请求头
// proxyConnection.setRequestProperty("Content-Type", "application/json");
getHeadsToString(exchange.getRequestHeaders()).forEach( header -> {
proxyConnection.setRequestProperty(header[0], header[1]);
});
// 复制客户端的请求体
proxyConnection.setDoOutput(true); // 允许代理携带请求体
try (InputStream requestBody = exchange.getRequestBody()) { // 获取客户端请求体的输入流
OutputStream proxyOutputStream = proxyConnection.getOutputStream(); // 代理链接请求体的输出流
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = requestBody.read(buffer)) != -1) {
proxyOutputStream.write(buffer, 0, bytesRead); // 写入代理请求体
}
proxyOutputStream.close();
}
// 获取代理服务器的响应码
int responseCode = proxyConnection.getResponseCode();
exchange.sendResponseHeaders(responseCode, 0); // 发送响应头
// 获取代理服务器的响应体响应给客户端
try (OutputStream responseBody = exchange.getResponseBody()) { // 获取客户端响应体的输出流
InputStream proxyInputStream = proxyConnection.getInputStream(); // 响应体的输入流
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = proxyInputStream.read(buffer)) != -1) {
responseBody.write(buffer, 0, bytesRead); // 写入客户端响应体
}
proxyInputStream.close();
}
// 关闭代理连接
proxyConnection.disconnect();
} catch (IOException e) {
// 异常处理
e.printStackTrace();
exchange.sendResponseHeaders(500, 0); // 内部服务器错误
} finally {
exchange.close();
}
}使用 HttpClient
导入依赖:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
</dependency>发送请求:
// HttpClient 主要负责执行请求
HttpClient httpclient = new DefaultHttpClient();
// HttpGet 封装 GET 请求
HttpGet get = new HttpGet("http://localhost:8080/cms");
// 通过 httpclient 发起请求
HttpResponse response = httpclient.execute(get);解析响应内容:
// 获得服务器响应回来的消息体(不包括HTTP HEAD)
HttpEntity entity = response.getEntity();
if(entity != null){
// 获取指向响应体的输入流
InputStream is = entity.getContent();
// 将InputStream转换为Reader,并使用缓冲读取,提高效率,同时可以按行读取内容
BufferedReader br = new BufferedReader(
new InputStreamReader(is,"UTF-8")
);
String line = null;
while((line = br.readLine()) != null){
System.out.println(line);
}
// 关闭输入流
is.close();
}释放所有的连接资源:
// 一般在所有的请求处理完成之后,才需要释放
httpclient.getConnectionManager().shutdown();获取响应体编码
// 即获取HTTP HEAD的:Content-Type:text/html;charset=UTF-8 中的字符集信息
String charset = EntityUtils.getContentCharSet(entity);使用响应中的编码来解释响应的内容:
InputStream is = entity.getContent();
BufferedReader br = new BufferedReader(
new InputStreamReader(is,charset)
);通过代理访问
// HttpClient主要负责执行请求
HttpClient httpclient = new DefaultHttpClient();
// 设置代理服务器
httpclient.getParams().setParameter(
ConnRoutePNames.DEFAULT_PROXY, new HttpHost("121.12.249.207",3128)
);
// 封装 GET 请求
HttpGet get = new HttpGet("http://www.baidu.com/");
// 发起请求
HttpResponse response = httpclient.execute(get);
// 解析响应内容,释放资源
// ...重定向
HttpClient 缺省情况下自动处理客户端重定向,即无需手动发起重定向请求。
阻止重定向:
HttpClient httpClient = new DefaultHttpClient();
HttpParams params = httpClient.getParams();
params.setParameter(ClientPNames.HANDLE_REDIRECTS, false);
// 发起请求、解析响应...获得重定向之后的网址信息:
HttpClient httpclient = new DefaultHttpClient();
// 创建一个上下文对象
HttpContext context = new BasicHttpContext();
// 封装 GET 请求
HttpGet get = new HttpGet("http://localhost:8080/cms/backend/main.jsp");
// 发送请求,并将执行过程中的状态信息存储至上下文
HttpResponse response = httpclient.execute(get,context);
// 获得重定向之后的主机地址信息
HttpHost targetHost = (HttpHost)context.getAttribute(
ExecutionContext.HTTP_TARGET_HOST
);
// 获得重定向之后请求对象的URI
HttpUriRequest targetURI = (HttpUriRequest)context.getAttribute(
ExecutionContext.HTTP_REQUEST
);
// 重定向后的URL即:targetHost + targetURIHttpContext,实际上是客户端用来在多次请求-响应的交互中,保持状态信息用的,是上下文的存在。我们自己也可以利用 HttpContext 来存放一些我们想要存放的其它信息,以便下次请求的时候,能够把这 些信息拿出来使用!
自动 Cookie 处理
自动Cookie处理,即可在请求中携带指定的Cookie信息,实现自动访问。
场景:自动登录
// 先发起GET请求获取登录页面,解析出响应体的编码 charset
// 发起POST请求,携带登录信息
HttpPost post = new HttpPost("http://localhost:8080/cms/backend/LoginServlet");
// 添加POST参数(Cookie同理)
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
nvps.add(new BasicNameValuePair("username","admin"));
nvps.add(new BasicNameValuePair("password","admin"));
// 添加至请求体
post.setEntity(new UrlEncodedFormEntity(nvps,charset));
// 发起请求
response = httpclient.execute(post);