1、URL的概念

统一资源定位符URL(Uniform Resource Locator)是www客户机访问Internet时用来标识资源的名字和地址。

URL的基本格式是:
<METHOD>://<HOSTNAME:PORT>/<PATH>/<FILE>  
1
 
1
<METHOD>://<HOSTNAME:PORT>/<PATH>/<FILE>  
  • Method是传输协议
  • HOSTNAME是文档和服务器所在的Internet主机名(域名系统中DNS中的点地址)
  • PORT是服务端口号(可省略)
  • PATH是路径名
  • FILE是文件名

例如:
http://www.weixueyuan.net/ (http是协议名,www.weixueyuan.net是主机名)
http://www.weixueyuan.net/view/6079.html (www.weixueyuan.net是主机名,view/6079.html是文件路径和文件名)

简单的可以把URL理解为包含:协议、主机名、端口、路径、查询字符串和参数等内容。每一段可以独立设置。


2、URL类

Java中有个java.net包,其中的类是进行网络编程的,URL对象则是一个可以表示网络资源的类。程序利用URL对象能够实现Internet寻址、网络资源的定位连接、在客户机与服务器之间访问等。

URL类的构造方法有很多,如下图:
 
最常用的还是第一种,即 URL(String spec),如 URL url = new URL("http://www.balabala.com:80");

URL对象的方法也很简单,基本上都是get方法,主要用于分段获取一个URL中各个部分,比单纯地使用一个字符串来表示链接地址,URL对象的方式更加灵活方便。

同时,它可以使用openConnection方法,获取一个URLConnection对象,以建立网络连接。


3、HttpURLConnecttion类以及请求发送

要接收和发送信息还要用HttpURLConnection类,HttpURLConnection类是URLConnection类的子类,其对象往往是通过URL对象进行获取,如下:
URL url = new URL("http://www.sun.com/");//先要创建一个URL对象
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();//获得URLConnection对象
2
 
1
URL url = new URL("http://www.sun.com/");//先要创建一个URL对象
2
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();//获得URLConnection对象

有了URLConnection对象,就可以实现网络连接,主要分三步走:
  • 设置请求头信息和请求体
  • 建立连接
  • 获取响应

下面以一个模拟发送http请求的方法代码进行示例:
public static void sendRequest(String link, String RequestMethod, String postContent) {
System.out.println("============sendRequest start, access link: " + link + " ============");
try {
URL url = new URL(link);
HttpURLConnection connection = (HttpURLConnection) url.openConnection(); //step1:设置请求头信息和请求体
//设置请求方式
connection.setRequestMethod(RequestMethod); //GET或POST等
connection.setDoOutput(postContent == null ? false : true); //是否需要输出数据,默认false
connection.setDoInput(true); //是否需要获取输入,默认true
//设置部分常规的请求头参数
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("User-Agent", "Mozilla/5.0"); //可以不设置,但部分网站会对该字段进行检查,以过滤非浏览器的请求
//设置请求体内容
if (postContent != null) {
OutputStream out = connection.getOutputStream();
out.write(postContent.getBytes());
out.close();
} //step2:建立连接
//建立连接
connection.connect(); //step3:获取响应
//获取响应头
System.out.println("------------acquire response header start------------");
Map header = connection.getHeaderFields();
Set<String> keys = header.keySet();
for (String key : keys) {
String val = connection.getHeaderField(key);
System.out.println(key + ":" + val);
}
System.out.println("------------acquire response header end------------");
//获取响应体
System.out.println("------------acquire response body start------------");
InputStream in = connection.getInputStream();
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] cache = new byte[2048];
int size = -1;
while ((size = in.read(cache)) != -1) {
out.write(cache, 0, size);
}
out.close();
in.close();
String responseBody = out.toString();
System.out.println("response body:" + responseBody);
System.out.println("------------acquire response body end------------");
//获取响应码
int responseCode = connection.getResponseCode();
System.out.println("responseCode:" + responseCode); } catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("============sendRequest end============");
}
60
 
