HTTP 编程
# HTTP 编程
HTTP 就是目前使用最广泛的 Web 应用程序使用的基础协议,例如,浏览器访问网站,手机 App 访问后台服务器,都是通过 HTTP 协议实现的。
HTTP 是 HyperText Transfer Protocol 的缩写,翻译为超文本传输协议,它是基于 TCP 协议之上的一种请求-响应协议。
# HTTP 编程
既然 HTTP 涉及到客户端和服务器端,和 TCP 类似,我们也需要针对客户端编程和针对服务器端编程。
本节我们不讨论服务器端的 HTTP 编程,因为服务器端的 HTTP 编程本质上就是编写 Web 服务器,这是一个非常复杂的体系,也是 JavaEE 开发的核心内容,我们在后面的章节再仔细研究。
本节我们只讨论作为客户端的 HTTP 编程。
因为浏览器也是一种 HTTP 客户端,所以,客户端的 HTTP 编程,它的行为本质上和浏览器是一样的,即发送一个 HTTP 请求,接收服务器响应后,获得响应内容。只不过浏览器进一步把响应内容解析后渲染并展示给了用户,而我们使用 Java 进行 HTTP 客户端编程仅限于获得响应内容。
我们来看一下 Java 如何使用 HTTP 客户端编程。
Java 标准库提供了基于 HTTP 的包,但是要注意,早期的 JDK 版本是通过 HttpURLConnection
访问 HTTP,典型代码如下:
URL url = new URL("https://www.peterjxl.com");
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setUseCaches(false);
conn.setConnectTimeout(5000); //设置超时时间为5秒
// 设置HTTP头
conn.setRequestProperty("Accept", "*/*");
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (compatible; MSIE 11; Windows NT 5.1)");
// 连接并发送HTTP请求:
conn.connect();
// 判断HTTP响应是否200:
if (conn.getResponseCode() != 200) {
throw new RuntimeException("bad response");
}
// 获取所有响应Header:
Map<String, List<String>> map = conn.getHeaderFields();
for (String key : map.keySet()) {
System.out.println(key + ": " + map.get(key));
}
// 获取响应内容:
InputStream input = conn.getInputStream();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
上述代码编写比较繁琐,并且需要手动处理 InputStream
,所以用起来很麻烦。
# HttpClient
从 Java 11 开始,引入了新的 HttpClient
,它使用链式调用的 API,能大大简化 HTTP 的处理。
我们来看一下如何使用新版的 HttpClient
。首先需要创建一个全局 HttpClient
实例,因为 HttpClient
内部使用线程池优化多个 HTTP 连接,可以复用:
static HttpClient httpClient = HttpClient.newBuilder().build();
使用 GET
请求获取文本内容代码如下:
import java.net.URI;
import java.net.http.*;
import java.net.http.HttpClient.Version;
import java.time.Duration;
import java.util.*;
public class Main {
// 全局HttpClient:
static HttpClient httpClient = HttpClient.newBuilder().build();
public static void main(String[] args) throws Exception {
String url = "https://www.sina.com.cn/";
HttpRequest request = HttpRequest.newBuilder(new URI(url))
// 设置Header:
.header("User-Agent", "Java HttpClient").header("Accept", "*/*")
// 设置超时:
.timeout(Duration.ofSeconds(5))
// 设置版本:
.version(Version.HTTP_2).build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
// HTTP允许重复的Header,因此一个Header可对应多个Value:
Map<String, List<String>> headers = response.headers().map();
for (String header : headers.keySet()) {
System.out.println(header + ": " + headers.get(header).get(0));
}
System.out.println(response.body().substring(0, 1024) + "...");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
如果我们要获取图片这样的二进制内容,只需要把 HttpResponse.BodyHandlers.ofString()
换成 HttpResponse.BodyHandlers.ofByteArray()
,就可以获得一个 HttpResponse<byte[]>
对象。如果响应的内容很大,不希望一次性全部加载到内存,可以使用 HttpResponse.BodyHandlers.ofInputStream()
获取一个 InputStream
流。
要使用 POST
请求,我们要准备好发送的 Body 数据并正确设置 Content-Type
:
String url = "http://www.example.com/login";
String body = "username=bob&password=123456";
HttpRequest request = HttpRequest.newBuilder(new URI(url))
// 设置Header:
.header("Accept", "*/*")
.header("Content-Type", "application/x-www-form-urlencoded")
// 设置超时:
.timeout(Duration.ofSeconds(5))
// 设置版本:
.version(Version.HTTP_2)
// 使用POST并设置Body:
.POST(BodyPublishers.ofString(body, StandardCharsets.UTF_8)).build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
String s = response.body();
2
3
4
5
6
7
8
9
10
11
12
13
14
可见发送 POST
数据也十分简单。
# 小结
Java 提供了 HttpClient
作为新的 HTTP 客户端编程接口用于取代老的 HttpURLConnection
接口;
HttpClient
使用链式调用并通过内置的 BodyPublishers
和 BodyHandlers
来更方便地处理数据。