HTTP can be viewed as an API. Among frameworks for developing websites and RESTful web services, Rails has pioneered this view of HTTP, which deliberately blurs the distinction between websites that deliver HTML and web services that deliver XML or JSON. In a well-designed Rails application, a GET request for the URI /products is equivalent to the same request for /products.html, and an HTML list of products is returned in response. A GET request against /products.json or /products.xml would return the same list but in JSON or XML, respectively. Rails has an often-copied idiom for combining URIs and HTTP verbs into a RESTful route—the route that a request takes to the code that handles the request. The Rails routing style is an elegant yet practical use of HTTP as an API. Table 1-3 is a summary of the Rails approach. In a URI, a term such as :id, which begins with a colon character, indicates a placeholder or parameter, in this case a placeholder whose intended value is a numerical identifier such as 27.
Table 1-3. Rails routing idioms
HTTP verb | URI (Name) | Meaning |
---|---|---|
GET | /products | Read all products |
POST | /products | Create a new product from information in the POST body |
GET | /products/new | Read the form to create a new product |
GET | /products/:id/edit | Read the form to edit an existing product |
GET | /products/:id | Read a single product |
PUT | /products/:id | Update a product with information in the POST body |
DELETE | /products:id | Delete the specified product |
These verb/name pairs are terse, precise, intuitive, and uniform in style. The pairs illustrate that RESTful conventions can yield simple, clear routing expressions about which operation should be performed on which resource. The POST and PUT verbs are used in requests that have an HTTP body; hence, the request data is in the HTTP message body. The GET and DELETE verbs are used in requests that have no body; hence, the request data, if any, is sent as query string key/value pairs.
The decision about whether to be RESTful in a particular application depends, as always, on practical matters that will come to the fore throughout this book. The current section looked at REST from on high; it is now time to descend into details with code examples. The next section summarizes the overview of HTTP with two Java clients. A first RESTful service follows.
The foregoing descriptions about HTTP can be fleshed out and summarized with two short Java clients,
which can be run against any URL—for a website or a web service. The first client (see Example 1-3) takes a deliberately
low-level approach by building up the HTTP request as a string, one chunk at a time. The second client (see Example 1-4)
uses the Java utility class URLConnection
, which shortens the code and makes the program more readable.
Example 1-3. A simple Java client that makes an HTTP GET
request
import
java.net.Socket
;
import
java.net.URL
;
import
java.net.MalformedURLException
;
import
java.net.UnknownHostException
;
import
java.io.IOException
;
import
java.io.PrintWriter
;
import
java.io.BufferedReader
;
import
java.io.InputStreamReader
;
public
class
SimpleHttpClient
{
public
static
void
main
(
String
[
]
args
)
{
// usage
if
(
args
.
length
<
1
)
{
System
.
err
.
println
(
"Usage: SimpleHttpClient <url>"
);
return
;
}
try
{
// Parse the URL.
URL
url
=
new
URL
(
args
[
0
]);
String
host
=
url
.
getHost
();
String
path
=
url
.
getPath
();
int
port
=
url
.
getPort
();
if
(
port
<
0
)
port
=
80
;
// Send the request.
String
request
=
"GET "
+
path
+
" HTTP/1.1\n"
;
request
+=
"host: "
+
host
;
request
+=
"\n\n"
;
Socket
sock
=
new
Socket
(
host
,
port
);
PrintWriter
writer
=
new
PrintWriter
(
sock
.
getOutputStream
());
writer
.
(
request
);
writer
.
flush
();
// Read and print the response.
BufferedReader
reader
=
new
BufferedReader
(
new
InputStreamReader
(
sock
.
getInputStream
()));
String
next_record
=
null
;
while
((
next_record
=
reader
.
readLine
())
!=
null
)
System
.
out
.
println
(
next_record
);
sock
.
close
();
}
catch
(
MalformedURLException
e
)
{
throw
new
RuntimeException
(
"Please try again. Bad URL.\n"
+
e
);
}
catch
(
UnknownHostException
e
)
{
throw
new
RuntimeException
(
"Please try again. Unknown host.\n"
+
e
);
}
catch
(
IOException
e
)
{
throw
new
RuntimeException
(
"Please try again. Something's wrong.\n"
+
e
);
}
}
}
The SimpleHttpClient
expects, as a command-line argument (line 1), a URL such as
http://www.amazon.com/index.html. After constructing a URL
instance from the
string URL (line 2), the client extracts the host, the path (URI), and the
port number (lines 3, 4, and 5) so that an HTTP GET request can be built in chunks.
Line 6, for example, builds the following start line, given the sample Amazon URL:
GET
/
index
.
html
HTTP
/
1.1
Only the required HTTP header is generated (line 7), with
host
as the key and the IP address of the server (in this case, www.amazon.com
) as
the value. After the request is sent (line 8), the response is read (lines 9 and 10)
and the connection is closed.
Example 1-4. A Java HTTP client that uses the utility URLConnection
class
import
java.net.URL
;
import
java.net.URLConnection
;
import
java.net.MalformedURLException
;
import
java.io.IOException
;
import
java.io.InputStreamReader
;
import
java.io.BufferedReader
;
public
class
UrlConnectionClient
{
public
static
void
main
(
String
[
]
args
)
{
// usage
if
(
args
.
length
<
1
)
{
System
.
err
.
println
(
"Usage: UrlConnectionClient <url>"
);
return
;
}
try
{
// Parse the URL.
URL
url
=
new
URL
(
args
[
0
].
trim
());
// Connect.
URLConnection
sock
=
url
.
openConnection
();
// Read and print.
BufferedReader
reader
=
new
BufferedReader
(
new
InputStreamReader
(
sock
.
getInputStream
()));
String
next_record
=
null
;
while
((
next_record
=
reader
.
readLine
())
!=
null
)
System
.
out
.
println
(
next_record
);
// Close.
reader
.
close
();
}
catch
(
MalformedURLException
e
)
{
throw
new
RuntimeException
(
e
);
}
catch
(
IOException
e
)
{
throw
new
RuntimeException
(
e
);
}
}
}
The UrlConnectionClient
(see Example 1-4) uses the class URLConnection
, which simplifies the code.
This client, like the first, expects a URL as a command-line argument. A URL
instance (line 1) again
is constructed but then used immediately (line 2) to open a connection. By default, the opened connection
is a GET request against the site with the given URL. The response is read chunk by chunk (line 3) and printed.
The connection then is closed (line 4).
Clients such as these occur throughout the forthcoming chapters, especially in examples that involve REST-style services. It is now time to introduce the first RESTful example.
Get Java Web Services: Up and Running, 2nd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.