Java中的HTTP客户端工具——原生HttpURLConnection

HTTP(超文本传输协议)是一种应用层协议,用于客户端和服务端进行通信,按照标准格式如JSON、XML等进行网络数据的传输,通常也作为应用程序之间以RESTAPI形式进行通信的常用协议。
在Java应用中需要调用其他应用提供的HTTP服务API时,通常需要使用一些HTTP客户端组件。

Java原生HttpURLConnection

URLConnection

抽象类URLConnection是表示应用程序和 URL 之间的通信链接的所有类的超类。此类的实例可用于读取和写入 URL 引用的资源。

public abstract class URLConnection {
    //访问地址的URL
    protected URL url;
   // 设置是否向HttpURLConnection输出
    protected boolean doInput = true;
   // 设置是否从HttpUrlConnection读入
    protected boolean doOutput = false;
   //是否允许用户交互(例如弹出身份验证对话框)有意义的上下文中检查此URL 。
    private static boolean defaultAllowUserInteraction = false;
    protected boolean allowUserInteraction = defaultAllowUserInteraction;
    // 设置是否使用缓存,默认是使用
    private static boolean defaultUseCaches = true;
    protected boolean useCaches = defaultUseCaches;
    //一些协议支持跳过对象的获取,除非对象在某个时间之后被修改过。默认必须始终进行提取。
    protected long ifModifiedSince = 0;
   //如果为false ,则此连接对象尚未创建到指定 URL 的通信链接。如果为true ,则通信链路已建立。
    protected boolean connected = false;
    // 设置超时时间
    private int connectTimeout;
    private int readTimeout;
    //请求头
    private MessageHeader requests;

    private static volatile FileNameMap fileNameMap;
    //如果此类连接尚未建立,则打开指向此 URL 引用的资源的通信链接。
    //如果在连接已打开时调用connect方法(由值为true的connected字段指示),则忽略该调用。
    abstract public void connect() throws IOException;
    //从此打开的连接中读取的输入流
    public InputStream getInputStream() throws IOException {
        throw new UnknownServiceException("protocol doesn't support input");
    }
   //写入此连接的输出流。
    public OutputStream getOutputStream() throws IOException {
        throw new UnknownServiceException("protocol doesn't support output");
    }
    //设置一般请求属性。
    //注意:有多个具有相同键的实例的请求属性使用逗号分隔的列表语法,将多个属性附加到单个属性中。
    public void setRequestProperty(String key, String value) {
        if (connected)
            throw new IllegalStateException("Already connected");
        if (key == null)
            throw new NullPointerException ("key is null");
        if (requests == null)
            requests = new MessageHeader();
        requests.set(key, value);
    }
}

HttpURLConnection :支持 HTTP 特定功能的 URLConnection。Java提供的发起HTTP请求的基础类库,提供了HTTP请求的基本能力

任何网络连接都需要经过socket才能连接,HttpURLConnection不需要设置socket,HttpURLConnection并不是底层的连接,而是在底层连接上的一个请求。
所以HttpURLConneciton只是一个抽象类,自身不能被实例化。

HttpURLConnection只能通过URL.openConnection()方法创建具体的实例。

abstract public class HttpURLConnection extends URLConnection {
    //默认方法是 GET
    protected String method = "GET";
    //HTTP 状态码
    protected int responseCode = -1;
   //响应消息
    protected String responseMessage = null;
    //如果为true ,协议将自动遵循重定向。如果为false ,协议将不会自动跟随重定向。
    private static boolean followRedirects = true;
    protected boolean instanceFollowRedirects = followRedirects;

    public void setInstanceFollowRedirects(boolean followRedirects) {
        instanceFollowRedirects = followRedirects;
     }

     public boolean getInstanceFollowRedirects() {
         return instanceFollowRedirects;
     }

    //合法的HTTP请求方法
    private static final String[] methods = {
        "GET", "POST", "HEAD", "OPTIONS", "PUT", "DELETE", "TRACE"
    };

