如何使用 java.net.URLConnection 来触发和处理 HTTP 请求

发布于 2024-08-31 13:59:17 字数 632 浏览 6 评论 0 原文

使用 java.net.URLConnection 在这里经常被问到,Oracle 教程< /a> 太简洁了。

该教程基本上只展示了如何触发 GET 请求并读取响应。它没有在任何地方解释如何使用它来执行 POST 请求、设置请求标头、读取响应标头、处理 cookie、提交 HTML 表单、上传文件等。

那么,我如何使用 java.net.URLConnection 来触发和处理“高级” “HTTP 请求?

Use of java.net.URLConnection is asked about pretty often here, and the Oracle tutorial is too concise about it.

That tutorial basically only shows how to fire a GET request and read the response. It doesn't explain anywhere how to use it to, among others, perform a POST request, set request headers, read response headers, deal with cookies, submit a HTML form, upload a file, etc.

So, how can I use java.net.URLConnection to fire and handle "advanced" HTTP requests?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

感性 2024-09-07 13:59:17

首先预先声明:发布的代码片段都是基本示例。您需要处理简单的 IOExceptionRuntimeException,例如 NullPointerExceptionArrayIndexOutOfBoundsException 并自行处理。

如果您正在为 Android 而不是 Java 进行开发,另请注意,自引入 API 级别 28 以来,明文 HTTP 请求是 默认禁用。我们鼓励您使用HttpsURLConnection。确实有必要时,可以在应用程序清单中启用明文。


Java 11

如果您已经使用 Java 11 或更高版本,那么很高兴知道 java.net 旁边有.URLConnection 另一个以不太详细的方式处理 HTTP 请求的 API:java.net.http.HttpClient

准备

我们首先需要至少知道 URL 和字符集。这些参数是可选的,取决于功能要求。

String url = "http://example.com";
String charset = "UTF-8";  // Or in Java 7 and later, use the constant: java.nio.charset.StandardCharsets.UTF_8.name()
String param1 = "value1";
String param2 = "value2";
// ...

String query = String.format("param1=%s¶m2=%s",
    URLEncoder.encode(param1, charset),
    URLEncoder.encode(param2, charset));

查询参数必须采用name=value格式,并通过&连接。通常,您还可以使用 URL 编码 使用指定的字符集 URLEncoder#encode()

String#format()只是为了方便。当我需要字符串连接运算符 + 两次以上时,我更喜欢它。


触发 带有(可选)查询参数的 HTTP GET 请求

这是一项微不足道的任务。这是默认的请求方法。

URLConnection connection = new URL(url + "?" + query).openConnection();
connection.setRequestProperty("Accept-Charset", charset);
InputStream response = connection.getInputStream();
// ...

任何查询字符串都应使用 ? 连接到 URL。 接受字符集 标头可能会提示服务器参数的编码方式。如果您不发送任何查询字符串,则可以保留 Accept-Charset 标头。如果您不需要设置任何标头,那么您甚至可以使用 URL#openStream() 快捷方法。

InputStream response = new URL(url).openStream();
// ...

无论哪种方式,如果另一方是 HttpServlet< /code>,然后是 doGet() 方法将被调用,参数将由 HttpServletRequest#getParameter()

出于测试目的,您可以将响应正文打印到标准输出,如下所示下面:

try (Scanner scanner = new Scanner(response)) {
    String responseBody = scanner.useDelimiter("\\A").next();
    System.out.println(responseBody);
}

使用查询参数触发 HTTP POST

请求URLConnection#setDoOutput()true 隐式将请求方法设置为 POST。 Web 表单的标准 HTTP POST 类型为 application/x-www-form-urlencoded,其中查询字符串被写入请求正文。

URLConnection connection = new URL(url).openConnection();
connection.setDoOutput(true); // Triggers POST.
connection.setRequestProperty("Accept-Charset", charset);
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + charset);

try (OutputStream output = connection.getOutputStream()) {
    output.write(query.getBytes(charset));
}

InputStream response = connection.getInputStream();
// ...

注意:每当您想以编程方式提交 HTML 表单时,请不要忘记获取任何 name=value 对> 元素添加到查询字符串中,当然还有您想要“按”的 元素的 name=value 对“以编程方式(因为这通常在服务器端使用来区分是否按下了按钮,如果按下了,是哪个按钮)。

您还可以转换获得的 URLConnectionHttpURLConnection 并使用其 HttpURLConnection#setRequestMethod() 代替。但是,如果您尝试使用连接进行输出,您仍然需要设置 URLConnection#setDoOutput()true

HttpURLConnection httpConnection = (HttpURLConnection) new URL(url).openConnection();
httpConnection.setRequestMethod("POST");
// ...

无论哪种方式,如果另一方是 HttpServlet< /code>,然后是 doPost() 方法将被调用,参数将由 HttpServletRequest#getParameter()


实际触发 HTTP 请求

您可以使用 URLConnection#connect(),但是当您想要获取有关 HTTP 响应的任何信息时,该请求将根据需要自动触发,例如使用 URLConnection#getInputStream() 等等。上面的示例正是这样做的,因此 connect() 调用实际上是多余的。


超时

您可以使用 URLConnection#setConnectTimeout() 设置连接超时和URLConnection#setReadTimeout() 设置读取超时。

默认值基本上是“无超时”。所以你想自己设置这些。例如:

httpConnection.setConnectTimeout(3000); // 3s
httpConnection.setReadTimeout(6000); // 6s

但是,使用基于 Sun/Oracle 的 JRE 时,需要注意读取超时。它会在抛出超时异常之前默默地重试读取,很可能只是为了在缓存中准备好任何成功的读取。另请参阅 Android (Java) HttpURLConnection 在“读取”时静默重试' timeout 这对于 GET 来说还可以,但对于 POST 来说绝对是错误的。如果您使用的是基于 Sun/Oracle 的 JRE,则需要按如下方式关闭它:

System.setProperty("sun.net.http.retryPost", "false")

如果您正在为 Android 编写代码,则上述内容将不起作用,您需要在 POST 上进行此解决方案:

httpConnection.setChunkedStreamingMode(0);

这将仅轻微影响性能。如果不希望出现这种情况,请考虑切换到其他 HTTP 客户端,例如 OkHttp


收集 HTTP 响应信息

  1. HTTP 响应状态

您需要 HttpURLConnection 在这里。如有必要,请先施放。

    int status = httpConnection.getResponseCode();
  1. HTTP 响应标头

     for (Entry> header : connection.getHeaderFields().entrySet()) {
         System.out.println(header.getKey() + "=" + header.getValue());
     }
    
  2. HTTP 响应编码

Content -Type 包含一个 charset 参数,那么响应正文可能是基于文本的,我们希望使用服务器端指定的字符编码来处理响应正文。

    String contentType = connection.getHeaderField("Content-Type");
    String charset = null;

    for (String param : contentType.replace(" ", "").split(";")) {
        if (param.startsWith("charset=")) {
            charset = param.split("=", 2)[1];
            break;
        }
    }

    if (charset != null) {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(response, charset))) {
            for (String line; (line = reader.readLine()) != null;) {
                // ... System.out.println(line)?
            }
        }
    } else {
        // It's likely binary content, use InputStream/OutputStream.
    }

维护会话

