HttpURLConnection in Java
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
| Method | Description |
|---|---|
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
| Range | Meaning | Examples |
|---|---|---|
| 2xx | Success | 200 OK, 201 Created, 204 No Content |
| 3xx | Redirect | 301 Moved Permanently, 302 Found, 304 Not Modified |
| 4xx | The client's mistake | 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found |
| 5xx | The server's failure | 500 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.
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);
}
}
}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 requestExplanation:
- GET:
openConnection()returns aURLConnection, so it must be cast toHttpURLConnection.getResponseCode()sends the request and returns 200. - POST:
setDoOutput(true)allows a body. The JSON is written togetOutputStream(), and closing that stream finishes the request. The server answers 201 Created. - The error case shows this class's biggest surprise. For a 404,
getInputStream()throwsFileNotFoundException, an exception named after files. The body of the error response is in a different stream,getErrorStream(). disconnect()releases the connection when you are finished.
HttpURLConnection vs HttpClient
| Point | HttpURLConnection | HttpClient (Java 11+) |
|---|---|---|
| HTTP versions | HTTP/1.1 only | HTTP/1.1 and HTTP/2 |
| Style | Blocking only | Blocking (send) and asynchronous (sendAsync) |
| API | Mutable object, casts, two streams | Immutable builders, typed request and response |
| Error responses | Exception from getInputStream; body in getErrorStream | A normal response with its status code and body |
| Body handling | Manual streams | Ready-made handlers: string, bytes, file, lines |
| Reuse | One object per request | One client shared by many requests |
| Other features | Basic | WebSocket, per-request timeouts, authenticators, cookie handlers |
| Status | Legacy, still supported | The 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
- Easy: Send a DELETE request to
/studentson the local server and print the status code (the server answers 405). - Harder: Write a helper
String request(String method, String address, String body)that returns the response body for both success and error statuses.
Key Takeaways
Was this tutorial helpful?