Do a Simple HTTP Request in Java – 在Java中做一个简单的HTTP请求

最后修改: 2017年 4月 29日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this quick tutorial, we present a way of performing HTTP requests in Java — by using the built-in Java class HttpUrlConnection. 

在这个快速教程中,我们将介绍一种在Java中执行HTTP请求的方式–通过使用内置的Java类HttpUrlConnection。

Note that starting with JDK 11, Java provides a new API for performing HTTP requests, which is meant as a replacement for the HttpUrlConnection, the HttpClient API.

请注意,从 JDK 11 开始,Java 为执行 HTTP 请求提供了一个新的 API,其目的是替代 HttpUrlConnection, HttpClient API

2. HttpUrlConnection

2.HttpUrlConnection

The HttpUrlConnection class allows us to perform basic HTTP requests without the use of any additional libraries. All the classes that we need are part of the java.net package.

HttpUrlConnection类允许我们执行基本的HTTP请求,而无需使用任何额外的库。我们需要的所有类都是java.net包的一部分。

The disadvantages of using this method are that the code can be more cumbersome than other HTTP libraries and that it does not provide more advanced functionalities such as dedicated methods for adding headers or authentication.

使用这种方法的缺点是:代码可能比其他HTTP库更繁琐,而且它不提供更高级的功能,如添加头文件或认证的专用方法。

3. Creating a Request

3.创建一个请求

We can create an HttpUrlConnection instance using the openConnection() method of the URL class. Note that this method only creates a connection object but doesn’t establish the connection yet.

我们可以使用URL类的openConnection()方法创建一个HttpUrlConnection实例。注意,这个方法只创建一个连接对象,但还没有建立连接。

The HttpUrlConnection class is used for all types of requests by setting the requestMethod attribute to one of the values: GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE.

HttpUrlConnection类通过将requestMethod属性设置为其中一个值,可用于所有类型的请求。get, post, head, options, put, delete, trace.

Let’s create a connection to a given URL using GET method:

让我们使用GET方法创建一个连接到一个给定的URL。

URL url = new URL("http://example.com");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");

4. Adding Request Parameters

4.添加请求参数

If we want to add parameters to a request, we have to set the doOutput property to true, then write a String of the form param1=value&param2=value to the OutputStream of the HttpUrlConnection instance:

如果我们想在请求中添加参数,我们必须将doOutput属性设置为true,然后将param1=value&param2=value形式的String写到HttpUrlConnection实例的OutputStream

Map<String, String> parameters = new HashMap<>();
parameters.put("param1", "val");

con.setDoOutput(true);
DataOutputStream out = new DataOutputStream(con.getOutputStream());
out.writeBytes(ParameterStringBuilder.getParamsString(parameters));
out.flush();
out.close();

To facilitate the transformation of the parameter Map, we have written a utility class called ParameterStringBuilder containing a static method, getParamsString(), that transforms a Map into a String of the required format:

为了方便转换参数Map,我们编写了一个名为ParameterStringBuilder的实用类,其中包含一个静态方法getParamsString(),可以将Map转换为所需格式的String

public class ParameterStringBuilder {
    public static String getParamsString(Map<String, String> params) 
      throws UnsupportedEncodingException{
        StringBuilder result = new StringBuilder();

        for (Map.Entry<String, String> entry : params.entrySet()) {
          result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
          result.append("=");
          result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
          result.append("&");
        }

        String resultString = result.toString();
        return resultString.length() > 0
          ? resultString.substring(0, resultString.length() - 1)
          : resultString;
    }
}

5. Setting Request Headers

5.设置请求头信息

Adding headers to a request can be achieved by using the setRequestProperty() method:

通过使用setRequestProperty()方法,可以实现向请求添加头信息。

con.setRequestProperty("Content-Type", "application/json");

To read the value of a header from a connection, we can use the getHeaderField() method:

要从一个连接中读取一个头的值,我们可以使用 getHeaderField()方法。

String contentType = con.getHeaderField("Content-Type");

6. Configuring Timeouts

6.配置超时

HttpUrlConnection class allows setting the connect and read timeouts. These values define the interval of time to wait for the connection to the server to be established or data to be available for reading.

HttpUrlConnection类允许设置连接和读取超时。这些值定义了等待与服务器建立连接或数据可被读取的时间间隔。

To set the timeout values, we can use the setConnectTimeout() and setReadTimeout() methods:

要设置超时值,我们可以使用setConnectTimeout()setReadTimeout()方法。

con.setConnectTimeout(5000);
con.setReadTimeout(5000);

In the example, we set both timeout values to five seconds.

在这个例子中,我们把两个超时值都设为5秒。

7. Handling Cookies

7.处理Cookie

The java.net package contains classes that ease working with cookies such as CookieManager and HttpCookie.

java.net包包含便于处理cookie的类,如CookieManagerHttpCookie

First, to read the cookies from a response, we can retrieve the value of the Set-Cookie header and parse it to a list of HttpCookie objects:

首先,为了从响应中读取cookie,我们可以检索Set-Cookie头的值并将其解析为HttpCookie对象的列表。

String cookiesHeader = con.getHeaderField("Set-Cookie");
List<HttpCookie> cookies = HttpCookie.parse(cookiesHeader);

Next, we will add the cookies to the cookie store:

接下来,我们将向cookie商店添加cookie

cookies.forEach(cookie -> cookieManager.getCookieStore().add(null, cookie));

Let’s check if a cookie called username is present, and if not, we will add it to the cookie store with a value of “john”:

让我们检查是否有一个叫做username的cookie,如果没有,我们将把它添加到cookie存储中,值为 “john”。

Optional<HttpCookie> usernameCookie = cookies.stream()
  .findAny().filter(cookie -> cookie.getName().equals("username"));
if (usernameCookie == null) {
    cookieManager.getCookieStore().add(null, new HttpCookie("username", "john"));
}

Finally, to add the cookies to the request, we need to set the Cookie header, after closing and reopening the connection:

最后,为了将cookie添加到请求中,我们需要在关闭和重开连接后设置Cookie头。

con.disconnect();
con = (HttpURLConnection) url.openConnection();

con.setRequestProperty("Cookie", 
  StringUtils.join(cookieManager.getCookieStore().getCookies(), ";"));

8. Handling Redirects

8.处理重定向

We can enable or disable automatically following redirects for a specific connection by using the setInstanceFollowRedirects() method with true or false parameter:

我们可以通过使用带有truefalse参数的setInstanceFollowRedirects()方法来启用或禁用特定连接的自动跟踪重定向

con.setInstanceFollowRedirects(false);

It is also possible to enable or disable automatic redirect for all connections:

也可以启用或禁用所有连接的自动重定向

HttpUrlConnection.setFollowRedirects(false);

By default, the behavior is enabled.

默认情况下,该行为被启用。

When a request returns a status code 301 or 302, indicating a redirect, we can retrieve the Location header and create a new request to the new URL:

当一个请求返回状态代码301或302,表示重定向时,我们可以检索Location头并创建一个新的请求到新的URL。

if (status == HttpURLConnection.HTTP_MOVED_TEMP
  || status == HttpURLConnection.HTTP_MOVED_PERM) {
    String location = con.getHeaderField("Location");
    URL newUrl = new URL(location);
    con = (HttpURLConnection) newUrl.openConnection();
}

9. Reading the Response

9.阅读答复

Reading the response of the request can be done by parsing the InputStream of the HttpUrlConnection instance.

读取请求的响应可以通过解析HttpUrlConnection实例的InputStream来完成。

To execute the request, we can use the getResponseCode(), connect(), getInputStream() or getOutputStream() methods:

为了执行请求,我们可以使用getResponseCode()connect()getInputStream() getOutputStream()方法

