Полезная информация

Exploring Java

Previous: 12.4 Writing a Protocol HandlerChapter 12
Working with URLs
Next: 13. The Abstract Window Toolkit
 

12.5 Talking to CGI Programs

CGI stands for Common Gateway Interface; it is an API for writing applications (often scripts) that can be run by a Web server to service a particular range of URLs. CGIs can perform dynamic activities like automatically generating Web documents. More important, they can accept data sent from the browser; they are most frequently used to process forms. The name=value pairs of the form fields are encoded by the browser in a special format and sent to the application using one of two methods. The first method, called GET, places the user's input directly into the URL and requests the corresponding document. The server recognizes that the first part of the URL refers to a CGI program and invokes it, passing along the information encoded in the URL as a string parameter. The second method for submitting data to a server is called POST. In this method, the browser uses the HTTP POST command to ask the server to accept the encoded data and pass it to the CGI program as a stream. Most CGI programs use utilities that shield them from the details of receiving the data and decoding it. But because of common limitations, the POST method is more suitable for sending large amounts of data.

In Java, we can create a URL that refers to a CGI program and send it data using either the GET or POST methods. Why would we want to talk to a CGI? Well, they are currently the most widely used technique for building advanced Web applications. Other techniques, like opening a socket or writing a Java servlet, are coming on strong, but CGI has been in widespread use for several years. Another important reason for using CGI is that many firewalls block socket connections entirely. But all firewalls that allow Web access have to let us use GET and POST to talk to CGIs. So CGI programs can be used as a last resort communications mechanism between applets and servers.

12.5.1 Using the GET Method

Using the GET method is pretty easy. All we have to do is create a URL pointing to a CGI program and use a simple convention to tack on the encoded name=value pairs that make up our data. For example, the following code snippet opens a URL to a CGI program called login.cgi on the server myhost and passes it two name=value pairs. It also prints whatever text the CGI sends back:

    URL url = new URL(
        "http://myhost/cgi-bin/login.cgi?Name=Pat&Password=foobar");

    BufferedReader bin = new BufferedReader (
        new InputStreamReader( url.openStream() ));

    String line;
    while ( (line = bin.readLine()) != null )  
        System.out.println( line );
To form the new URL, we start with the URL of login.cgi; we add a question mark(?), which marks the beginning of the data, followed by the first name=value pair. We can add as many pairs as we want, separated by ampersand (&) characters. The rest of our code opens the stream and reads back the data. Remember that creating a URL doesn't actually open the connection. In this case, the URL connection was made implicitly when we called openStream(). Although we are assuming here that our CGI sends back text, it could send anything. We could use the getContentType() method of the URL to check the MIME type of any returned data, and try to retrieve the data as an object using getContent().

Finally, it's important to point out that we have skipped a step here. This example works because our name=value pairs happen to be simple text. If any "non-printable" or special characters (including "?" or "&") are in the data itself, they have to be encoded first. The java.net.URLEncoder class provides a utility for encoding the data. We'll show how to use it in the next example.

12.5.2 Using the POST Method

Let's create an applet that acts like a simple form. It presents two text fields, Name and Password, and a Post button that sends the data to a specified URL:

import java.net.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;

public class Post extends java.applet.Applet implements ActionListener {
    TextField nameField, passwordField;

    GridBagConstraints constraints = new GridBagConstraints();
    void addGB( Component component, int x, int y  ) {
        constraints.gridx = x;  constraints.gridy = y;
        add ( component, constraints );
    }
    public void init() {
        Button postButton = new Button("Post");
        postButton.addActionListener( this );
        setLayout( new GridBagLayout() );
        addGB( new Label("Name:"),              0,0 );
        addGB( nameField = new TextField(20),   1,0 );
        addGB( new Label("Password:"),          0,1 );
        addGB( passwordField = new TextField(20),1,1 );
        Panel p = new Panel();
        p.add ( postButton );
        constraints.gridwidth = 2;
        addGB( p, 0,2 );
    }
    public void actionPerformed(ActionEvent e) {
        postData();
    }

