HttpURLConnection in Java

Naveen AnamaneniUpdated · 6 min read

HttpURLConnection is the subclass of URLConnection for HTTP and HTTPS. It adds what HTTP needs on top of the general class: a request method (GET, POST, PUT, DELETE), the status code of the response, redirect handling, and a separate stream for error responses. It was Java's only built-in HTTP client until Java 11 introduced HttpClient. It is now considered legacy: it supports only HTTP/1.1, works only in a blocking style, and has an awkward API. You still need to read it, because a great deal of existing code and many older libraries use it. In this lesson, you will learn how to make GET and POST requests with it, how it reports errors, and how it compares with HttpClient.

Methods

MethodDescription
setRequestMethod(method)It is used to choose GET (the default), POST, PUT, DELETE, HEAD or OPTIONS.
setDoOutput(true)It says the request will have a body.
setRequestProperty(name, value)It sets a request header.
getOutputStream()It returns the stream for writing the request body.
getResponseCode()It returns the status code, such as 200 or 404. It sends the request if not yet sent.
getResponseMessage()It returns the status text, such as OK or Not Found.
getInputStream()It returns the body of a successful response.
getErrorStream()It returns the body of an error response (status 400 and above).
setInstanceFollowRedirects(boolean)It turns automatic redirect following on or off.
disconnect()It releases the connection.

Status Codes

RangeMeaningExamples
2xxSuccess200 OK, 201 Created, 204 No Content
3xxRedirect301 Moved Permanently, 302 Found, 304 Not Modified
4xxThe client's mistake400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found
5xxThe server's failure500 Internal Server Error, 503 Service Unavailable

HttpURLConnection has constants for them, such as HttpURLConnection.HTTP_OK (200) and HTTP_NOT_FOUND (404).

Example: GET, POST and an Error

As in the previous lesson, the program starts a small local server so it runs anywhere. The server returns a list for GET, accepts a new student for POST, and has no page at /nothing.

Legacy.java▶ Try it
import com.sun.net.httpserver.HttpServer;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.charset.StandardCharsets;
 
public class Legacy {
    static String read(InputStream in) throws Exception {
        try (in) {
            return new String(in.readAllBytes(), StandardCharsets.UTF_8);
        }
    }
 
    public static void main(String[] args) throws Exception {
        HttpServer server =
            HttpServer.create(new InetSocketAddress("localhost", 0), 0);
        server.createContext("/students", exchange -> {
            String method = exchange.getRequestMethod();
            String reply;
            int status;
            if (method.equals("GET")) {
                status = 200;
                reply = "[\"Navika\",\"Srihitha\"]";
            } else if (method.equals("POST")) {
                String sent = new String(
                    exchange.getRequestBody().readAllBytes(),
                    StandardCharsets.UTF_8);
                status = 201;
                reply = "created " + sent;
            } else {
                status = 405;
                reply = "method not allowed";
            }
            byte[] body = reply.getBytes(StandardCharsets.UTF_8);
            exchange.getResponseHeaders()
                .set("Content-Type", "application/json");
            exchange.sendResponseHeaders(status, body.length);
            exchange.getResponseBody().write(body);
            exchange.close();
        });
        server.start();
        String base = "http://localhost:" + server.getAddress().getPort();
        try {
            HttpURLConnection get = (HttpURLConnection)
                URI.create(base + "/students").toURL().openConnection();
            get.setRequestMethod("GET");
            get.setConnectTimeout(3000);
            get.setReadTimeout(3000);
            System.out.println(get.getResponseCode() + " "
                + get.getResponseMessage());
            System.out.println(get.getHeaderField("Content-Type"));
            System.out.println(read(get.getInputStream()));
            get.disconnect();
 
            HttpURLConnection post = (HttpURLConnection)
                URI.create(base + "/students").toURL().openConnection();
            post.setRequestMethod("POST");
            post.setDoOutput(true);
            post.setRequestProperty("Content-Type", "application/json");
            try (OutputStream out = post.getOutputStream()) {
                out.write("{\"name\":\"Advitha\"}"
                    .getBytes(StandardCharsets.UTF_8));
            }
            System.out.println(post.getResponseCode() + " "
                + read(post.getInputStream()));
            post.disconnect();
 
            HttpURLConnection missing = (HttpURLConnection)
                URI.create(base + "/nothing").toURL().openConnection();
            int code = missing.getResponseCode();
            System.out.println(code + " " + missing.getResponseMessage());
            try {
                missing.getInputStream();
            } catch (FileNotFoundException e) {
                System.out.println("getInputStream: FileNotFoundException");
            }
            System.out.println("error body: "
                + read(missing.getErrorStream()).trim());
            missing.disconnect();
        } finally {
            server.stop(0);
        }
    }
}
Output
200 OK
application/json
["Navika","Srihitha"]
201 created {"name":"Advitha"}
404 Not Found
getInputStream: FileNotFoundException
error body: <h1>404 Not Found</h1>No context found for request