服务器端会话通常由 cookie 支持。某些 Web 表单要求您登录和/或由会话跟踪。您可以使用 CookieHandler 用于维护 cookie 的 API。您需要准备一个 CookieManager 带有 ACCEPT_ALL 在发送所有 HTTP 请求之前。

// First set the default cookie manager.
CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));

// All the following subsequent URLConnections will use the same cookie manager.
URLConnection connection = new URL(url).openConnection();
// ...

connection = new URL(url).openConnection();
// ...

connection = new URL(url).openConnection();
// ...

请注意,众所周知,这并不总是在所有情况下都能正常工作。如果您失败了,那么最好是手动收集并设置 cookie 标头。您基本上需要从登录响应或第一个 GET 请求中获取所有 Set-Cookie 标头,然后将其传递给后续请求。

// Gather all cookies on the first request.
URLConnection connection = new URL(url).openConnection();
List<String> cookies = connection.getHeaderFields().get("Set-Cookie");
// ...

// Then use the same cookies on all subsequent requests.
connection = new URL(url).openConnection();
for (String cookie : cookies) {
    connection.addRequestProperty("Cookie", cookie.split(";", 2)[0]);
}
// ...

split(";", 2)[0] 用于删除与服务器端无关的 cookie 属性,例如 expirespathcookie.substring(0, cookie.indexOf(';')) 而不是 split()


流模式

< code>HttpURLConnection 默认情况下会在实际发送之前缓冲整个请求正文,无论您是否使用 connection.setRequestProperty 自己设置固定内容长度(“内容长度”,内容长度);。每当您同时发送大型 POST 请求(例如上传文件)时,这可能会导致 OutOfMemoryException。为了避免这种情况,您需要设置 HttpURLConnection#setFixedLengthStreamingMode()

httpConnection.setFixedLengthStreamingMode(contentLength);

但是,如果事先确实不知道内容长度,那么您可以通过设置 HttpURLConnection#setChunkedStreamingMode() 相应地。这将设置 HTTP 传输编码 header 到 chunked 这将强制请求正文以块的形式发送。下面的示例将以 1 KB 为单位发送正文。

httpConnection.setChunkedStreamingMode(1024);

用户代理

请求可能会返回意外响应,而它与真正的网络浏览器配合良好。服务器端可能根据 User 阻止请求-Agent 请求标头。 URLConnection 默认情况下将其设置为 Java/1.6.0_19,其中最后一部分显然是 JRE 版本。您可以按如下方式覆盖此设置:

connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"); // Do as if you're using Chrome 41 on Windows 7.

使用最近使用的浏览器中的用户代理字符串。


错误处理

如果 HTTP 响应代码为 4nn(客户端错误)或 5nn(服务器错误),那么您可能需要读取 HttpURLConnection#getErrorStream()< /code> 查看服务器是否发送了任何有用的错误信息。

InputStream error = ((HttpURLConnection) connection).getErrorStream();

如果 HTTP 响应代码为 -1,则连接和响应处理出现问题。旧版 JRE 中的 HttpURLConnection 实现在保持连接活动方面存在一些问题。您可能想通过将 http.keepAlive 系统属性设置为 false 来关闭它。您可以在应用程序的开头以编程方式执行此操作,方法是:

System.setProperty("http.keepAlive", "false");

上传文件

您通常使用 multipart/form-data 混合 POST 内容(二进制和字符数据)的编码。 RFC2388 中更详细地描述了编码。

String param = "value";
File textFile = new File("/path/to/file.txt");
File binaryFile = new File("/path/to/file.bin");
String boundary = Long.toHexString(System.currentTimeMillis()); // Just generate some unique random value.
String CRLF = "\r\n"; // Line separator required by multipart/form-data.
URLConnection connection = new URL(url).openConnection();
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);

try (
    OutputStream output = connection.getOutputStream();
    PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, charset), true);
) {
    // Send normal param.
    writer.append("--" + boundary).append(CRLF);
    writer.append("Content-Disposition: form-data; name=\"param\"").append(CRLF);
    writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF);
    writer.append(CRLF).append(param).append(CRLF).flush();

    // Send text file.
    writer.append("--" + boundary).append(CRLF);
    writer.append("Content-Disposition: form-data; name=\"textFile\"; filename=\"" + textFile.getName() + "\"").append(CRLF);
    writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF); // Text file itself must be saved in this charset!
    writer.append(CRLF).flush();
    Files.copy(textFile.toPath(), output);
    output.flush(); // Important before continuing with writer!
    writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary.

    // Send binary file.
    writer.append("--" + boundary).append(CRLF);
    writer.append("Content-Disposition: form-data; name=\"binaryFile\"; filename=\"" + binaryFile.getName() + "\"").append(CRLF);
    writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(binaryFile.getName())).append(CRLF);
    writer.append("Content-Transfer-Encoding: binary").append(CRLF);
    writer.append(CRLF).flush();
    Files.copy(binaryFile.toPath(), output);
    output.flush(); // Important before continuing with writer!
    writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary.

    // End of multipart/form-data.
    writer.append("--" + boundary + "--").append(CRLF).flush();
}

如果另一方是 HttpServlet ,然后是 doPost() 方法将被调用,并且这些部分将由 HttpServletRequest#getPart() (注,因此不是 getParameter() 等等!)。另请参阅此答案了解示例。


处理不受信任或配置错误的 HTTPS 站点

如果您正在为 Android 而不是 Java 进行开发,要小心:如果您没有正确的证书,下面的解决方法可能会节省您的时间开发期间部署。但您不应该将其用于生产。如今(2021 年 4 月),如果 Google 检测到不安全的主机名验证程序,将不允许您的应用在 Play 商店上分发,请参阅 https://support.google.com/faqs/answer/7188426.

有时您需要连接 HTTPS 网址,可能是因为您正在编写网络爬虫。在这种情况下,您可能会在某些未及时更新 SSL 证书的 HTTPS 站点上遇到 javax.net.ssl.SSLException: Not Trusted servercertificate ,或者 java .security.cert.CertificateException:在某些配置错误的 HTTPS 站点上找不到与 [主机名] 匹配的主题备用 DNS 名称javax.net.ssl.SSLProtocolException:握手警报:unrecognized_name

Web scraper 类中的以下一次性运行的static 初始化程序应该使 HttpsURLConnection 对这些 HTTPS 站点更加宽松,从而不再抛出这些异常。

static {
    TrustManager[] trustAllCertificates = new TrustManager[] {
        new X509TrustManager() {
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null; // Not relevant.
            }
            @Override
            public void checkClientTrusted(X509Certificate[] certs, String authType) {
                // Do nothing. Just allow them all.
            }
            @Override
            public void checkServerTrusted(X509Certificate[] certs, String authType) {
                // Do nothing. Just allow them all.
            }
        }
    };

    HostnameVerifier trustAllHostnames = new HostnameVerifier() {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true; // Just allow them all.
        }
    };

    try {
        System.setProperty("jsse.enableSNIExtension", "false");
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCertificates, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HttpsURLConnection.setDefaultHostnameVerifier(trustAllHostnames);
    }
    catch (GeneralSecurityException e) {
        throw new ExceptionInInitializerError(e);
    }
}

解析和提取 HTML

如果您想要的只是从 HTML 中解析和提取数据,那么最好使用 HTML 解析器,例如 Jsoup

