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?
Best Solution
First a disclaimer beforehand: the posted code snippets are all basic examples. You'll need to handle trivial
IOException
s andRuntimeException
s likeNullPointerException
,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
, but if it is really necessary, cleartext can be enabled in the Application Manifest.Preparing
We first need to know at least the URL and the charset. The parameters are optional and depend on the functional requirements.
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 usingURLEncoder#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.
Any query string should be concatenated to the URL using
?
. TheAccept-Charset
header may hint the server what encoding the parameters are in. If you don't send any query string, then you can leave theAccept-Charset
header away. If you don't need to set any headers, then you can even use theURL#openStream()
shortcut method.Either way, if the other side is an
HttpServlet
, then itsdoGet()
method will be called and the parameters will be available byHttpServletRequest#getParameter()
.For testing purposes, you can print the response body to standard output as below:
Firing an HTTP POST request with query parameters
Setting the
URLConnection#setDoOutput()
totrue
implicitly sets the request method to POST. The standard HTTP POST as web forms do is of typeapplication/x-www-form-urlencoded
wherein the query string is written to the request body.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 thename=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
toHttpURLConnection
and use itsHttpURLConnection#setRequestMethod()
instead. But if you're trying to use the connection for output you still need to setURLConnection#setDoOutput()
totrue
.Either way, if the other side is an
HttpServlet
, then itsdoPost()
method will be called and the parameters will be available byHttpServletRequest#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 usingURLConnection#getInputStream()
and so on. The above examples does exactly that, so theconnect()
call is in fact superfluous.Gathering HTTP response information
You need an
HttpURLConnection
here. Cast it first if necessary.HTTP response headers:
HTTP response encoding:
When the
Content-Type
contains acharset
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.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 aCookieManager
with aCookiePolicy
ofACCEPT_ALL
before sending all HTTP requests.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 firstGET
request and then pass this through the subsequent requests.The
split(";", 2)[0]
is there to get rid of cookie attributes which are irrelevant for the server side likeexpires
,path
, etc. Alternatively, you could also usecookie.substring(0, cookie.indexOf(';'))
instead ofsplit()
.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 usingconnection.setRequestProperty("Content-Length", contentLength);
. This may causeOutOfMemoryException
s whenever you concurrently send large POST requests (e.g. uploading files). To avoid this, you would like to set theHttpURLConnection#setFixedLengthStreamingMode()
.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 HTTPTransfer-Encoding
header tochunked
which will force the request body being sent in chunks. The below example will send the body in chunks of 1 KB.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. TheURLConnection
will by default set it toJava/1.6.0_19
where the last part is obviously the JRE version. You can override this as follows:Use the User-Agent string from a recent browser.
Error handling
If the HTTP response code is
4nn
(Client Error) or5nn
(Server Error), then you may want to read theHttpURLConnection#getErrorStream()
to see if the server has sent any useful error information.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 thehttp.keepAlive
system property tofalse
. You can do this programmatically in the beginning of your application by: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.If the other side is an
HttpServlet
, then itsdoPost()
method will be called and the parts will be available byHttpServletRequest#getPart()
(note, thus notgetParameter()
and so on!). ThegetPart()
method is however relatively new, it's introduced in Servlet 3.0 (Glassfish 3, Tomcat 7, etc.). Prior to Servlet 3.0, your best choice is using Apache Commons FileUpload to parse amultipart/form-data
request. Also see this answer for examples of both the FileUpload and the Servelt 3.0 approaches.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 ajava.security.cert.CertificateException: No subject alternative DNS name matching [hostname] found
orjavax.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 makeHttpsURLConnection
more lenient as to those HTTPS sites and thus not throw those exceptions anymore.Last words
The Apache HttpComponents HttpClient is much more convenient in this all :)
Parsing and extracting HTML
If all you want is parsing and extracting data from HTML, then better use a HTML parser like Jsoup.