    //设置 URL 请求的方法,默认方法是 GET。
    public void setRequestMethod(String method) throws ProtocolException {
        if (connected) {
            throw new ProtocolException("Can't reset method: already connected");
        }
        for (int i = 0; i < methods.length; i++) {
            if (methods[i].equals(method)) {
                if (method.equals("TRACE")) {
                    SecurityManager s = System.getSecurityManager();
                    if (s != null) {
                        s.checkPermission(new NetPermission("allowHttpTrace"));
                    }
                }
                this.method = method;
                return;
            }
        }
        throw new ProtocolException("Invalid HTTP method: " + method);
    }

    //从 HTTP 响应消息中获取状态代码。如果无法从响应中识别出任何代码(即响应不是有效的 HTTP),则返回 -1。
    public int getResponseCode() throws IOException {
        if (responseCode != -1) {
            return responseCode;
        }
        Exception exc = null;
        try {
            getInputStream();
        } catch (Exception e) {
            exc = e;
        }
        String statusLine = getHeaderField(0);
        if (statusLine == null) {
            if (exc != null) {
                if (exc instanceof RuntimeException)
                    throw (RuntimeException)exc;
                else
                    throw (IOException)exc;
            }
            return -1;
        }
        if (statusLine.startsWith("HTTP/1.")) {
            int codePos = statusLine.indexOf(' ');
            if (codePos > 0) {

                int phrasePos = statusLine.indexOf(' ', codePos+1);
                if (phrasePos > 0 && phrasePos < statusLine.length()) {
                    responseMessage = statusLine.substring(phrasePos+1);
                }
                if (phrasePos < 0)
                    phrasePos = statusLine.length();
                try {
                    responseCode = Integer.parseInt
                            (statusLine.substring(codePos+1, phrasePos));
                    return responseCode;
                } catch (NumberFormatException e) { }
            }
        }
        return -1;
    }
    //获取与响应代码一起从服务器返回的 HTTP 响应消息(如果有)
    public String getResponseMessage() throws IOException {
        getResponseCode();
        return responseMessage;
    }

    @SuppressWarnings("deprecation")
    public long getHeaderFieldDate(String name, long Default) {
        String dateString = getHeaderField(name);
        try {
            if (dateString.indexOf("GMT") == -1) {
                dateString = dateString+" GMT";
            }
            return Date.parse(dateString);
        } catch (Exception e) {
        }
        return Default;
    }
    //断开连接
    public abstract void disconnect();
    //使用代理
    public abstract boolean usingProxy();

    //HTTP1.1的状态码
    public static final int HTTP_OK = 200;
    public static final int HTTP_CREATED = 201;
    public static final int HTTP_ACCEPTED = 202;
    public static final int HTTP_NOT_AUTHORITATIVE = 203;
    public static final int HTTP_NO_CONTENT = 204;
    public static final int HTTP_RESET = 205;
    public static final int HTTP_PARTIAL = 206;

    public static final int HTTP_MULT_CHOICE = 300;
    public static final int HTTP_MOVED_PERM = 301;
    public static final int HTTP_MOVED_TEMP = 302;
    public static final int HTTP_SEE_OTHER = 303;
    public static final int HTTP_NOT_MODIFIED = 304;
    public static final int HTTP_USE_PROXY = 305;

    public static final int HTTP_BAD_REQUEST = 400;
    public static final int HTTP_UNAUTHORIZED = 401;
    public static final int HTTP_PAYMENT_REQUIRED = 402;
    public static final int HTTP_FORBIDDEN = 403;
    public static final int HTTP_NOT_FOUND = 404;
    public static final int HTTP_BAD_METHOD = 405;
    public static final int HTTP_NOT_ACCEPTABLE = 406;
    public static final int HTTP_PROXY_AUTH = 407;
    public static final int HTTP_CLIENT_TIMEOUT = 408;
    public static final int HTTP_CONFLICT = 409;
    public static final int HTTP_GONE = 410;
    public static final int HTTP_LENGTH_REQUIRED = 411;
    public static final int HTTP_PRECON_FAILED = 412;
    public static final int HTTP_ENTITY_TOO_LARGE = 413;
    public static final int HTTP_REQ_TOO_LONG = 414;
    public static final int HTTP_UNSUPPORTED_TYPE = 415;