First a disclaimer beforehand: the posted code snippets are all basic examples. You'll need to handle trivial IOExceptions and RuntimeExceptions like NullPointerException, ArrayIndexOutOfBoundsException and consorts yourself.

In case you're developing for Android instead of Java, note also that since introduction of API level 28, cleartext HTTP requests are disabled by default. You are encouraged to use HttpsURLConnection. When really necessary, cleartext can be enabled in the Application Manifest.


Java 11

In case you're already on Java 11 or newer, then it's good to know that there's next to java.net.URLConnection another API to deal with HTTP requests in a less verbose manner: java.net.http.HttpClient.

Preparing

We first need to know at least the URL and the charset. The parameters are optional and depend on the functional requirements.

String url = "http://example.com";
String charset = "UTF-8";  // Or in Java 7 and later, use the constant: java.nio.charset.StandardCharsets.UTF_8.name()
String param1 = "value1";
String param2 = "value2";
// ...

String query = String.format("param1=%s¶m2=%s",
    URLEncoder.encode(param1, charset),
    URLEncoder.encode(param2, charset));

The query parameters must be in name=value format and be concatenated by &. You would normally also URL-encode the query parameters with the specified charset using URLEncoder#encode().

The String#format() is just for convenience. I prefer it when I would need the String concatenation operator + more than twice.


Firing an HTTP GET request with (optionally) query parameters

It's a trivial task. It's the default request method.

URLConnection connection = new URL(url + "?" + query).openConnection();
connection.setRequestProperty("Accept-Charset", charset);
InputStream response = connection.getInputStream();
// ...

Any query string should be concatenated to the URL using ?. The Accept-Charset header may hint the server what encoding the parameters are in. If you don't send any query string, then you can leave the Accept-Charset header away. If you don't need to set any headers, then you can even use the URL#openStream() shortcut method.

InputStream response = new URL(url).openStream();
// ...

Either way, if the other side is an HttpServlet, then its doGet() method will be called and the parameters will be available by HttpServletRequest#getParameter().

For testing purposes, you can print the response body to standard output as below:

try (Scanner scanner = new Scanner(response)) {
    String responseBody = scanner.useDelimiter("\\A").next();
    System.out.println(responseBody);
}

Firing an HTTP POST request with query parameters

Setting the URLConnection#setDoOutput() to true implicitly sets the request method to POST. The standard HTTP POST as web forms do is of type application/x-www-form-urlencoded wherein the query string is written to the request body.

URLConnection connection = new URL(url).openConnection();
connection.setDoOutput(true); // Triggers POST.
connection.setRequestProperty("Accept-Charset", charset);
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + charset);

try (OutputStream output = connection.getOutputStream()) {
    output.write(query.getBytes(charset));
}

InputStream response = connection.getInputStream();
// ...

Note: whenever you'd like to submit a HTML form programmatically, don't forget to take the name=value pairs of any <input type="hidden"> elements into the query string and of course also the name=value pair of the <input type="submit"> element which you'd like to "press" programmatically (because that's usually been used in the server side to distinguish if a button was pressed and if so, which one).

You can also cast the obtained URLConnection to HttpURLConnection and use its HttpURLConnection#setRequestMethod() instead. But if you're trying to use the connection for output you still need to set URLConnection#setDoOutput() to true.

HttpURLConnection httpConnection = (HttpURLConnection) new URL(url).openConnection();
httpConnection.setRequestMethod("POST");
// ...

Either way, if the other side is an HttpServlet, then its doPost() method will be called and the parameters will be available by HttpServletRequest#getParameter().


Actually firing the HTTP request

You can fire the HTTP request explicitly with URLConnection#connect(), but the request will automatically be fired on demand when you want to get any information about the HTTP response, such as the response body using URLConnection#getInputStream() and so on. The above examples does exactly that, so the connect() call is in fact superfluous.


Timeouts

You can use URLConnection#setConnectTimeout() to set the connect timeout and URLConnection#setReadTimeout() to set the read timeout.

The default is basically "no timeout". So you'd like to set these yourself. For example:

httpConnection.setConnectTimeout(3000); // 3s
httpConnection.setReadTimeout(6000); // 6s

There's however a caveat with the read timeout when using Sun/Oracle based JRE. It will silently retry the reading before throwing the timeout exception, most probably merely to have any successfull reading ready in the cache. See also Android (Java) HttpURLConnection silent retry on 'read' timeout This is okayish for GET, but absolutely wrong for POST. In case you're using a Sun/Oracle based JRE, you'll want to turn off that as follows:

System.setProperty("sun.net.http.retryPost", "false")

In case you're writing for Android, above will not work, you'll need this work around on POST:

httpConnection.setChunkedStreamingMode(0);

This will only slightly impact the performance. In case that's undesireable, then consider switching to a different HTTP client such as OkHttp.


Gathering HTTP response information

  1. HTTP response status:

You need an HttpURLConnection here. Cast it first if necessary.

    int status = httpConnection.getResponseCode();
  1. HTTP response headers:

     for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
         System.out.println(header.getKey() + "=" + header.getValue());
     }
    
  2. HTTP response encoding:

When the Content-Type contains a charset parameter, then the response body is likely text based and we'd like to process the response body with the server-side specified character encoding then.

    String contentType = connection.getHeaderField("Content-Type");
    String charset = null;

    for (String param : contentType.replace(" ", "").split(";")) {
        if (param.startsWith("charset=")) {
            charset = param.split("=", 2)[1];
            break;
        }
    }

    if (charset != null) {
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(response, charset))) {
            for (String line; (line = reader.readLine()) != null;) {
                // ... System.out.println(line)?
            }
        }
    } else {
        // It's likely binary content, use InputStream/OutputStream.
    }

Maintaining the session

The server side session is usually backed by a cookie. Some web forms require that you're logged in and/or are tracked by a session. You can use the CookieHandler API to maintain cookies. You need to prepare a CookieManager with a CookiePolicy of ACCEPT_ALL before sending all HTTP requests.

// First set the default cookie manager.
CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));

// All the following subsequent URLConnections will use the same cookie manager.
URLConnection connection = new URL(url).openConnection();
// ...

connection = new URL(url).openConnection();
// ...

connection = new URL(url).openConnection();
// ...

Note that this is known to not always work properly in all circumstances. If it fails for you, then best is to manually gather and set the cookie headers. You basically need to grab all Set-Cookie headers from the response of the login or the first GET request and then pass this through the subsequent requests.

// Gather all cookies on the first request.
URLConnection connection = new URL(url).openConnection();
List<String> cookies = connection.getHeaderFields().get("Set-Cookie");
// ...

// Then use the same cookies on all subsequent requests.
connection = new URL(url).openConnection();
for (String cookie : cookies) {
    connection.addRequestProperty("Cookie", cookie.split(";", 2)[0]);
}
// ...

The split(";", 2)[0] is there to get rid of cookie attributes which are irrelevant for the server side like expires, path, etc. Alternatively, you could also use cookie.substring(0, cookie.indexOf(';')) instead of split().


Streaming mode

The HttpURLConnection will by default buffer the entire request body before actually sending it, regardless of whether you've set a fixed content length yourself using connection.setRequestProperty("Content-Length", contentLength);. This may cause OutOfMemoryExceptions whenever you concurrently send large POST requests (e.g. uploading files). To avoid this, you would like to set the HttpURLConnection#setFixedLengthStreamingMode().

