java网络学习之URLConnection类学习(3)

URLConnection概述

URLConnection是一个抽象类,URL对象通过openConnection()可以获得的URLConnection的子类实现的连接对象,URL对象支持7种协议。所以子类实现有FileURLConnection,FtpURLConnection,HttpURLConnection,HttpsURLConnection,JarURLConnection,MailToURLConnection 这几种连接对象,在包sun.net.www.protocol包下面。

 

任何URLConnection子类实现的网络连接都需要经过底层socket才能连接。

虽然底层的网络连接可以被多个HttpURLConnection实例共享,但每一个HttpURLConnection实例只能发送一个请求。请求结束之后,应该调用HttpURLConnection实例的InputStream或OutputStream的close()方法以释放请求的网络资源,不过这种方式对于持久化连接没用。对于持久化连接,得用disconnect()方法关闭底层连接的socket。
 

访问一个url的步骤如下:

1创建一个URL对象
2从URL 获取URLConnection对象
3配置URLConnection
4获取响应头字段
5获取输入流并读取数据
6获取输出流并写入数据
7关闭连接
步骤3至6是可选的,步骤5和6是可互换的。

配置URLConnection详解
在实际建立连接之前,我们可以配置影响客户端和服务器之间正在进行的通信的各个方面,例如超时,缓存,HTTP请求方法等。

该URLConnection的类提供了配置连接下面的方法:setConnectTimeout(int timeout):设置连接超时(以毫秒为单位)。一个java.net.SocketTimeoutException如果超时可以建立连接之前被抛出。超时为零表示无限超时(默认值)。
setReadTimeout(int timeout):设置读取超时(以毫秒为单位)。超时到期后,没有可用于从连接的输入流中读取的数据,将引发SocketTimeoutException。超时为零表示无限超时(默认值)。
setDefaultUseCaches(boolean default):设置URLConnection是否默认使用缓存(默认为true)。此方法会影响URLConnection类的未来实例。
setUseCaches(boolean useCaches):设置此连接是否使用缓存(默认为true)。
setDoInput(boolean doInput):设置此URLConnection是否可用于从服务器读取内容(默认为true)。
setDoOutput(boolean doOutput):设置是否可以使用此URLConnection将数据发送到服务器(默认为false)。
setIfModifiedSince(long time):设置客户端检索的内容的最后修改时间,主要用于HTTP协议。例如,如果服务器发现内容自指定时间以来没有改变,则它不获取内容并返回状态代码304-未修改。如果客户端的修改时间超过了指定时间,则客户端将获得新鲜内容。
setAllowUserInteraction(boolean allow):启用或禁用用户交互,例如,如果需要,弹出一个身份验证对话框(默认为false)。
setDefaultAllowUserInteraction(boolean default):为所有未来的URLConnection对象设置用户交互的默认值。
setRequestProperty(String key,String value):设置由key = value对指定的常规请求属性。如果key已存在,则旧值将被新值覆盖。
HTTP请求允许一个key带多个用逗号分开的values,但是HttpURLConnection只提供了单个操作的方法:

addRequestProperty(key,value)
setRequestProperty和addRequestProperty的区别就是,setRequestProperty会覆盖已经存在的key的所有values,有清零重新赋值的作用。而addRequestProperty则是在原来key的基础上继续添加其他value。

请注意,应在建立连接之前调用这些方法。如果已建立连接,则某些方法会抛出IllegalStateException。

此外,子类HttpURLConnection提供了以下用于使用HTTP特定功能配置连接的方法

setRequestMethod(String method):设置URL请求的方法,它是HTTP方法GET,POST,HEAD,OPTIONS,PUT,DELETE和TRACE之一。默认方法是GET(注意:这里方法类型必须大写)。
setChunkedStreamingMode(int chunkLength):当高于内容未知内容长度时,启用HTTP请求主体的流式传输而不进行内部缓冲。
setFixedLengthStreamingMode(long contentLength):当高于已知内容长度时,启用HTTP请求主体的流式传输而不进行内部缓冲。
setFollowRedirects(boolean follow):此静态方法设置是否应该由此类的未来对象自动跟随HTTP重定向(默认为true)。
setInstanceFollowRedirects(boolean follow):设置HTTP重定向是否应该自动跟随此HttpURLConnection类的实例(默认为true)。

 

获取响应头字段
建立连接后,服务器将处理URL请求并返回包含响应头数据和实际内容的响应。

响应头字段显示有关服务器,状态代码,协议信息等的信息。

实际内容可以是文本,HTML,图像等,具体取决于文档的类型。

所述的URLConnection类提供用于读取头字段下面的方法:

Map<String, List<String>> getHeaderFields():返回包含所有标题字段的映射。键是字段名称,值是String列表,表示相应的字段值。
String getHeaderField(int n):读取第n个头字段的值。
getHeaderField(String name):读取指定头字段的值。
getHeaderFieldKey(int n):读取第n个头字段的键。
getHeaderFieldDate(String name,long default):读取解析为Date的命名字段的值。如果字段丢失或值格式错误,则返回默认值。
getHeaderFieldInt(String name,int default):读取被解析为整数的命名字段的值。如果字段丢失或值格式错误,则返回默认值。
getHeaderFieldLong(String name,long default):读取被解析为长数字的命名字段的值。如果字段丢失或值格式错误,则返回默认值。
这些是读取任何标题字段的一般方法。对于一些经常访问的头字段,URLConnection类提供了更具体的方法:

getContentEncoding():读取内容编码头字段的值,该字段指示内容的编码类型。
getContentLength():读取content-length头字段的值,该字段指示内容的大小(以字节为单位)。
getContentType():读取content-type头字段的值,该字段指示内容的类型。
getDate():读取日期标题字段的值,该字段指示服务器上的日期时间。
getExpiration():读取expires头字段的值,指示响应被视为过时的时间。这是用于缓存控制。
getLastModified():读取上次修改的头字段的值,该字段指示内容的上次​​修改时间。
子类HttpURLConnection提供了另一种方法:

getResponseCode():返回服务器发送的HTTP状态码。
请注意,读取头字段时,将隐式建立连接,而不调用connect()。

 

读取实际内容可以使用方法:

  • getContent
  • getInputStream 

 

关闭连接
要关闭连接,请在InputStream或OutputStream对象上调用close()方法。这样做可以释放与URLConnection实例关联的网络资源。

 

下面是一个请求的列子:

import javax.net.ssl.*;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLEncoder;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.Map;
 
/**
 * @Author: bo zhang
 * @Description:
 * @Date:Create:in 2018-09-28 0:01
 * @Modified By:暂无
 */
public class HttpRequest {
 
    //忽略证书的认证
    private static void SkipCertificateValidation() throws Exception {
        TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
 
            public void checkClientTrusted(X509Certificate[] certs, String authType) {
            }
 
            public void checkServerTrusted(X509Certificate[] certs, String authType) {
            }
        }
        };

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HostnameVerifier allHostsValid = new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        };
        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
    }
 
    /**
     * 请求方式
     *
     * @param urlStr 接口请求链接
     * @return get请求直接返回请求的流
     * @throws Exception
     */
    public static InputStream getRequestWithAuth(String urlStr, Map<String, Object> params) throws Exception {
        SkipCertificateValidation();//https请求的时候跳过证书认证(得在创建con前面才有效)
        URL url = new URL(urlStr);
        HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
        con.setRequestMethod("GET");//注意这个get要大写
        /  "GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE"  有这些方法
        con.setRequestProperty("Content-type", "application/x-www-form-urlencoded");
 
        //采用base auth验证方式
        /*String authEncoded = Base64.getEncoder().encodeToString((authStr).getBytes());
        con.setRequestProperty("Authorization", "Basic " + authEncoded); //这个是必须要加的*/
        con.setDoInput(true);
        if (con.getResponseCode() != 200) {
            throw new RuntimeException("请求出错,状态码为:" + con.getResponseCode());
        }
        return con.getInputStream();
    }
 
    /**
     * 请求方式
     *
     * @param urlStr 接口请求链接
     * @return
     * @throws Exception
     */
    public static String postRequestWithAuth(String urlStr,  Map<String, Object> params) throws Exception {
        SkipCertificateValidation();//跳过证书认证
        URL url = new URL(urlStr);
        HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
        con.setRequestMethod("POST");
        con.setDoOutput(true);
        con.setDefaultUseCaches(false);//post请求不需要缓存
        con.setRequestProperty("Content-type", "application/x-www-form-urlencoded");
 
      /*  String authStr = user + ":" + pass;
        String authEncoded = Base64.getEncoder().encodeToString((authStr).getBytes());
        con.setRequestProperty("Authorization", "Basic " + authEncoded);*/
 
        //设置请求要加的参数
        SetRequestValue(con.getOutputStream(),params);
 
        con.setConnectTimeout(5000);
        int code = con.getResponseCode();
        if (code != 200) {
            return "接口发生未处理异常,请求状态码:"+code;
          //con.getErrorStream()  可获得出错后的  返回流
        }
        con.disconnect();//断开连接
        return "请求成功!";
    }
 
    //将参数拼接起来
    private static void SetRequestValue(OutputStream outputStream, Map<String, Object> params) throws IOException {
        if (params != null && params.size() > 0) {
            DataOutputStream out = new DataOutputStream(outputStream);
            String requestString = "";
            for (String key : params.keySet()) {
                requestString += key.trim() + "=" + URLEncoder.encode(params.get(key).toString(), "UTF-8").trim() + "&";
            }
            System.out.println("原始长度:" + requestString);
            System.out.println("需要的字符串:" + requestString.substring(0, requestString.length() - 1));
            out.writeBytes(requestString);
            out.flush();
            out.close();
        }
    }
}

总结:

  1. URLConnection的connect()函数,实际上只是建立了一个与服务器的tcp连接,并没有实际发送http请求。 
    无论是post还是get,http请求实际上直到HttpURLConnection的getInputStream()这个函数里面才正式发送出去。

  2. 在用POST方式发送URL请求时,URL请求参数的设定顺序是重中之重, 
    对connection对象的处理设置参数和一般请求属性和写入提交数据都必须要在connect()函数执行之前完成。对outputStream的写提交数据操作,必须要在inputStream的读操作之前。这些顺序实际上是由http请求的格式决定的。

  3. http请求实际上由两部分组成,一个是http头,所有关于此次http请求的配置都在http头里面定义,一个是正文content。connect()函数会根据HttpURLConnection对象的配置值生成http头部信息,因此在调用connect函数之前,就必须把所有的配置准备好。

  4. 在http头后面紧跟着的是http请求的正文,正文的内容是通过outputStream流写入的, 
    实际上outputStream不是一个网络流,充其量是个字符串流,往里面写入的东西不会立即发送到网络,而是存在于内存缓冲区中,待outputStream流关闭时,根据输入的内容生成http正文。至此,http请求的东西已经全部准备就绪。在getInputStream()函数调用的时候,就会把准备好的http请求正式发送到服务器了,然后返回一个输入流,用于读取服务器对于此次http请求的返回信息。由于http请求在getInputStream的时候已经发送出去了(包括http头和正文),因此在getInputStream()函数之后对connection对象进行设置(对http头的信息进行修改)或者写入outputStream(对正文进行修改)都是没有意义的了,执行这些操作会导致异常的发生

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值