HTTP as an API

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.

Two HTTP Clients in Java

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) {                                             1
            System.err.println("Usage: SimpleHttpClient <url>");
            return;
        }
        try {
            // Parse the URL.
            URL url = new URL(args[0]);                                    2
            String host = url.getHost();                                   3
            String path = url.getPath();                                   4
            int port = url.getPort();                                      5
            if (port < 0) port = 80;
            // Send the request.
            String request = "GET " + path + " HTTP/1.1\n";                6
            request += "host: " + host;                                    7
            request += "\n\n";
            Socket sock = new Socket(host, port);
            PrintWriter writer = new PrintWriter(sock.getOutputStream());
            writer.print(request);                                         8
            writer.flush();
            // Read and print the response.
            BufferedReader reader =                                        9
                new BufferedReader(new InputStreamReader(sock.getInputStream()));
            String next_record = null;
            while ((next_record = reader.readLine()) != null)              10
                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());                                     1

            // Connect.
            URLConnection sock = url.openConnection();                             2

            // Read and print.
            BufferedReader reader =
                new BufferedReader(new InputStreamReader(sock.getInputStream()));
            String next_record = null;
            while ((next_record = reader.readLine()) != null)                      3
                System.out.println(next_record);

            // Close.
            reader.close();                                                        4
        }
        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.