[英]How to use java.net.URLConnection to fire and handle HTTP requests
Use of java.net.URLConnection
is asked about pretty often here, and the Oracle tutorial is too concise about it. java.net.URLConnection
使用在这里经常被问到,而Oracle 教程对此过于简洁。
That tutorial basically only shows how to fire a GET request and read the response.该教程基本上只展示了如何触发GET请求并读取响应。 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.
它没有在任何地方解释如何使用它来执行POST请求、设置请求标头、读取响应标头、处理 cookie、提交 HTML 表单、上传文件等。
So, how can I use java.net.URLConnection
to fire and handle "advanced" HTTP requests?那么,如何使用
java.net.URLConnection
来触发和处理“高级”HTTP 请求?
First a disclaimer beforehand: the posted code snippets are all basic examples.首先事先声明:发布的代码片段都是基本示例。 You'll need to handle trivial
IOException
s and RuntimeException
s like NullPointerException
, ArrayIndexOutOfBoundsException
and consorts yourself.您需要处理琐碎的
IOException
和RuntimeException
如NullPointerException
、 ArrayIndexOutOfBoundsException
和自己。
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 .如果您正在为 Android 而不是 Java 进行开发,还要注意,自从引入 API 级别 28 以来,默认情况下禁用明文 HTTP 请求。 You are encouraged to use
HttpsURLConnection
, but if it is really necessary, cleartext can be enabled in the Application Manifest.鼓励您使用
HttpsURLConnection
,但如果确实有必要,可以在应用程序清单中启用明文。
We first need to know at least the URL and the charset.我们首先至少需要知道 URL 和字符集。 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 &
.查询参数必须采用
name=value
格式并由&
连接。 You would normally also URL-encode the query parameters with the specified charset using URLEncoder#encode()
.您通常还会使用
URLEncoder#encode()
使用指定的字符集对查询参数进行 URL 编码。
The String#format()
is just for convenience. String#format()
只是为了方便。 I prefer it when I would need the String concatenation operator +
more than twice.当我需要字符串连接运算符
+
两次以上时,我更喜欢它。
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. Accept-Charset
标头可能会提示服务器参数的编码方式。如果您不发送任何查询字符串,那么您可以离开Accept-Charset
标头。 If you don't need to set any headers, then you can even use the URL#openStream()
shortcut method.如果您不需要设置任何标题,那么您甚至可以使用
URL#openStream()
快捷方法。
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()
.无论哪种方式,如果另一端是
HttpServlet
,那么它的doGet()
方法将被调用并且参数将通过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);
}
Setting the URLConnection#setDoOutput()
to true
implicitly sets the request method to POST.将
URLConnection#setDoOutput()
为true
隐式地将请求方法设置为 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. 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();
// ...
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).注意:当你想以编程方式提交HTML表单,不要忘记采取
name=value
任何对<input type="hidden">
元素融入到查询字符串,当然还有的name=value
对您希望以编程方式“按下”的<input type="submit">
元素(因为通常在服务器端使用它来区分按钮是否被按下,如果按下,是哪个)。
You can also cast the obtained URLConnection
to HttpURLConnection
and use its HttpURLConnection#setRequestMethod()
instead.您还可以将获得的
URLConnection
为HttpURLConnection
并使用其HttpURLConnection#setRequestMethod()
代替。 But if you're trying to use the connection for output you still need to set URLConnection#setDoOutput()
to true
.但是,如果您尝试使用连接进行输出,您仍然需要将
URLConnection#setDoOutput()
为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()
.无论哪种方式,如果另一端是
HttpServlet
,那么它的doPost()
方法将被调用并且参数将通过HttpServletRequest#getParameter()
可用。
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.您可以使用
URLConnection#connect()
显式触发 HTTP 请求,但是当您想要获取有关 HTTP 响应的任何信息(例如使用URLConnection#getInputStream()
等的响应正文URLConnection#connect()
,该请求将根据需要自动触发。 The above examples does exactly that, so the connect()
call is in fact superfluous.上面的例子正是这样做的,所以
connect()
调用实际上是多余的。
You need an HttpURLConnection
here.你需要一个
HttpURLConnection
在这里。 Cast it first if necessary.如有必要,请先施放。
int status = httpConnection.getResponseCode();
HTTP response headers : HTTP 响应头:
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) { System.out.println(header.getKey() + "=" + header.getValue()); }
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.当
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.
}
The server side session is usually backed by a cookie.服务器端会话通常由 cookie 支持。 Some web forms require that you're logged in and/or are tracked by a session.
某些 Web 表单要求您登录和/或被会话跟踪。 You can use the
CookieHandler
API to maintain cookies.您可以使用
CookieHandler
API 来维护 cookie。 You need to prepare a CookieManager
with a CookiePolicy
of ACCEPT_ALL
before sending all HTTP requests.在发送所有 HTTP 请求之前,您需要准备一个
CookieManager
的CookiePolicy
为ACCEPT_ALL
。
// 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.
如果它对您来说失败了,那么最好是手动收集和设置 cookie 标头。 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.您基本上需要从登录或第一个
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]);
}
// ...
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()
. split(";", 2)[0]
是为了摆脱与服务器端无关的 cookie 属性,如expires
、 path
等。或者,您也可以使用cookie.substring(0, cookie.indexOf(';'))
而不是split()
。
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);
HttpURLConnection
将默认在实际发送之前缓冲整个请求正文,无论您是否使用connection.setRequestProperty("Content-Length", contentLength);
自己设置了固定的内容长度connection.setRequestProperty("Content-Length", contentLength);
. . This may cause
OutOfMemoryException
s whenever you concurrently send large POST requests (eg uploading files).每当您同时发送大型 POST 请求(例如上传文件)时,这可能会导致
OutOfMemoryException
。 To avoid this, you would like to set the HttpURLConnection#setFixedLengthStreamingMode()
.为避免这种情况,您需要设置
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.但是如果内容长度真的事先不知道,那么您可以通过相应地设置
HttpURLConnection#setChunkedStreamingMode()
来使用分块流模式。 This will set the HTTPTransfer-Encoding
header to chunked
which will force the request body being sent in chunks.这会将 HTTP
Transfer-Encoding
标头设置为chunked
,这将强制以chunked
形式发送请求正文。 The below example will send the body in chunks of 1 KB.下面的示例将以 1 KB 的块发送正文。
httpConnection.setChunkedStreamingMode(1024);
It can happen that a request returns an unexpected response, while it works fine with a real web browser . 一个请求可能会返回一个意外的响应,而它在真实的 Web 浏览器中工作正常。 The server side is probably blocking requests based on the
User-Agent
request header.服务器端可能会根据
User-Agent
请求标头阻止请求。 The URLConnection
will by default set it to Java/1.6.0_19
where the last part is obviously the JRE version.默认情况下,
URLConnection
会将其设置为Java/1.6.0_19
,其中最后一部分显然是 JRE 版本。 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 .使用来自最近浏览器的 User-Agent 字符串。
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.如果 HTTP 响应代码是
4nn
(客户端错误)或5nn
(服务器错误),那么您可能需要阅读HttpURLConnection#getErrorStream()
以查看服务器是否发送了任何有用的错误信息。
InputStream error = ((HttpURLConnection) connection).getErrorStream();
If the HTTP response code is -1, then something went wrong with connection and response handling.如果 HTTP 响应代码为 -1,则连接和响应处理出现问题。 The
HttpURLConnection
implementation is in older JREs somewhat buggy with keeping connections alive. HttpURLConnection
实现在较旧的 JRE 中在保持连接活动方面有些问题。 You may want to turn it off by setting the http.keepAlive
system property to false
.您可能希望通过将
http.keepAlive
系统属性设置为false
来关闭它。 You can do this programmatically in the beginning of your application by:您可以通过以下方式在应用程序开始时以编程方式执行此操作:
System.setProperty("http.keepAlive", "false");
You'd normally use multipart/form-data
encoding for mixed POST content (binary and character data).您通常会对混合的 POST 内容(二进制和字符数据)使用
multipart/form-data
编码。 The encoding is in more detail described in RFC2388 .编码在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!).如果另一端是一个
HttpServlet
,那么它的doPost()
方法将被调用并且这些部分将通过HttpServletRequest#getPart()
可用(注意,因此不是getParameter()
等等!)。 The getPart()
method is however relatively new, it's introduced in Servlet 3.0 (Glassfish 3, Tomcat 7, etc.).然而
getPart()
方法相对较新,它是在 Servlet 3.0(Glassfish 3、Tomcat 7 等)中引入的。 Prior to Servlet 3.0, your best choice is using Apache Commons FileUpload to parse a multipart/form-data
request.在 Servlet 3.0 之前,最好的选择是使用Apache Commons FileUpload来解析
multipart/form-data
请求。 Also see this answer for examples of both the FileUpload and the Servelt 3.0 approaches.有关 FileUpload 和 Servelt 3.0 方法的示例,另请参阅此答案。
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.如果您正在为 Android 而不是 Java 进行开发,请注意:如果您在开发过程中没有部署正确的证书,下面的解决方法可能会节省您的时间。 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.
这些天(2021 年 4 月)如果 Google 检测到不安全的主机名验证程序,他们将不允许您的应用在 Play 商店中分发,请参阅https://support.google.com/faqs/answer/7188426。
Sometimes you need to connect an HTTPS URL, perhaps because you're writing a web scraper.有时您需要连接 HTTPS URL,可能是因为您正在编写网络爬虫。 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.在这种情况下,您可能会遇到
javax.net.ssl.SSLException: Not trusted server certificate
on some HTTPS 站点,他们的 SSL 证书没有保持最新,或者java.security.cert.CertificateException: No subject alternative DNS name matching [hostname] found
或javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name
某些配置错误的 HTTPS 站点上的javax.net.ssl.SSLProtocolException: handshake alert: unrecognized_name
。
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
初始值设定项应该使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);
}
}
The Apache HttpComponents HttpClient is much more convenient in this all :) Apache HttpComponents HttpClient在这方面要方便得多:)
If all you want is parsing and extracting data from HTML, then better use a HTML parser like Jsoup .如果您只想从 HTML 解析和提取数据,那么最好使用像Jsoup这样的 HTML 解析器。
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).在使用 HTTP 时,引用
HttpURLConnection
而不是基类URLConnection
几乎总是更有用(因为当您在 HTTP URL 上请求URLConnection.openConnection()
时, URLConnection
是一个抽象类,无论如何您都会得到它)。
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 , ...).然后,您可以不依赖
URLConnection#setDoOutput(true)
将请求方法隐式设置为POST而是执行httpURLConnection.setRequestMethod("POST")
这有些人可能会觉得更自然(并且还允许您指定其他请求方法,例如放置,删除,...)。
It also provides useful HTTP constants so you can do:它还提供了有用的 HTTP 常量,因此您可以执行以下操作:
int responseCode = httpURLConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
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.受 Stack Overflow 上的这个问题和其他问题的启发,我创建了一个最小的开源basic-http-client ,它包含了这里找到的大部分技术。
google-http-java-client is also a great open source resource. google-http-java-client也是一个很棒的开源资源。
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.我建议您查看kevinsawicki/http-request上的代码,它基本上是
HttpUrlConnection
之上的包装器,它提供了一个更简单的 API,以防您只想立即发出请求,或者您可以查看源代码(不是太大)来看看连接是如何处理的。
Example: Make a GET
request with content type application/json
and some query parameters:示例:使用内容类型
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);
The new HTTP Client shipped with Java 9 but as part of an Incubator module named
jdk.incubator.httpclient
.新的 HTTP 客户端随 Java 9 一起提供,但作为名为
jdk.incubator.httpclient
的 Incubator 模块的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.孵化器模块是一种将非最终 API 交给开发人员的方法,同时这些 API 会在未来版本中逐步确定或删除。
In Java 9, you can send a GET
request like:在 Java 9 中,您可以发送一个
GET
请求,如:
// 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
:然后你可以检查返回的
HttpResponse
:
int statusCode = response.statusCode();
String responseBody = response.body(HttpResponse.asString());
Since this new HTTP Client is in由于这个新的 HTTP 客户端在
java.httpclient
jdk.incubator.httpclient
module, you should declare this dependency in your module-info.java
file: jdk.incubator.httpclient
模块,你应该在你的module-info.java
文件中声明这个依赖:
module com.foo.bar {
requires jdk.incubator.httpclient;
}
There are two options you can go with HTTP URL Hits : GET / POST 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: 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()));
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.)我经常在需要做一些 HTTP 的项目中,我可能不想引入很多第三方依赖项(引入其他依赖项等等)
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 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....我的目标是以更简单的方式提供人们想要做的常见事情......
Initially I was misled by this article which favours HttpClient
.最初我被这篇偏爱
HttpClient
文章误导了。
Later I have realized that HttpURLConnection
is going to stay from this article .后来我意识到
HttpURLConnection
将保留在本文中。
As per the Google blog :根据谷歌博客:
Apache HTTP client has fewer bugs on Eclair and Froyo.
Apache HTTP 客户端在 Eclair 和 Froyo 上的错误较少。 It is the best choice for these releases.
它是这些版本的最佳选择。 For Gingerbread , HttpURLConnection is the best choice.
对于 Gingerbread , HttpURLConnection 是最佳选择。 Its simple API and small size makes it great fit for Android.
其简单的 API 和小尺寸使其非常适合 Android。
Transparent compression and response caching reduce network use, improve speed and save battery.
透明压缩和响应缓存减少网络使用,提高速度并节省电池。 New applications should use HttpURLConnection;
新的应用程序应该使用 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.在阅读了这篇文章和其他一些关于流的堆栈问题后,我确信
HttpURLConnection
会持续更长时间。
Some of the SE questions favouring HttpURLConnections
:一些支持
HttpURLConnections
的 SE 问题:
On Android, make a POST request with URL Encoded Form data without using UrlEncodedFormEntity 在 Android 上,使用 URL Encoded Form 数据发出 POST 请求而不使用 UrlEncodedFormEntity
HttpPost works in Java project, but not on Android HttpPost 适用于 Java 项目,但不适用于 Android
There is also OkHttp , which is an HTTP client that's efficient by default:还有OkHttp ,它是一个默认高效的 HTTP 客户端:
- HTTP/2 support allows all requests to the same host to share a socket.
HTTP/2 支持允许对同一主机的所有请求共享一个套接字。
- Connection pooling reduces request latency (if HTTP/2 isn't available).
连接池减少了请求延迟(如果 HTTP/2 不可用)。
- Transparent GZIP shrinks download sizes.
透明 GZIP 可缩小下载大小。
- Response caching avoids the network completely for repeat requests.
响应缓存完全避免网络重复请求。
First create an instance of OkHttpClient
:首先创建一个
OkHttpClient
实例:
OkHttpClient client = new OkHttpClient();
Then, prepare your GET
request:然后,准备您的
GET
请求:
Request request = new Request.Builder()
.url(url)
.build();
finally, use OkHttpClient
to send prepared Request
:最后,使用
OkHttpClient
发送准备好的Request
:
Response response = client.newCall(request).execute();
For more details, you can consult the OkHttp's documentation更详细的可以参考OkHttp的文档
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:您还可以使用
JdkRequest
-http (我是开发人员)中的JdkRequest ,它为您完成所有这些工作,装饰 HttpURLConnection,触发 HTTP 请求和解析响应,例如:
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查看此博客文章了解更多信息: http : //www.yegor256.com/2014/04/11/jcabi-http-intro.html
如果您使用 HTTP GET ,请删除此行:
urlConnection.setDoOutput(true);
If you are using Java 11 ( except on Android ), instead of the legacy HttpUrlConnection class, you can use Java 11 new HTTP Client API .如果您使用的是Java 11 ( Android 除外),则可以使用 Java 11 新的HTTP Client API ,而不是旧的HttpUrlConnection类。
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
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 .要以 multipart (
multipart/form-data
) 或 url-encoded ( application/x-www-form-urlencoded
) 格式发送表单数据,请参阅此解决方案。
See this article for examples and more information about HTTP Client API.有关 HTTP 客户端 API 的示例和更多信息,请参阅本文。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.