介绍了一些JDK提供的原生的用于发起HTTP调用的API,比如UTL、HttpURLConnection。
HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议,进行HTTP调用来访问网络资源。实际上,上一篇文章学习的的Socket编程对于大多数程序员来说在工作中根本接触不到,接触到最多的就是HTTP远程调用。
HTTP协议位于传输层TCP协议之上的应用层,虽然HTTP协议依赖于TCP,但是肯定不需要再直接使用Socket编程了,当然Java.net包中也为我们提供了可以发起HTTP请求的相关工具类,比如URL、HttpURLConnection。
1 URL
public final class URL
extends Object
implements Serializable
为了更好地面向对象,java将URL封装到了对象之中,该对象就叫URL对象,该对象的类就叫URL类,它表示统一资源定位符。实际上还有一个URI的类,它表示统一资源描述符!
构造器:
URL(String spec) | 根据 String 表示形式创建 URL 对象。 |
API方法,以http://192.168.2.218:8080/myweb/demo.html?username=lisi&age=21演示:
String getProtocol() | 获取此 URL 的协议名称。 | http |
String getHost() | 获取此 URL 的主机名称(IP地址)。 | 192.168.2.218 |
int getPort() | 获取此 URL 的端口号。如果未设置端口号,则返回-1。 | 8080 |
String getPath() | 获取此 URL 的路径部分。如果没有路径,则返回一个空字符串。 | /myweb/demo.html |
String getQuery() | 获取此 URL 的查询,如果没有查询,则返回 null(传递的参数)。 | username=lisi&age=21 |
String toString() | 获取此 URL 的字符串表示形式。 | 传入的字符串 |
URLConnection openConnection() | 和服务器建立连接。返回一个 URLConnection 对象,它表示到URL引用的远程对象(服务器)的连接。 就是把客户端(URL)和服务器(应用程序)的连接也封装到了URLConnection对象之中,能拿到该对象,说明客户端(URL)已经和服务器(应用程序)建立了连接。 | |
InputStream openStream() | 打开连接,并且返回从此打开的连接读取的输入流。等价于openConnection().getInputStream() |
1.1 URLConnection
public abstract class URLConnection
extends Object
抽象类 URLConnection代表应用程序和 URL 之间的通信链接。此类的实例可用于读取和写入此 URL 引用的资源,因此该类实例又叫连接对象。
通过在 URL 上调用 openConnection()方法可以获取连接对象。
API方法:
OutputStream getOutputStream() | 返回写入到此连接的输出流。可以向服务器(应用程序)发送信息。 |
InputStream getInputStream() | 返回从此打开的连接读取的输入流。读取服务器(应用程序)的响应信息。 |
1.2 使用URL发起请求
使用URL发起网络请求,可以直接从 URL 中读取服务器响应的字节流数据。
这里,我们请求百度的网址!
public class URLTest {
public static void main(String[] args) throws IOException {
//创建一个连接到百度的URL
URL url = new URL("http://www.baidu.com");
/* 字节流 */
//InputStream is = url.openConnection().getInputStream();
InputStream is = url.openStream();
/* 字符流 */
InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);
/* 提供缓存功能 */
BufferedReader br = new BufferedReader(isr);
String line;
//每次读取一行的数据输出,实际上输出的是一个HTML页面,也就是说服务器响应的是一个静态HTML页面的代码
//该页面进过浏览器渲染,我们就能看到熟悉的“百度一下”的界面
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
}
}
实际上输出的是一个HTML页面,也就是说服务器响应的是一个静态HTML页面的代码,该页面进过浏览器渲染,我们就能看到熟悉的“百度一下”的界面。
2 HttpURLConnection
URLConnection支持各种协议的连接,而java.net.HttpURLConnection则表示一个支持HTTP协议和特定功能的URLConnection。使用HttpURLConnection可以比较方便的指定HTTP请求所需要的各种请求头参数,以及获取响应头、响应体等内容。
HttpURLConnection不需要设置socket,HttpURLConnection并不是底层的连接,而是在底层Socket连接上的一个请求。实际底层的Socket网络连接可以被多个HttpURLConnection实例共享,但每一个HttpURLConnection实例只能发送一个请求。请求结束之后,应该调用HttpURLConnection实例的InputStream或OutputStream的close()方法以释放请求的网络资源,不过这种方式对于持久化连接没用。对于持久化连接,得用disconnect()方法关闭底层连接的socket。
这个类的使用遵循以下模式:
- 通过调用URL.openConnection()来获得一个新的URLConnection对象,并且将其结果强制转换为HttpURLConnection.
- 准备请求。一个请求主要的参数是它的URI。请求头可能也包含元数据,例如证书,首选数据类型和会话Cookies.
- 可以选择性的上传一个请求体。如果是POST请求,那么HttpURLConnection实例必须设置setDoOutput(true)。如果它包含一个请求体,那么通过将数据写入一个由getOutStream()返回的输出流来传输请求体数据。
- 读取响应。响应头通常包含元数据例如响应体的内容类型和长度,修改日期和会话Cookie s。响应体可以被由getInputStream返回的输入流读取。如果响应没有响应体,则该方法会返回一个空的流。
- 关闭连接。一旦一个响应体已经被阅读后,HttpURLConnection 对象应该通过调用disconnect()关闭。断开连接会释放被一个connection占有的资源,这样它们就能被关闭或再次使用。
对当当网搜索商品的URL发起GET请求:
public class HttpURLConnectionTest {
public static void main(String[] args) throws IOException {
/*
* 创建一个连接到当当网搜索页面的URL
*/
String param = "圣诞节";
URL url = new URL("http://search.dangdang.com/?key=" + param + "&act=input");
//强制转换为HttpURLConnection
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
/*设置连接参数*/
//设置连接主机超时(单位:毫秒),即用于connect()方法打开连接时的超时时间
urlConnection.setConnectTimeout(3000);
//设置从主机读取数据超时(单位:毫秒),也就是InputStream.read()方法,即从输入流读取数据的超时时间
urlConnection.setReadTimeout(3000);
//设置请求方法类型,默认就是GET
urlConnection.setRequestMethod("GET");
/*设置请求头的信息*/
//添加/替换请求头的信息
urlConnection.setRequestProperty("Content-type", "text/html; charset=gb2312");
//添加请求头的信息
//urlConnection.addRequestProperty("Content-type", "text/html;charset=gb2312");
urlConnection.setRequestProperty("Accept", "*/*");
urlConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36");
urlConnection.setRequestProperty("Connection", "keep-alive");
/*
* 连接并发送HTTP请求
*/
urlConnection.connect();
/*
* 获取响应码,判断是否200
*/
if (urlConnection.getResponseCode() != 200) {
throw new RuntimeException("bad response");
}
/*
* 获取所有响应头的信息
*/
Map<String, List<String>> map = urlConnection.getHeaderFields();
for (String key : map.keySet()) {
System.out.println(key + ": " + map.get(key));
}
/*
* 获取响应体,获取的是输入流,需要自己转换
*/
InputStream input = urlConnection.getInputStream();
/* 字符流 */
InputStreamReader isr = new InputStreamReader(input, "gb2312");
BufferedReader br = new BufferedReader(isr);
String line;
//输出响应体
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
}
}
部分响应结果如下:
对于post请求,需要调用connection.setDoOutput(true)方法,表示设置允许写入,调用connection.setUseCaches(false)方法,表示不使用缓存,并且需要通过connection.getOutputStream()获取输出流,然后将请求体的数据写入到这个输出流中,比较麻烦。
上面的代码仍然非常的繁琐,并且需要手动的处理输入、输出流,所以用起来很麻烦。也就是说,虽然在 JDK 的 java net包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK库本身提供的功能还不够丰富和灵活。
本次我们仅仅介绍了一些JDK提供的原生的用于发起HTTP调用的API,在实际开发中,我们应该使用更加成熟的被封装好的工具类,比如Apache Common 提供的HttpClient,在Spring项目中,我们可以使用Spring提供的的RestTemplate,并且自 5.0 起,Spring提供了一个非阻塞(异步调用)、响应式的org.springframe.web.reactive.client.WebClient,WebClient同时对同步和异步以及流式的HTTP调用处理方案提供高效支持,用于替代RestTemplate,RestTemplate将在将来的版本中弃用。这些工具类,我们后面做项目的时候自然会遇到!
如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!