    private void postData() {
        StringBuffer sb = new StringBuffer();
        sb.append( URLEncoder.encode("Name") + "=" );
        sb.append( URLEncoder.encode(nameField.getText()) );
        sb.append( "&" + URLEncoder.encode("Password") + "=" );
        sb.append( URLEncoder.encode(passwordField.getText()) );
        String formData = sb.toString();
        try {
            URL url = new URL( getParameter("postURL") );
            HttpURLConnection urlcon = (HttpURLConnection)url.openConnection();
            urlcon.setRequestMethod("POST");
            urlcon.setRequestProperty("Content-type", 
                "application/x-www-form-urlencoded");
            urlcon.setDoOutput(true);
            urlcon.setDoInput(true);
            PrintWriter pout = new PrintWriter( new OutputStreamWriter(
                urlcon.getOutputStream(), "8859_1"), true );
            pout.print( formData );
            pout.flush();

            // read results...
            if ( urlcon.getResponseCode() != HttpURLConnection.HTTP_OK ) {
                System.out.println("Bad post...");
                return;
            }
            InputStream in = urlcon.getInputStream();
            // ...

        } catch (MalformedURLException e) {
            System.out.println("Bad postURL");      
        } catch (IOException e2) {
            System.out.println("I/O error: "+e2);
        }
    }
}
To use this applet, you must specify the URL of your CGI program by using the "postURL" HTML parameter. For example:
<param name="postURL" value="http://myhost/cgi-bin/post-login.cgi" >
The beginning of the applet creates the form; there's nothing here that won't be obvious after you've read the chapters on AWT. All the magic happens in the private postData() method. First we create a StringBuffer and use it to append name=value pairs, separated by ampersands. (We don't need the initial question mark when we're using the POST method.) Each pair is first encoded using the static URLEncoder.encode() method. We ran the name fields through the encoder as well as the value fields, even though we know from the first example that they contain no special characters, and we didn't have to.

Next we set up the connection to the CGI program. In our previous example, we didn't have to do anything special to send the data, because the request was made by the Web browser for us. Here, we have to carry some of the weight of talking to the remote Web server. Fortunately, the HttpURLConnection object does most of the work for us; we just have to tell it that we want to do a POST to the URL and the type of data we are sending. We ask for the URLConnection object using the URL's openConnection() method. We know that we are using HTTP, so we should be able to cast it to an HttpURLConnection type, which has the support we need.

Next we use setRequestMethod() to tell the connection we want to POST. We also use setRequestProperty() to set the "Content-Type" field of our HTTP request to the appropriate type--in this case, the proper MIME type for encoded form data. (This helps the CGI sort out what we're sending.) Finally, we use the setDoOutput() and setDoInput() methods to tell the connection that we want to send and receive stream data. We get an output stream from the connection with getOutputStream() and create a PrintWriter so we can easily write our encoded data.

After we post the data, we call getResponseCode() to check the HTTP response code from the server to see if it indicates the POST was successful. Other response codes (defined as constants in HttpURLConnection) indicate various failures. At the end, we indicate where we could read back the response. I haven't implemented anything; you can add your own code if you want.

Although form-encoded data is the most common, other types of communications are possible. We could have used the input and output streams to exchange arbitrary data with the CGI.

12.5.3 What About HTTPS?

HTTPS is the standard HTTP protocol run over SSL (Secure Socket Layer) sockets, which use public-key encryption techniques to encrypt the data sent. Most browsers currently come with built-in support for HTTPS (or raw SSL sockets). Therefore, if the server supports HTTPS, you can use the browser to send and receive secure data simply by specifying the https: protocol in your URLs.


Previous: 12.4 Writing a Protocol HandlerExploring JavaNext: 13. The Abstract Window Toolkit
12.4 Writing a Protocol HandlerBook Index13. The Abstract Window Toolkit

Other Books in this LibraryJava in a NutshellJava Language ReferenceJava AWTJava Fundamental ClassesExploring Java