    @Deprecated
    public static final int HTTP_SERVER_ERROR = 500;
    public static final int HTTP_INTERNAL_ERROR = 500;
    public static final int HTTP_NOT_IMPLEMENTED = 501;
    public static final int HTTP_BAD_GATEWAY = 502;
    public static final int HTTP_UNAVAILABLE = 503;
    public static final int HTTP_GATEWAY_TIMEOUT = 504;
    public static final int HTTP_VERSION = 505;
}

HTTP常见状态码:https://blog.csdn.net/wounler/article/details/120269477
HttpsURLConnection :支持 HTTPS 特定功能的 URLConnection。

public abstract class HttpsURLConnection extends HttpURLConnection {
    private static HostnameVerifier defaultHostnameVerifier = new DefaultHostnameVerifier();
    protected HostnameVerifier hostnameVerifier;
    private static SSLSocketFactory defaultSSLSocketFactory = null;
    private SSLSocketFactory sslSocketFactory;

    protected HttpsURLConnection(URL var1) {
        super(var1);
        this.hostnameVerifier = defaultHostnameVerifier;
        this.sslSocketFactory = getDefaultSSLSocketFactory();
    }

    public abstract String getCipherSuite();

    public abstract Certificate[] getLocalCertificates();

    public abstract Certificate[] getServerCertificates() throws SSLPeerUnverifiedException;

    public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
        Certificate[] var1 = this.getServerCertificates();
        return ((X509Certificate)var1[0]).getSubjectX500Principal();
    }

    public Principal getLocalPrincipal() {
        Certificate[] var1 = this.getLocalCertificates();
        return var1 != null ? ((X509Certificate)var1[0]).getSubjectX500Principal() : null;
    }

    public static void setDefaultHostnameVerifier(HostnameVerifier var0) {
        if (var0 == null) {
            throw new IllegalArgumentException("no default HostnameVerifier specified");
        } else {
            SecurityManager var1 = System.getSecurityManager();
            if (var1 != null) {
                var1.checkPermission(new SSLPermission("setHostnameVerifier"));
            }

            defaultHostnameVerifier = var0;
        }
    }

    public static HostnameVerifier getDefaultHostnameVerifier() {
        return defaultHostnameVerifier;
    }

    public void setHostnameVerifier(HostnameVerifier var1) {
        if (var1 == null) {
            throw new IllegalArgumentException("no HostnameVerifier specified");
        } else {
            this.hostnameVerifier = var1;
        }
    }

    public HostnameVerifier getHostnameVerifier() {
        return this.hostnameVerifier;
    }

    public static void setDefaultSSLSocketFactory(SSLSocketFactory var0) {
        if (var0 == null) {
            throw new IllegalArgumentException("no default SSLSocketFactory specified");
        } else {
            SecurityManager var1 = System.getSecurityManager();
            if (var1 != null) {
                var1.checkSetFactory();
            }

            defaultSSLSocketFactory = var0;
        }
    }

    public static SSLSocketFactory getDefaultSSLSocketFactory() {
        if (defaultSSLSocketFactory == null) {
            defaultSSLSocketFactory = (SSLSocketFactory)SSLSocketFactory.getDefault();
        }

        return defaultSSLSocketFactory;
    }

    public void setSSLSocketFactory(SSLSocketFactory var1) {
        if (var1 == null) {
            throw new IllegalArgumentException("no SSLSocketFactory specified");
        } else {
            SecurityManager var2 = System.getSecurityManager();
            if (var2 != null) {
                var2.checkSetFactory();
            }

            this.sslSocketFactory = var1;
        }
    }

    public SSLSocketFactory getSSLSocketFactory() {
        return this.sslSocketFactory;
    }

    private static class DefaultHostnameVerifier implements HostnameVerifier {
        private DefaultHostnameVerifier() {
        }

        public boolean verify(String var1, SSLSession var2) {
            return false;
        }
    }
}