httpConnection.setFixedLengthStreamingMode(contentLength);

But if the content length is really not known beforehand, then you can make use of chunked streaming mode by setting the HttpURLConnection#setChunkedStreamingMode() accordingly. This will set the HTTP Transfer-Encoding header to chunked which will force the request body being sent in chunks. The below example will send the body in chunks of 1 KB.

httpConnection.setChunkedStreamingMode(1024);

User-Agent

It can happen that a request returns an unexpected response, while it works fine with a real web browser. The server side is probably blocking requests based on the User-Agent request header. The URLConnection will by default set it to Java/1.6.0_19 where the last part is obviously the JRE version. You can override this as follows:

connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"); // Do as if you're using Chrome 41 on Windows 7.

Use the User-Agent string from a recent browser.


Error handling

If the HTTP response code is 4nn (Client Error) or 5nn (Server Error), then you may want to read the HttpURLConnection#getErrorStream() to see if the server has sent any useful error information.

InputStream error = ((HttpURLConnection) connection).getErrorStream();

If the HTTP response code is -1, then something went wrong with connection and response handling. The HttpURLConnection implementation is in older JREs somewhat buggy with keeping connections alive. You may want to turn it off by setting the http.keepAlive system property to false. You can do this programmatically in the beginning of your application by:

System.setProperty("http.keepAlive", "false");

Uploading files

You'd normally use multipart/form-data encoding for mixed POST content (binary and character data). The encoding is in more detail described in RFC2388.

String param = "value";
File textFile = new File("/path/to/file.txt");
File binaryFile = new File("/path/to/file.bin");
String boundary = Long.toHexString(System.currentTimeMillis()); // Just generate some unique random value.
String CRLF = "\r\n"; // Line separator required by multipart/form-data.
URLConnection connection = new URL(url).openConnection();
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);

try (
    OutputStream output = connection.getOutputStream();
    PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, charset), true);
) {
    // Send normal param.
    writer.append("--" + boundary).append(CRLF);
    writer.append("Content-Disposition: form-data; name=\"param\"").append(CRLF);
    writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF);
    writer.append(CRLF).append(param).append(CRLF).flush();

    // Send text file.
    writer.append("--" + boundary).append(CRLF);
    writer.append("Content-Disposition: form-data; name=\"textFile\"; filename=\"" + textFile.getName() + "\"").append(CRLF);
    writer.append("Content-Type: text/plain; charset=" + charset).append(CRLF); // Text file itself must be saved in this charset!
    writer.append(CRLF).flush();
    Files.copy(textFile.toPath(), output);
    output.flush(); // Important before continuing with writer!
    writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary.

    // Send binary file.
    writer.append("--" + boundary).append(CRLF);
    writer.append("Content-Disposition: form-data; name=\"binaryFile\"; filename=\"" + binaryFile.getName() + "\"").append(CRLF);
    writer.append("Content-Type: " + URLConnection.guessContentTypeFromName(binaryFile.getName())).append(CRLF);
    writer.append("Content-Transfer-Encoding: binary").append(CRLF);
    writer.append(CRLF).flush();
    Files.copy(binaryFile.toPath(), output);
    output.flush(); // Important before continuing with writer!
    writer.append(CRLF).flush(); // CRLF is important! It indicates end of boundary.

    // End of multipart/form-data.
    writer.append("--" + boundary + "--").append(CRLF).flush();
}

If the other side is an HttpServlet, then its doPost() method will be called and the parts will be available by HttpServletRequest#getPart() (note, thus not getParameter() and so on!). Also see this answer for examples.


Dealing with untrusted or misconfigured HTTPS sites

In case you're developing for Android instead of Java, be careful: the workaround below may save your day if you don't have correct certificates deployed during development. But you should not use it for production. These days (April 2021) Google will not allow your app be distributed on Play Store if they detect insecure hostname verifier, see https://support.google.com/faqs/answer/7188426.

Sometimes you need to connect an HTTPS URL, perhaps because you're writing a web scraper. In that case, you may likely face a javax.net.ssl.SSLException: Not trusted server certificate on some HTTPS sites who doesn't keep their SSL certificates up to date, or a java.security.cert.CertificateException: No subject alternative DNS name matching [hostname] found or javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name on some misconfigured HTTPS sites.

The following one-time-run static initializer in your web scraper class should make HttpsURLConnection more lenient as to those HTTPS sites and thus not throw those exceptions anymore.

static {
    TrustManager[] trustAllCertificates = new TrustManager[] {
        new X509TrustManager() {
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null; // Not relevant.
            }
            @Override
            public void checkClientTrusted(X509Certificate[] certs, String authType) {
                // Do nothing. Just allow them all.
            }
            @Override
            public void checkServerTrusted(X509Certificate[] certs, String authType) {
                // Do nothing. Just allow them all.
            }
        }
    };

    HostnameVerifier trustAllHostnames = new HostnameVerifier() {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true; // Just allow them all.
        }
    };

    try {
        System.setProperty("jsse.enableSNIExtension", "false");
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCertificates, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HttpsURLConnection.setDefaultHostnameVerifier(trustAllHostnames);
    }
    catch (GeneralSecurityException e) {
        throw new ExceptionInInitializerError(e);
    }
}

Parsing and extracting HTML

If all you want is parsing and extracting data from HTML, then better use a HTML parser like Jsoup.

甩你一脸翔 2024-09-07 13:59:17

使用 HTTP 时,引用 HttpURLConnection 几乎总是比引用基类 URLConnection 更有用(因为当您询问时 URLConnection 是一个抽象类)对于 HTTP URL 上的 URLConnection.openConnection() ,无论如何您都会得到返回的内容)。

然后,您可以不依赖 URLConnection#setDoOutput(true) 隐式将请求方法设置为 POST,而是执行 httpURLConnection.setRequestMethod("POST") code> 有些人可能会觉得更自然(并且还允许您指定其他请求方法,例如 PUTDELETE ...)。

它还提供有用的 HTTP 常量,因此您可以执行以下操作:

int responseCode = httpURLConnection.getResponseCode();