1
public static void sendRequest(String link, String RequestMethod, String postContent) {
2
        System.out.println("============sendRequest start, access link: " + link + " ============");
3
        try {
4
            URL url = new URL(link);
5
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
6

7
            //step1:设置请求头信息和请求体
8
            //设置请求方式
9
            connection.setRequestMethod(RequestMethod); //GET或POST等
10
            connection.setDoOutput(postContent == null ? false : true); //是否需要输出数据,默认false
11
            connection.setDoInput(true); //是否需要获取输入,默认true
12
            //设置部分常规的请求头参数
13
            connection.setRequestProperty("Connection", "Keep-Alive");
14
            connection.setRequestProperty("User-Agent", "Mozilla/5.0"); //可以不设置,但部分网站会对该字段进行检查,以过滤非浏览器的请求
15
            //设置请求体内容
16
            if (postContent != null) {
17
                OutputStream out = connection.getOutputStream();
18
                out.write(postContent.getBytes());
19
                out.close();
20
            }
21

22
            //step2:建立连接
23
            //建立连接
24
            connection.connect();
25

26
            //step3:获取响应
27
            //获取响应头
28
            System.out.println("------------acquire response header start------------");
29
            Map header = connection.getHeaderFields(); 
30
            Set<String> keys = header.keySet();
31
            for (String key : keys) {
32
                String val = connection.getHeaderField(key);
33
                System.out.println(key + ":" + val);
34
            }
35
            System.out.println("------------acquire response header end------------");
36
            //获取响应体
37
            System.out.println("------------acquire response body start------------");
38
            InputStream in = connection.getInputStream(); 
39
            ByteArrayOutputStream out = new ByteArrayOutputStream();
40
            byte[] cache = new byte[2048];
41
            int size = -1;
42
            while ((size = in.read(cache)) != -1) {
43
                out.write(cache, 0, size);
44
            }
45
            out.close();
46
            in.close();
47
            String responseBody = out.toString();
48
            System.out.println("response body:" + responseBody);
49
            System.out.println("------------acquire response body end------------");
50
            //获取响应码
51
            int responseCode = connection.getResponseCode(); 
52
            System.out.println("responseCode:" + responseCode);
53

54
        } catch (MalformedURLException e) {
55
            e.printStackTrace();
56
        } catch (IOException e) {
57
            e.printStackTrace();
58
        }
59
        System.out.println("============sendRequest end============");
60
    }


4、关于connect()和触发请求发送

最开始,根据方法名,我以为connect()就是发送请求,并获取到响应,即完成一次网络连接访问。但实际上在写这篇博客的示例代码,在调试时发现,即便不写connect(),也可以完成一次请求模拟,也就是说,connect()不是用来触发发出请求并获取响应的。

那么何时触发请求的发送呢?我利用IDE的断点和Fiddler的断点调试功能进行了测试,发现在执行“获取响应相关内容的方法时”,才会正式发出HTTP请求并返回响应,这里的相关方法包括但不仅限于:
  • connection.getHeaderFields(); //获取响应头属性集合
  • connection.getInputStream(); //获取响应输入流
  • connection.getResponseCode(); //获取响应状态码

之前看到某网友说,要正确发送请求需要调用方法connection.getResponseCode(),大概原因在此,尽管他的说法并不完全正确。所以,如果你只是发送GET方法,同时也没有主动获取响应相关的内容,那么实际上并不会发出请求。