Explanation:

  1. GET: openConnection() returns a URLConnection, so it must be cast to HttpURLConnection. getResponseCode() sends the request and returns 200.
  2. POST: setDoOutput(true) allows a body. The JSON is written to getOutputStream(), and closing that stream finishes the request. The server answers 201 Created.
  3. The error case shows this class's biggest surprise. For a 404, getInputStream() throws FileNotFoundException, an exception named after files. The body of the error response is in a different stream, getErrorStream().
  4. disconnect() releases the connection when you are finished.

HttpURLConnection vs HttpClient

PointHttpURLConnectionHttpClient (Java 11+)
HTTP versionsHTTP/1.1 onlyHTTP/1.1 and HTTP/2
StyleBlocking onlyBlocking (send) and asynchronous (sendAsync)
APIMutable object, casts, two streamsImmutable builders, typed request and response
Error responsesException from getInputStream; body in getErrorStreamA normal response with its status code and body
Body handlingManual streamsReady-made handlers: string, bytes, file, lines
ReuseOne object per requestOne client shared by many requests
Other featuresBasicWebSocket, per-request timeouts, authenticators, cookie handlers
StatusLegacy, still supportedThe recommended client

Common Mistakes

Calling getInputStream() without checking the status. Error statuses throw; read getErrorStream() instead.

Forgetting setDoOutput(true) before writing a body. getOutputStream() throws ProtocolException.

Ignoring the error stream. APIs put the reason for a failure in the error body; without it you only know the number.

No timeouts. As with URLConnection, the default is to wait forever.

Interview Corner

How do you make an HTTP request with HttpURLConnection?

Create a URL, call openConnection() and cast the result to HttpURLConnection. Set the method with setRequestMethod, timeouts and headers with setConnectTimeout, setReadTimeout and setRequestProperty. For a body, call setDoOutput(true) and write to getOutputStream(). Then call getResponseCode(); read getInputStream() for a successful response or getErrorStream() for an error status, and finally disconnect().

Why is HttpClient preferred over HttpURLConnection?

HttpClient (Java 11) supports HTTP/2 and asynchronous requests with CompletableFuture, has an immutable builder-based API, treats error statuses as normal responses, provides ready-made body handlers and publishers, supports WebSocket, and lets one client object be shared across requests. HttpURLConnection is limited to blocking HTTP/1.1 and has an awkward, error-prone API.

Practice

  1. Easy: Send a DELETE request to /students on the local server and print the status code (the server answers 405).
  2. Harder: Write a helper String request(String method, String address, String body) that returns the response body for both success and error statuses.

Quick check: HttpURLConnection

4 questions
1Where is the body of a 404 response?
2What must you call before writing a request body?
3Which HTTP version does HttpURLConnection support?
4What do status codes starting with 5 mean?

Key Takeaways

Was this tutorial helpful?

Lesson 480 of 482