JarURLConnection:与 Java ARchive (JAR) 文件或 JAR 文件中的条目的 URL 连接。

public abstract class JarURLConnection extends URLConnection {

    private URL jarFileURL;
    private String entryName;

    protected URLConnection jarFileURLConnection;

    protected JarURLConnection(URL url) throws MalformedURLException {
        super(url);
        parseSpecs(url);
    }

    private void parseSpecs(URL url) throws MalformedURLException {
        String spec = url.getFile();
        int separator = spec.indexOf("!/");
        if (separator == -1) {
            throw new MalformedURLException("no !/ found in url spec:" + spec);
        }
        jarFileURL = new URL(spec.substring(0, separator++));
        entryName = null;
        if (++separator != spec.length()) {
            entryName = spec.substring(separator, spec.length());
            entryName = ParseUtil.decode (entryName);
        }
    }

    public URL getJarFileURL() {
        return jarFileURL;
    }

    public String getEntryName() {
        return entryName;
    }

    public abstract JarFile getJarFile() throws IOException;

    public Manifest getManifest() throws IOException {
        return getJarFile().getManifest();
    }

    public JarEntry getJarEntry() throws IOException {
        return getJarFile().getJarEntry(entryName);
    }

    public Attributes getAttributes() throws IOException {
        JarEntry e = getJarEntry();
        return e != null ? e.getAttributes() : null;
    }

    public Attributes getMainAttributes() throws IOException {
        Manifest man = getManifest();
        return man != null ? man.getMainAttributes() : null;
    }

    public java.security.cert.Certificate[] getCertificates()
         throws IOException
    {
        JarEntry e = getJarEntry();
        return e != null ? e.getCertificates() : null;
    }
}

HttpURLConnection进行POST请求例子:

try {
    // 1. 获取请求地址
    URL url = new URL("请求地址");
    // 2. 得到网络访问对象java.net.HttpURLConnection
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    // 3. 设置请求参数(过期时间,输入、输出流、访问方式),以流的形式进行连接 
    connection.setDoOutput(false);
    connection.setDoInput(true);
    connection.setRequestMethod("POST");
    connection.setUseCaches(true);
    connection.setInstanceFollowRedirects(true);
    connection.setConnectTimeout(3000);
    connection.setRequestProperty("Content-Type", "application/json;charset=utf-8");
    connection.connect();
    //4. 入参处理
    String body = "入参";
    BufferedWriter writer = new BufferedWriter(
                  new OutputStreamWriter(connection.getOutputStream(), "UTF-8"));
    writer.write(body);
    writer.close();
    // 5. 得到响应状态码的返回值 responseCode
    int code = connection.getResponseCode();
    // 6. 如果返回值正常,数据在网络中是以流的形式得到服务端返回的数据
    String msg = "";
    if (code == 200) { // 正常响应
    BufferedReader reader = new BufferedReader(
                    new InputStreamReader(connection.getInputStream()));
    String line = null;
    while ((line = reader.readLine()) != null) { // 循环从流中读取
         msg += line + "\n";
         }
     reader.close(); // 关闭流
    }
    // 7. 断开连接,释放资源
    connection.disconnect();
} catch (IOException e) {
     e.printStackTrace();
}

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

HttpURLConnection相关博客推荐

上传文件:https://blog.csdn.net/u010957645/article/details/86062741
下载文件:https://blog.csdn.net/zhoukun1008/article/details/79569942

ps:FileOutputStream读取流的时候如果是文件夹,就会出错,无论怎么读,都拒绝访问,应该在读取的目录后面加上文件名!

调用链路源码分析可以参考:
https://blog.csdn.net/u012504392/article/details/109432655

你可能感兴趣的:(Java中的HTTP客户端工具——原生HttpURLConnection)