if (responseCode == HttpURLConnection.HTTP_OK) {

When working with HTTP it's almost always more useful to refer to HttpURLConnection rather than the base class URLConnection (since URLConnection is an abstract class when you ask for URLConnection.openConnection() on a HTTP URL that's what you'll get back anyway).

Then you can instead of relying on URLConnection#setDoOutput(true) to implicitly set the request method to POST instead do httpURLConnection.setRequestMethod("POST") which some might find more natural (and which also allows you to specify other request methods such as PUT, DELETE, ...).

It also provides useful HTTP constants so you can do:

int responseCode = httpURLConnection.getResponseCode();

if (responseCode == HttpURLConnection.HTTP_OK) {
§对你不离不弃 2024-09-07 13:59:17

受到 StackOverflow 上的这个问题和其他问题的启发,我创建了一个最小的开源 basic-http-client 体现了此处找到的大部分技术。

google-http-java-client 也是一个很棒的开源软件资源。

Inspired by this and other questions on Stack Overflow, I've created a minimal open source basic-http-client that embodies most of the techniques found here.

google-http-java-client is also a great open source resource.

飘过的浮云 2024-09-07 13:59:17

我建议你看一下 kevinsawicki/http-request 上的代码,它基本上是 之上的包装器HttpUrlConnection 它提供了一个更简单的 API,以防您现在只想发出请求,或者您可以查看源代码(不是太大)来了解如何处理连接。

示例:使用内容类型 application/json 和一些查询参数发出 GET 请求:

// GET http://google.com?q=baseball%20gloves&size=100
String response = HttpRequest.get("http://google.com", true, "q", "baseball gloves", "size", 100)
        .accept("application/json")
        .body();
System.out.println("Response was: " + response);

I suggest you take a look at the code on kevinsawicki/http-request, its basically a wrapper on top of HttpUrlConnection it provides a much simpler API in case you just want to make the requests right now or you can take a look at the sources (it's not too big) to take a look at how connections are handled.

Example: Make a GET request with content type application/json and some query parameters:

// GET http://google.com?q=baseball%20gloves&size=100
String response = HttpRequest.get("http://google.com", true, "q", "baseball gloves", "size", 100)
        .accept("application/json")
        .body();
System.out.println("Response was: " + response);
離殇 2024-09-07 13:59:17

更新

新的 HTTP 客户端随 Java 9 一起提供,但作为
孵化器模块名为jdk.incubator.httpclient。孵化器模块有
一种将非最终 API 交给开发人员的方法,同时
API 未来将朝着最终确定或删除的方向发展
发布。

在 Java 9 中,您可以发送 GET 请求,如下所示:

// GET
HttpResponse response = HttpRequest
    .create(new URI("http://www.stackoverflow.com"))
    .headers("Foo", "foovalue", "Bar", "barvalue")
    .GET()
    .response();

然后您可以检查返回的 HttpResponse

int statusCode = response.statusCode();
String responseBody = response.body(HttpResponse.asString());

由于这个新的 HTTP 客户端位于 java.httpclient 中 jdk.incubator.httpclient 模块,您应该在 module-info.java 文件中声明此依赖项:

module com.foo.bar {
    requires jdk.incubator.httpclient;
}

Update

The new HTTP Client shipped with Java 9 but as part of an
Incubator module named jdk.incubator.httpclient. Incubator modules are
a means of putting non-final APIs in the hands of developers while the
APIs progress towards either finalization or removal in a future
release.

In Java 9, you can send a GET request like:

// GET
HttpResponse response = HttpRequest
    .create(new URI("http://www.stackoverflow.com"))
    .headers("Foo", "foovalue", "Bar", "barvalue")
    .GET()
    .response();

Then you can examine the returned HttpResponse:

int statusCode = response.statusCode();
String responseBody = response.body(HttpResponse.asString());

Since this new HTTP Client is in java.httpclient jdk.incubator.httpclient module, you should declare this dependency in your module-info.java file:

module com.foo.bar {
    requires jdk.incubator.httpclient;
}
玻璃人 2024-09-07 13:59:17

HTTP URL Hits 有两个选项: GET / POST

GET 请求:

HttpURLConnection.setFollowRedirects(true); // Defaults to true

String url = "https://name_of_the_url";
URL request_url = new URL(url);
HttpURLConnection http_conn = (HttpURLConnection)request_url.openConnection();
http_conn.setConnectTimeout(100000);
http_conn.setReadTimeout(100000);
http_conn.setInstanceFollowRedirects(true);
System.out.println(String.valueOf(http_conn.getResponseCode()));

POST 请求:

HttpURLConnection.setFollowRedirects(true); // Defaults to true

String url = "https://name_of_the_url"
URL request_url = new URL(url);
HttpURLConnection http_conn = (HttpURLConnection)request_url.openConnection();
http_conn.setConnectTimeout(100000);
http_conn.setReadTimeout(100000);
http_conn.setInstanceFollowRedirects(true);
http_conn.setDoOutput(true);
PrintWriter out = new PrintWriter(http_conn.getOutputStream());
if (urlparameter != null) {
   out.println(urlparameter);
}
out.close();
out = null;
System.out.println(String.valueOf(http_conn.getResponseCode()));

There are two options you can go with HTTP URL Hits : GET / POST

GET Request:

HttpURLConnection.setFollowRedirects(true); // Defaults to true

String url = "https://name_of_the_url";
URL request_url = new URL(url);
HttpURLConnection http_conn = (HttpURLConnection)request_url.openConnection();
http_conn.setConnectTimeout(100000);
http_conn.setReadTimeout(100000);
http_conn.setInstanceFollowRedirects(true);
System.out.println(String.valueOf(http_conn.getResponseCode()));

POST request:

HttpURLConnection.setFollowRedirects(true); // Defaults to true

String url = "https://name_of_the_url"
URL request_url = new URL(url);
HttpURLConnection http_conn = (HttpURLConnection)request_url.openConnection();
http_conn.setConnectTimeout(100000);
http_conn.setReadTimeout(100000);
http_conn.setInstanceFollowRedirects(true);
http_conn.setDoOutput(true);
PrintWriter out = new PrintWriter(http_conn.getOutputStream());
if (urlparameter != null) {
   out.println(urlparameter);
}
out.close();
out = null;
System.out.println(String.valueOf(http_conn.getResponseCode()));
舞袖。长 2024-09-07 13:59:17

这个回应也让我深受启发。

我经常在需要执行一些 HTTP 的项目中,并且我可能不想引入很多第三方依赖项(这会引入其他依赖项等等)

我开始编写自己的实用程序基于一些对话(不是任何地方完成的):

package org.boon.utils;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.Map;

import static org.boon.utils.IO.read;

public class HTTP {

然后只有一堆或静态方法。

public static String get(
        final String url) {

    Exceptions.tryIt(() -> {
        URLConnection connection;
        connection = doGet(url, null, null, null);
        return extractResponseString(connection);
    });
    return null;
}

public static String getWithHeaders(
        final String url,
        final Map<String, ? extends Object> headers) {
    URLConnection connection;
    try {
        connection = doGet(url, headers, null, null);
        return extractResponseString(connection);
    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }
}

public static String getWithContentType(
        final String url,
        final Map<String, ? extends Object> headers,
        String contentType) {
    URLConnection connection;
    try {
        connection = doGet(url, headers, contentType, null);
        return extractResponseString(connection);
    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }
}
public static String getWithCharSet(
        final String url,
        final Map<String, ? extends Object> headers,
        String contentType,
        String charSet) {
    URLConnection connection;
    try {
        connection = doGet(url, headers, contentType, charSet);
        return extractResponseString(connection);
    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }
}

然后发布...

public static String postBody(
        final String url,
        final String body) {
    URLConnection connection;
    try {
        connection = doPost(url, null, "text/plain", null, body);
        return extractResponseString(connection);
    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }
}

public static String postBodyWithHeaders(
        final String url,
        final Map<String, ? extends Object> headers,
        final String body) {
    URLConnection connection;
    try {
        connection = doPost(url, headers, "text/plain", null, body);
        return extractResponseString(connection);
    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }
}


public static String postBodyWithContentType(
        final String url,
        final Map<String, ? extends Object> headers,
        final String contentType,
        final String body) {

    URLConnection connection;
    try {
        connection = doPost(url, headers, contentType, null, body);

        return extractResponseString(connection);

    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }

}


public static String postBodyWithCharset(
        final String url,
        final Map<String, ? extends Object> headers,
        final String contentType,
        final String charSet,
        final String body) {

    URLConnection connection;
    try {
        connection = doPost(url, headers, contentType, charSet, body);

        return extractResponseString(connection);

    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }
}

private static URLConnection doPost(String url, Map<String, ? extends Object> headers,
                                    String contentType, String charset, String body
                                    ) throws IOException {
    URLConnection connection;/* Handle output. */
    connection = new URL(url).openConnection();
    connection.setDoOutput(true);
    manageContentTypeHeaders(contentType, charset, connection);

    manageHeaders(headers, connection);

    IO.write(connection.getOutputStream(), body, IO.CHARSET);
    return connection;
}

private static void manageHeaders(Map<String, ? extends Object> headers, URLConnection connection) {
    if (headers != null) {
        for (Map.Entry<String, ? extends Object> entry : headers.entrySet()) {
            connection.setRequestProperty(entry.getKey(), entry.getValue().toString());
        }
    }
}

private static void manageContentTypeHeaders(String contentType, String charset, URLConnection connection) {
    connection.setRequestProperty("Accept-Charset", charset == null ? IO.CHARSET : charset);
    if (contentType!=null && !contentType.isEmpty()) {
        connection.setRequestProperty("Content-Type", contentType);
    }
}

private static URLConnection doGet(String url, Map<String, ? extends Object> headers,
                                    String contentType, String charset) throws IOException {
    URLConnection connection;/* Handle output. */
    connection = new URL(url).openConnection();
    manageContentTypeHeaders(contentType, charset, connection);

    manageHeaders(headers, connection);

    return connection;
}

private static String extractResponseString(URLConnection connection) throws IOException {
/* Handle input. */
    HttpURLConnection http = (HttpURLConnection)connection;
    int status = http.getResponseCode();
    String charset = getCharset(connection.getHeaderField("Content-Type"));

    if (status==200) {
        return readResponseBody(http, charset);
    } else {
        return readErrorResponseBody(http, status, charset);
    }
}

private static String readErrorResponseBody(HttpURLConnection http, int status, String charset) {
    InputStream errorStream = http.getErrorStream();
    if ( errorStream!=null ) {
        String error = charset== null ? read( errorStream ) :
            read( errorStream, charset );
        throw new RuntimeException("STATUS CODE =" + status + "\n\n" + error);
    } else {
        throw new RuntimeException("STATUS CODE =" + status);
    }
}

private static String readResponseBody(HttpURLConnection http, String charset) throws IOException {
    if (charset != null) {
        return read(http.getInputStream(), charset);
    } else {
        return read(http.getInputStream());
    }
}

private static String getCharset(String contentType) {
    if (contentType==null)  {
        return null;
    }
    String charset = null;
    for (String param : contentType.replace(" ", "").split(";")) {
        if (param.startsWith("charset=")) {
            charset = param.split("=", 2)[1];
            break;
        }
    }
    charset = charset == null ? IO.CHARSET : charset;

    return charset;
}

好吧,你明白了...

以下是测试:

static class MyHandler implements HttpHandler {
    public void handle(HttpExchange t) throws IOException {

        InputStream requestBody = t.getRequestBody();
        String body = IO.read(requestBody);
        Headers requestHeaders = t.getRequestHeaders();
        body = body + "\n" + copy(requestHeaders).toString();
        t.sendResponseHeaders(200, body.length());
        OutputStream os = t.getResponseBody();
        os.write(body.getBytes());
        os.close();
    }
}


@Test
public void testHappy() throws Exception {

    HttpServer server = HttpServer.create(new InetSocketAddress(9212), 0);
    server.createContext("/test", new MyHandler());
    server.setExecutor(null); // creates a default executor
    server.start();

    Thread.sleep(10);

    Map<String,String> headers = map("foo", "bar", "fun", "sun");

    String response = HTTP.postBodyWithContentType("http://localhost:9212/test", headers, "text/plain", "hi mom");

    System.out.println(response);

    assertTrue(response.contains("hi mom"));
    assertTrue(response.contains("Fun=[sun], Foo=[bar]"));

    response = HTTP.postBodyWithCharset("http://localhost:9212/test", headers, "text/plain", "UTF-8", "hi mom");

    System.out.println(response);

    assertTrue(response.contains("hi mom"));
    assertTrue(response.contains("Fun=[sun], Foo=[bar]"));

    response = HTTP.postBodyWithHeaders("http://localhost:9212/test", headers, "hi mom");

    System.out.println(response);

    assertTrue(response.contains("hi mom"));
    assertTrue(response.contains("Fun=[sun], Foo=[bar]"));

    response = HTTP.get("http://localhost:9212/test");

    System.out.println(response);

    response = HTTP.getWithHeaders("http://localhost:9212/test", headers);

    System.out.println(response);

    assertTrue(response.contains("Fun=[sun], Foo=[bar]"));

    response = HTTP.getWithContentType("http://localhost:9212/test", headers, "text/plain");

    System.out.println(response);

    assertTrue(response.contains("Fun=[sun], Foo=[bar]"));

    response = HTTP.getWithCharSet("http://localhost:9212/test", headers, "text/plain", "UTF-8");

    System.out.println(response);

    assertTrue(response.contains("Fun=[sun], Foo=[bar]"));

    Thread.sleep(10);

    server.stop(0);
}

@Test
public void testPostBody() throws Exception {

    HttpServer server = HttpServer.create(new InetSocketAddress(9220), 0);
    server.createContext("/test", new MyHandler());
    server.setExecutor(null); // creates a default executor
    server.start();

    Thread.sleep(10);

    Map<String,String> headers = map("foo", "bar", "fun", "sun");

    String response = HTTP.postBody("http://localhost:9220/test", "hi mom");

    assertTrue(response.contains("hi mom"));

    Thread.sleep(10);

    server.stop(0);
}

@Test(expected = RuntimeException.class)
public void testSad() throws Exception {

    HttpServer server = HttpServer.create(new InetSocketAddress(9213), 0);
    server.createContext("/test", new MyHandler());
    server.setExecutor(null); // creates a default executor
    server.start();

    Thread.sleep(10);

    Map<String,String> headers = map("foo", "bar", "fun", "sun");

    String response = HTTP.postBodyWithContentType("http://localhost:9213/foo", headers, "text/plain", "hi mom");

    System.out.println(response);

    assertTrue(response.contains("hi mom"));
    assertTrue(response.contains("Fun=[sun], Foo=[bar]"));

    Thread.sleep(10);

    server.stop(0);
}

您可以在这里找到其余部分:

https://github.com/RichardHightower/boon

我的目标是以一种更简单的方式提供人们想要做的常见事情......

I was also very inspired by this response.

I am often on projects where I need to do some HTTP, and I may not want to bring in a lot of third-party dependencies (which bring in others and so on and so on, etc.)

I started to write my own utilities based on some of this conversation (not any where done):

package org.boon.utils;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.Map;

import static org.boon.utils.IO.read;

public class HTTP {

Then there are just a bunch or static methods.

public static String get(
        final String url) {

    Exceptions.tryIt(() -> {
        URLConnection connection;
        connection = doGet(url, null, null, null);
        return extractResponseString(connection);
    });
    return null;
}

public static String getWithHeaders(
        final String url,
        final Map<String, ? extends Object> headers) {
    URLConnection connection;
    try {
        connection = doGet(url, headers, null, null);
        return extractResponseString(connection);
    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }
}

public static String getWithContentType(
        final String url,
        final Map<String, ? extends Object> headers,
        String contentType) {
    URLConnection connection;
    try {
        connection = doGet(url, headers, contentType, null);
        return extractResponseString(connection);
    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }
}
public static String getWithCharSet(
        final String url,
        final Map<String, ? extends Object> headers,
        String contentType,
        String charSet) {
    URLConnection connection;
    try {
        connection = doGet(url, headers, contentType, charSet);
        return extractResponseString(connection);
    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }
}

Then post...

public static String postBody(
        final String url,
        final String body) {
    URLConnection connection;
    try {
        connection = doPost(url, null, "text/plain", null, body);
        return extractResponseString(connection);
    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }
}

public static String postBodyWithHeaders(
        final String url,
        final Map<String, ? extends Object> headers,
        final String body) {
    URLConnection connection;
    try {
        connection = doPost(url, headers, "text/plain", null, body);
        return extractResponseString(connection);
    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }
}


public static String postBodyWithContentType(
        final String url,
        final Map<String, ? extends Object> headers,
        final String contentType,
        final String body) {

    URLConnection connection;
    try {
        connection = doPost(url, headers, contentType, null, body);

        return extractResponseString(connection);

    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }

}


public static String postBodyWithCharset(
        final String url,
        final Map<String, ? extends Object> headers,
        final String contentType,
        final String charSet,
        final String body) {

    URLConnection connection;
    try {
        connection = doPost(url, headers, contentType, charSet, body);

        return extractResponseString(connection);

    } catch (Exception ex) {
        Exceptions.handle(ex);
        return null;
    }
}

private static URLConnection doPost(String url, Map<String, ? extends Object> headers,
                                    String contentType, String charset, String body
                                    ) throws IOException {
    URLConnection connection;/* Handle output. */
    connection = new URL(url).openConnection();
    connection.setDoOutput(true);
    manageContentTypeHeaders(contentType, charset, connection);

    manageHeaders(headers, connection);

    IO.write(connection.getOutputStream(), body, IO.CHARSET);
    return connection;
}

private static void manageHeaders(Map<String, ? extends Object> headers, URLConnection connection) {
    if (headers != null) {
        for (Map.Entry<String, ? extends Object> entry : headers.entrySet()) {
            connection.setRequestProperty(entry.getKey(), entry.getValue().toString());
        }
    }
}

private static void manageContentTypeHeaders(String contentType, String charset, URLConnection connection) {
    connection.setRequestProperty("Accept-Charset", charset == null ? IO.CHARSET : charset);
    if (contentType!=null && !contentType.isEmpty()) {
        connection.setRequestProperty("Content-Type", contentType);
    }
}

private static URLConnection doGet(String url, Map<String, ? extends Object> headers,
                                    String contentType, String charset) throws IOException {
    URLConnection connection;/* Handle output. */
    connection = new URL(url).openConnection();
    manageContentTypeHeaders(contentType, charset, connection);

    manageHeaders(headers, connection);

    return connection;
}

private static String extractResponseString(URLConnection connection) throws IOException {
/* Handle input. */
    HttpURLConnection http = (HttpURLConnection)connection;
    int status = http.getResponseCode();
    String charset = getCharset(connection.getHeaderField("Content-Type"));

    if (status==200) {
        return readResponseBody(http, charset);
    } else {
        return readErrorResponseBody(http, status, charset);
    }
}

private static String readErrorResponseBody(HttpURLConnection http, int status, String charset) {
    InputStream errorStream = http.getErrorStream();
    if ( errorStream!=null ) {
        String error = charset== null ? read( errorStream ) :
            read( errorStream, charset );
        throw new RuntimeException("STATUS CODE =" + status + "\n\n" + error);
    } else {
        throw new RuntimeException("STATUS CODE =" + status);
    }
}

private static String readResponseBody(HttpURLConnection http, String charset) throws IOException {
    if (charset != null) {
        return read(http.getInputStream(), charset);
    } else {
        return read(http.getInputStream());
    }
}

private static String getCharset(String contentType) {
    if (contentType==null)  {
        return null;
    }
    String charset = null;
    for (String param : contentType.replace(" ", "").split(";")) {
        if (param.startsWith("charset=")) {
            charset = param.split("=", 2)[1];
            break;
        }
    }
    charset = charset == null ? IO.CHARSET : charset;

    return charset;
}

Well, you get the idea....

Here are the tests:

static class MyHandler implements HttpHandler {
    public void handle(HttpExchange t) throws IOException {

        InputStream requestBody = t.getRequestBody();
        String body = IO.read(requestBody);
        Headers requestHeaders = t.getRequestHeaders();
        body = body + "\n" + copy(requestHeaders).toString();
        t.sendResponseHeaders(200, body.length());
        OutputStream os = t.getResponseBody();
        os.write(body.getBytes());
        os.close();
    }
}


@Test
public void testHappy() throws Exception {

    HttpServer server = HttpServer.create(new InetSocketAddress(9212), 0);
    server.createContext("/test", new MyHandler());
    server.setExecutor(null); // creates a default executor
    server.start();

    Thread.sleep(10);

    Map<String,String> headers = map("foo", "bar", "fun", "sun");

    String response = HTTP.postBodyWithContentType("http://localhost:9212/test", headers, "text/plain", "hi mom");

    System.out.println(response);

    assertTrue(response.contains("hi mom"));
    assertTrue(response.contains("Fun=[sun], Foo=[bar]"));

    response = HTTP.postBodyWithCharset("http://localhost:9212/test", headers, "text/plain", "UTF-8", "hi mom");

    System.out.println(response);

    assertTrue(response.contains("hi mom"));
    assertTrue(response.contains("Fun=[sun], Foo=[bar]"));

    response = HTTP.postBodyWithHeaders("http://localhost:9212/test", headers, "hi mom");

    System.out.println(response);

    assertTrue(response.contains("hi mom"));
    assertTrue(response.contains("Fun=[sun], Foo=[bar]"));

    response = HTTP.get("http://localhost:9212/test");

    System.out.println(response);

    response = HTTP.getWithHeaders("http://localhost:9212/test", headers);

    System.out.println(response);

    assertTrue(response.contains("Fun=[sun], Foo=[bar]"));

    response = HTTP.getWithContentType("http://localhost:9212/test", headers, "text/plain");

    System.out.println(response);

    assertTrue(response.contains("Fun=[sun], Foo=[bar]"));

    response = HTTP.getWithCharSet("http://localhost:9212/test", headers, "text/plain", "UTF-8");

    System.out.println(response);

    assertTrue(response.contains("Fun=[sun], Foo=[bar]"));

    Thread.sleep(10);

    server.stop(0);
}

@Test
public void testPostBody() throws Exception {

    HttpServer server = HttpServer.create(new InetSocketAddress(9220), 0);
    server.createContext("/test", new MyHandler());
    server.setExecutor(null); // creates a default executor
    server.start();

    Thread.sleep(10);

    Map<String,String> headers = map("foo", "bar", "fun", "sun");

    String response = HTTP.postBody("http://localhost:9220/test", "hi mom");

    assertTrue(response.contains("hi mom"));

    Thread.sleep(10);

    server.stop(0);
}

@Test(expected = RuntimeException.class)
public void testSad() throws Exception {

    HttpServer server = HttpServer.create(new InetSocketAddress(9213), 0);
    server.createContext("/test", new MyHandler());
    server.setExecutor(null); // creates a default executor
    server.start();

    Thread.sleep(10);

    Map<String,String> headers = map("foo", "bar", "fun", "sun");

    String response = HTTP.postBodyWithContentType("http://localhost:9213/foo", headers, "text/plain", "hi mom");

    System.out.println(response);

    assertTrue(response.contains("hi mom"));
    assertTrue(response.contains("Fun=[sun], Foo=[bar]"));

    Thread.sleep(10);

    server.stop(0);
}

You can find the rest here:

https://github.com/RichardHightower/boon

My goal is to provide the common things one would want to do in a bit more easier way then....

一片旧的回忆 2024-09-07 13:59:17

最初我被这篇文章误导了,它有利于HttpClient

后来我意识到 HttpURLConnection 将从 保留下来这篇文章

根据 Google 博客

Apache HTTP 客户端在 Eclair 和 Froyo 上的错误较少。它是这些版本的最佳选择。对于 Gingerbread , HttpURLConnection 是最佳选择。其简单的 API 和小尺寸使其非常适合 Android。

透明压缩和响应缓存可减少网络使用、提高速度并节省电池。新应用程序应该使用HttpURLConnection;这是我们未来将花费精力的地方。

阅读这篇文章< /a> 和其他一些堆栈溢出问题,我确信 HttpURLConnection 将保留更长时间。

一些支持 HttpURLConnections 的 SE 问题:

在 Android 上,使用 URL 编码表单数据发出 POST 请求,而不使用 UrlEncodedFormEntity

HttpPost 适用于 Java 项目,但不适用于 Android

Initially I was misled by this article which favours HttpClient.

Later I have realized that HttpURLConnection is going to stay from this article.

As per the Google blog:

Apache HTTP client has fewer bugs on Eclair and Froyo. It is the best choice for these releases. For Gingerbread , HttpURLConnection is the best choice. Its simple API and small size makes it great fit for Android.

Transparent compression and response caching reduce network use, improve speed and save battery. New applications should use HttpURLConnection; it is where we will be spending our energy going forward.

After reading this article and some other stack over flow questions, I am convinced that HttpURLConnection is going to stay for longer durations.

Some of the SE questions favouring HttpURLConnections:

On Android, make a POST request with URL Encoded Form data without using UrlEncodedFormEntity

HttpPost works in Java project, but not on Android

会发光的星星闪亮亮i 2024-09-07 13:59:17

还有 OkHttp,它是一个默认情况下高效的 HTTP 客户端:

  • HTTP/2 支持允许对同一主机的所有请求共享套接字。
  • 连接池可减少请求延迟(如果 HTTP/2 不可用)。
  • 透明 GZIP 缩小了下载大小。
  • 响应缓存完全避免了网络重复请求。

首先创建一个 OkHttpClient 实例:

OkHttpClient client = new OkHttpClient();

然后,准备您的 GET 请求:

Request request = new Request.Builder()
      .url(url)
      .build();

最后,使用 OkHttpClient 发送准备好的 Request

Response response = client.newCall(request).execute();

更多细节可以参考OkHttp的文档

There is also OkHttp, which is an HTTP client that’s efficient by default:

  • HTTP/2 support allows all requests to the same host to share a socket.
  • Connection pooling reduces request latency (if HTTP/2 isn’t available).
  • Transparent GZIP shrinks download sizes.
  • Response caching avoids the network completely for repeat requests.

First create an instance of OkHttpClient:

OkHttpClient client = new OkHttpClient();

Then, prepare your GET request:

Request request = new Request.Builder()
      .url(url)
      .build();

finally, use OkHttpClient to send prepared Request:

Response response = client.newCall(request).execute();

For more details, you can consult the OkHttp's documentation

错爱 2024-09-07 13:59:17

如果您使用 HTTP GET,请删除此行:

urlConnection.setDoOutput(true);

If you are using HTTP GET, please remove this line:

urlConnection.setDoOutput(true);
乖乖 2024-09-07 13:59:17

您还可以使用 JdkRequest 来自 < a href="http://http.jcabi.com">jcabi-http(我是一名开发人员),它为您完成所有这些工作,例如装饰 HttpURLConnection、触发 HTTP 请求和解析响应:

String html = new JdkRequest("http://www.google.com").fetch().body();

查看此博客文章了解更多信息:http://www.yegor256.com/2014 /04/11/jcabi-http-intro.html

You can also use JdkRequest from jcabi-http (I'm a developer), which does all this work for you, decorating HttpURLConnection, firing HTTP requests and parsing responses, for example:

String html = new JdkRequest("http://www.google.com").fetch().body();

Check this blog post for more info: http://www.yegor256.com/2014/04/11/jcabi-http-intro.html

べ繥欢鉨o。 2024-09-07 13:59:17

如果您使用的是 Java 11 或更高版本(Android 除外),而不是旧版 < code>HttpUrlConnection 类,您可以使用 Java 11 新的 HTTP 客户端 API

示例 GET 请求:

var uri = URI.create("https://httpbin.org/get?age=26&isHappy=true");
var client = HttpClient.newHttpClient();
var request = HttpRequest
        .newBuilder()
        .uri(uri)
        .header("accept", "application/json")
        .GET()
        .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());

异步执行的相同请求:

var responseAsync = client
        .sendAsync(request, HttpResponse.BodyHandlers.ofString())
        .thenApply(HttpResponse::body)
        .thenAccept(System.out::println);
// responseAsync.join(); // Wait for completion

示例 POST 请求:

var request = HttpRequest
        .newBuilder()
        .uri(uri)
        .version(HttpClient.Version.HTTP_2)
        .timeout(Duration.ofMinutes(1))
        .header("Content-Type", "application/json")
        .header("Authorization", "Bearer fake")
        .POST(BodyPublishers.ofString("{ title: 'This is cool' }"))
        .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());

用于以多部分形式发送表单数据 (multipart/form-data)或 url 编码 (application/x-www-form-urlencoded) 格式,请参阅此解决方案

有关 HTTP 客户端 API 的示例和更多信息,请参阅本文

旁注

要使用 Java 标准库创建简单的 HTTP 服务器,请参阅这篇文章

If you are using Java 11 or newer (except on Android), instead of the legacy HttpUrlConnection class, you can use Java 11 new HTTP Client API.

An example GET request:

var uri = URI.create("https://httpbin.org/get?age=26&isHappy=true");
var client = HttpClient.newHttpClient();
var request = HttpRequest
        .newBuilder()
        .uri(uri)
        .header("accept", "application/json")
        .GET()
        .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());

The same request executed asynchronously:

var responseAsync = client
        .sendAsync(request, HttpResponse.BodyHandlers.ofString())
        .thenApply(HttpResponse::body)
        .thenAccept(System.out::println);
// responseAsync.join(); // Wait for completion

An example POST request:

var request = HttpRequest
        .newBuilder()
        .uri(uri)
        .version(HttpClient.Version.HTTP_2)
        .timeout(Duration.ofMinutes(1))
        .header("Content-Type", "application/json")
        .header("Authorization", "Bearer fake")
        .POST(BodyPublishers.ofString("{ title: 'This is cool' }"))
        .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());

For sending form data as multipart (multipart/form-data) or url-encoded (application/x-www-form-urlencoded) format, see this solution.

See this article for examples and more information about HTTP Client API.

Sidenote

To create a simple HTTP server using Java standard library, see this post.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文