zoukankan      html  css  js  c++  java
  • [02] URL和HttpURLConnection类


    1、URL的概念

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

    URL的基本格式是:
    <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对象

    有了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============");
        }


    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============");
        }
    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请求
    }

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

    那么我们测试一下,已经调用connect,但是在正式发送请求之前,我尝试去修改请求头的配置,结果如之上描述的抛出了异常:
    connection.connect();
    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、参考链接



  • 相关阅读:
    Python学习(四十一)—— Djago进阶
    Python学习(四十)—— Djago之认证系统
    Python学习(三十九)—— Django之Form组件
    Python学习(三十八)—— Djago之Ajax
    Python学习(三十七)—— 模板语言之自定义filter和中间件
    Python学习(三十六)—— Cookie、Session和自定义分页
    Python学习(三十五)—— Django之ORM训练专题
    Python学习(三十四)—— Django之ORM之单表、联表操作
    Python学习(三十三)—— Django之ORM
    JavaSE学习心得笔记(持续更新)
  • 原文地址:https://www.cnblogs.com/deng-cc/p/8395377.html
Copyright © 2011-2022 走看看