int status = con.getResponseCode();

Finally, let’s read the response of the request and place it in a content String:

最后,让我们读取请求的响应,并将其放在一个content String中。

BufferedReader in = new BufferedReader(
  new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer content = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
    content.append(inputLine);
}
in.close();

To close the connection, we can use the disconnect() method:

为了关闭连接,我们可以使用disconnect()方法。

con.disconnect();

10. Reading the Response on Failed Requests

10.读取失败请求的响应

If the request fails, trying to read the InputStream of the HttpUrlConnection instance won’t work. Instead, we can consume the stream provided by HttpUrlConnection.getErrorStream().

如果请求失败了,试图读取HttpUrlConnection实例的InputStream将不起作用。相反,我们可以消耗由HttpUrlConnection.getErrorStream()提供的流。

We can decide which InputStream to use by comparing the HTTP status code:

我们可以通过比较HTTP状态代码来决定使用哪个InputStream

int status = con.getResponseCode();

Reader streamReader = null;

if (status > 299) {
    streamReader = new InputStreamReader(con.getErrorStream());
} else {
    streamReader = new InputStreamReader(con.getInputStream());
}

And finally, we can read the streamReader in the same way as the previous section.

最后,我们可以用与上一节相同的方法读取streamReader

11. Building the Full Response

11.构建全面响应

It’s not possible to get the full response representation using the HttpUrlConnection instance.

使用HttpUrlConnection实例不可能获得完整的响应表示。

However, we can build it using some of the methods that the HttpUrlConnection instance offers:

然而,我们可以使用HttpUrlConnectioninstance提供的一些方法来构建它

public class FullResponseBuilder {
    public static String getFullResponse(HttpURLConnection con) throws IOException {
        StringBuilder fullResponseBuilder = new StringBuilder();

        // read status and message

        // read headers

        // read response content

        return fullResponseBuilder.toString();
    }
}

Here, we’re reading the parts of the responses, including the status code, status message and headers, and adding these to a StringBuilder instance.

在这里,我们正在读取响应的部分,包括状态代码、状态信息和头信息,并将这些添加到一个StringBuilder实例中。

First, let’s add the response status information:

首先,让我们添加响应状态信息

fullResponseBuilder.append(con.getResponseCode())
  .append(" ")
  .append(con.getResponseMessage())
  .append("\n");

Next, we’ll get the headers using getHeaderFields() and add each of them to our StringBuilder in the format HeaderName: HeaderValues:

接下来,我们将使用getHeaderFields()获得头信息,并将它们分别添加到我们的StringBuilder中,格式为HeaderName。HeaderValues

con.getHeaderFields().entrySet().stream()
  .filter(entry -> entry.getKey() != null)
  .forEach(entry -> {
      fullResponseBuilder.append(entry.getKey()).append(": ");
      List headerValues = entry.getValue();
      Iterator it = headerValues.iterator();
      if (it.hasNext()) {
          fullResponseBuilder.append(it.next());
          while (it.hasNext()) {
              fullResponseBuilder.append(", ").append(it.next());
          }
      }
      fullResponseBuilder.append("\n");
});

Finally, we’ll read the response content as we did previously and append it.

最后,我们将像之前那样读取响应内容,并将其追加。

Note that the getFullResponse method will validate whether the request was successful or not in order to decide if it needs to use con.getInputStream() or con.getErrorStream() to retrieve the request’s content.

注意,getFullResponse方法将验证请求是否成功,以决定是否需要使用con.getInputStream()con.getErrorStream()来检索请求的内容。

12. Conclusion

12.结论

In this article, we showed how we can perform HTTP requests using the HttpUrlConnection class.

在这篇文章中,我们展示了如何使用HttpUrlConnection类执行HTTP请求。

The full source code of the examples can be found over on GitHub.

这些例子的完整源代码可以在GitHub上找到