如上例代码中,如果把整个step3的代码全部去掉(即不调用"获取响应相关内容的方法”),如下方法试图请求连接,是不会真正发出请求的:
public static void sendRequest(String link, String RequestMethod, String postContent) {
System.out.println("============sendRequest start, access link: " + link + " ============");
try {
URL url = new URL(link);
HttpURLConnection connection = (HttpURLConnection) url.openConnection(); //step1:设置请求头信息和请求体
//设置请求方式
connection.setRequestMethod(RequestMethod); //GET或POST等
connection.setDoOutput(postContent == null ? false : true); //是否需要输出数据,默认false
connection.setDoInput(true); //是否需要获取输入,默认true
//设置部分常规的请求头参数
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("User-Agent", "Mozilla/5.0"); //可以不设置,但部分网站会对该字段进行检查,以过滤非浏览器的请求
//设置请求体内容
if (postContent != null) {
OutputStream out = connection.getOutputStream();
out.write(postContent.getBytes());
out.close();
} //step2:建立连接
//建立连接
connection.connect(); } catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("============sendRequest end============");
}
32
 
1
public static void sendRequest(String link, String RequestMethod, String postContent) {
2
        System.out.println("============sendRequest start, access link: " + link + " ============");
3
        try {
4
            URL url = new URL(link);
5
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
6

7
            //step1:设置请求头信息和请求体
8
            //设置请求方式
9
            connection.setRequestMethod(RequestMethod); //GET或POST等
10
            connection.setDoOutput(postContent == null ? false : true); //是否需要输出数据,默认false
11
            connection.setDoInput(true); //是否需要获取输入,默认true
12
            //设置部分常规的请求头参数
13
            connection.setRequestProperty("Connection", "Keep-Alive");
14
            connection.setRequestProperty("User-Agent", "Mozilla/5.0"); //可以不设置,但部分网站会对该字段进行检查,以过滤非浏览器的请求
15
            //设置请求体内容
16
            if (postContent != null) {
17
                OutputStream out = connection.getOutputStream();
18
                out.write(postContent.getBytes());
19
                out.close();
20
            }
21

22
            //step2:建立连接
23
            //建立连接
24
            connection.connect();
25

26
        } catch (MalformedURLException e) {
27
            e.printStackTrace();
28
        } catch (IOException e) {
29
            e.printStackTrace();
30
        }
31
        System.out.println("============sendRequest end============");
32
    }
public static void main(String[] args) {
//为了Fiddler抓包而设置的代理
System.setProperty("http.proxyHost", "localhost");
System.setProperty("http.proxyPort", "8888"); //test code
String link = "http://www.sctywd.com/login.jsp";
sendRequest(link, "GET", null); //无法发出HTTP请求
}
x
 
1
public static void main(String[] args) {
2
    //为了Fiddler抓包而设置的代理
3
    System.setProperty("http.proxyHost", "localhost");
4
    System.setProperty("http.proxyPort", "8888");
5

6
    //test code
7
    String link = "http://www.sctywd.com/login.jsp";
8
    sendRequest(link, "GET", null); //无法发出HTTP请求
9
}

那么connect()有什么作用呢?看下源码中方法的说明:
 
由以上,我们大概可以看出几个信息:
  • connect()可以多次调用,但第一次之后的调用都会被自动忽略,即只会调用一次
  • 请求信息只能在连接建立之前修改,一旦连接建立,再试图修改请求信息会出现错误
  • 建立在连接之上的一些方法,会隐性地调用connect()

那么我们测试一下,已经调用connect,但是在正式发送请求之前,我尝试去修改请求头的配置,结果如之上描述的抛出了异常:
connection.connect();
connection.setRequestMethod("GET");//抛出异常 java.net.ProtocolException: Can't reset method: already connected
 
1
connection.connect();
2
connection.setRequestMethod("GET");//抛出异常 java.net.ProtocolException: Can't reset method: already connected
 
而且之上也提到过,依赖于连接建立的一些方法如 getOutputStream 和 getInputStream 等,其内部都会隐式的调用 connect(),也就是说,一旦这些方法调用之后,同样再试图更改请求头信息,也是不可以的

引用某网友: “connect()函数会根据HttpURLConnection对象的配置值生成http头部信息,因此在调用connect函数之前,就必须把所有的配置准备好”。

最后,connect既会隐性地调用,又会因为顺序可能抛出异常,我们要不要主动显式地调用呢,个人建议主动调用,明确顺序流程,避免埋藏一些隐性的bug。

5、其他

HttpURLConnection是JDK自带的网络请求工具,定义在java.net包下,当然,类似的还有HttpsURLConnection。实际上在Java中模拟网络连接,还有Apache的HttpClient和Square公司的OkHttp,此处仅作抛砖,供未来和他人引玉了。


6、参考链接



[02] URL和HttpURLConnection类的更多相关文章

  1. HttpURLConnection类

    导语 java.net.HttpURLConnectin类是URLConnection类的抽象子类.它在处理协议为HTTP的URL时特别有效.具体而言,它通过它可以获取和设置请求方法,确定是否重定向, ...

  2. JDK下sun.net.www.protocol.http.HttpURLConnection类-----Http客户端实现类的实现分析

    HttpClient类是进行TCP连接的实现类, package sun.net.www.http; import java.io.*; import java.net.*; import java. ...

  3. 一个用php实现的获取URL信息的类

    获取URL信息的类 使用这个类,你能获得URL的如下信息: - Host  - Path  - Statuscode (eg. 404,200, ...)  - HTTP Version  - Ser ...

  4. HttpURLConnection类的使用

    此类以获取天气的一个api地址为例: package javaexcjs; import java.io.BufferedReader; import java.io.OutputStreamWrit ...

  5. url提交参数类

    url提交参数类 type /// <summary> /// 准备url /// </summary> TynUrl = class private FUrl, FComma ...

  6. UrlUtils工具类,Java URL工具类,Java URL链接工具类

    UrlUtils工具类,Java URL工具类,Java URL链接工具类 >>>>>>>>>>>>>>>&g ...

  7. Django 02 url路由配置及渲染方式

    Django 02 url路由配置及渲染方式 一.URL #URL #(Uniform Resoure Locator) 统一资源定位符:对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是 ...

  8. HttpURLConnection访问url的工具类

    java代码: import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; ...

  9. Android基于HttpUrlConnection类的文件下载

    /** * get方法的文件下载 * <p> * 特别说明 android中的progressBar是google唯一的做了处理的可以在子线程中更新UI的控件 * * @param pat ...

随机推荐

  1. Docker-compose networks 的例子

    今天实验了下 docker 下的网络设置,记录一下过程,以免后面忘记. (系统:Centos 7.4 ,docker 版本:18.03.1-ce, docker-compose version 1.1 ...

  2. FullCalendar – jQuery Event Calendar in ASP.NET

    https://github.com/esausilva/ASP.Net-EventCalendar http://trentrichardson.com/examples/timepicker/ h ...

  3. 2017-12-01 中英文代码对比之ZLOGO 4 & LOGO

    基于前文中文编程语言之Z语言初尝试: ZLOGO 4的一些评论, 此文尝试作一个非常简单的代码对比, 使讨论更加有实例根据. 下图是节选自前文最后的示例代码, 由于选取的对照LOGO版本 (alanc ...

  4. 【读书笔记】iOS-Objective-C编程

    Objective-C中的类可以继承自任何一个顶级类,需要注意的是,虽然NSObject是最常见的顶级类,但是它并不是唯一的顶级类,例如,NSProxy就是和NSObject一样的顶级类,所以你不能说 ...

  5. 1145.cn 百度MIP适配实例

    MIP,全称Mobile Instant Pages(移动端即时页面),是百度推出的一套移动端网页开放技术标准.网站移动端页面统计MIP改造,能实现页面缓存,从而达到移动网页加速效果. 百度官方已经明 ...

  6. MVC与单元测试实践之健身网站(五)-系统信息、前台入口

    Fit项目停滞了一段时间,现在继续吧.上一篇完成了动作文本和配图的添加.编辑等内容.接下来要完成的是后台的最后一个模块:系统信息:以及前台的入口:关于注册.登录.修改密码等. 一 系统信息 a) 用户 ...

  7. 洗礼灵魂,修炼python(10)--有趣的判断分支+从实例中掌握循环语句

    所有的编程语言里都有判断语句和循环语句. 判断语句则是用来分支程序流程的 循环语句则是为了实现一个效果,让程序的规律性的重复操作 不用说,分支和循环自然在python里也是有的 一,条件判断:if,i ...

  8. 【PAT】B1075 链表元素分类(25 分)

    这道题算有点难,心目中理想的难度. 不能前怕狼后怕虎,一会担心超时,一会又担心内存过大,直接撸 将三部分分别保存到vector 有意思的在于输出 分别输出第一个的add和num 中间输出nextadd ...

  9. 【16】有关python面向对象编程

    面向对象编程 一.第一个案例---创建类 #__author:"吉" #date: 2018/10/27 0027 #function: # 设计类: ''' 1 类名:首字母大写 ...

  10. 类装载器-ClassLoader

    类装载器的工作机制 类装载器就是寻找类的字节码文件并构造出类在JVM内部表示对象的组件.在Java中,类装载器把一个类装入JVM中,需要经过以下步骤: 装载:查找和导入Class文件. 链接:执行校验 ...