It's often necessary for more than one component to share control of a request. For example, one servlet may be responsible for authenticating and authorizing a client, while it's the job of a different servlet to retrieve some data for the user. The sharing of a request can be accomplished in several different ways.
There are important differences between how a web container processes a forward request versus how it processes a redirect. The Struts front controller servlet, discussed in Chapter 1, will always perform one or the other for a typical request, so it's important that you understand these differences and the impact that each mechanism will have on your application.
When the sendRedirect( ) method is invoked, it causes the web container to return to the browser a response indicating that a new URL should be requested. Because the browser issues a completely new request, any objects that are stored as request attributes before the redirect occurs will be lost. This is one of the biggest differences between a forward and redirect. Figure 2-5 illustrates why this occurs.
Because of the extra round trip that occurs, a redirect is slower than a forward. Example 2-1 provides an example servlet that performs a redirect for a JSP page called result.jsp when a request for the servlet is issued.
package com.oreilly.struts.chapter2examples;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.RequestDispatcher;
/**
* Perform a redirect for the page "redirect.jsp"
*/
public class RedirectServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
redirect( request, response );
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
redirect( request, response );
}
/**
* Set a few URL parameters and objects for the request to see what happens
* to them during a redirect.
*/
protected void redirect(HttpServletRequest req,HttpServletResponse resp)
throws ServletException, IOException{
log( "A request arrived for " + req.getServletPath( ) );
// Put some objects into request scope
req.setAttribute( "firstName", "John" );
req.setAttribute( "lastName", "Doe" );
String contextPath = req.getContextPath( );
String redirectStr = contextPath + "/result.jsp?username=foo&password=bar";
log( "redirecting to " + redirectStr );
// Always call the encodeRedirectURL( ) method when perfoming a redirect
resp.sendRedirect( resp.encodeRedirectURL(redirectStr) );
}
}
When the servlet in Example 2-1 receives either a GET or POST request, it calls the redirect( ) method and passes to it the HttpServletRequest and HttpServletResponse objects. The method sets two String objects into the request, to demonstrate that they will not be available after the redirect, and it creates a String that will become the URL for which the client is told to make a new request. After the encodeRedirectURL( ) method is called and passed the redirect string, the sendRedirect( ) method is invoked on the response object.
|
The JSP page to which the servlet will redirect is shown in Example 2-2.
<html>
<head>
<title>Struts Redirect/Forward Example</title>
</head>
<body>
<img src="images\tomcat-power.gif">
<br>
<%
String firstName = (String)request.getAttribute( "firstName" );
if ( firstName == null ){
firstName = "Not found in request";
}
String lastName = (String)request.getAttribute( "lastName" );
if ( lastName == null ){
lastName = "Not found in request";
}
%>
<b>First Name:</b> <%=firstName%><br>
<b>Last Name:</b> <%=lastName%><br>
</body>
</html>
When you enter the URL for this servlet (http://localhost:8080/servlet/com.oreilly.struts.chapter2examples.RedirectServlet) in a browser, the browser output will look like Figure 2-6.
Notice that the first name and last name arguments that were set in the servlet were not found in the request. This is because a second request actually was issued for this page. Fortunately, we can peek behind the scenes and observe what's taking place.
Let's look at the HTTP response that comes back to the client when the RedirectServlet is requested. We can view the response using a standard Telnet session. An HTTP connection uses a simple network socket to communicate with the server, so we can partially simulate the interaction between a browser and a server using the Telnet application. You can establish a connection to a web server using Telnet by connecting to the port on which the server is listening (usually port 80 or, in the case of Tomcat, 8080):
telnet localhost 8080
You can do this from the command line, whether you're using a DOS shell or Unix. The Telnet session will alert you if it's unable to connect to a server with the specified hostname and port number.
If a connection is established, the Telnet session will sit and wait for you to enter the HTTP request information. The only information you are required to enter is the first line of the request, which tells the server what resource the client wants. Each line in the request is a text line, separated by a newline character. The request header ends with a blank line, so you actually have to press Enter twice to let Telnet know you are done with the request. Figure 2-7 shows the HTTP response message Tomcat returns when a request is made for the RedirectServlet resource.
In Figure 2-7, the first line of the request issues a GET and a path for a resource. The path is the portion of the HTTP request that comes after the hostname and includes the preceding "/" character. The "HTTP/1.0" string at the end of the GET request is the HTTP version protocol.
|
Everything after the first line is a response from the server. The entire HTML output at the bottom comes from Tomcat; it indicates that the original request has performed a redirect and that the client should request a new URL.
If you look at the Location response header, which is the third line in Figure 2-7, you can see that the server has informed the client what the URL for the new request should be. Any URL parameters attached to the original request will be present in the new request. When the browser issues the new request, these parameters will be sent along with it. This technique of using Telnet provides a simple way of interacting with a web server and viewing what responses it sends back to the client.
Hopefully, you now have a better understanding of how a redirect works. It should also be clear why any objects placed into the original request are not available to the redirected resource. This will become very important later, when we discuss the Struts framework.
|
A forward differs from a redirect in several ways. When you invoke a forward for a request, the request is sent to another resource on the server, without the client being informed that a different resource is going to process the request. This process occurs completely within the web container, and the client is never the wiser. Unlike with a redirect, objects can be stored in the request and passed along for the next resource to use. Figure 2-8 illustrates the steps that take place when a request is forwarded.
Because a forward takes place completely on the server and there is no communication with the client, the performance is better than with a redirect. However, there are some differences in how a forward deals with relative URLs. Example 2-3 should make this clearer.
package com.oreilly.struts.chapter2examples;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.RequestDispatcher;
/**
* Perform a forward to the page "redirect.jsp"
*/
public class ForwardServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
forward( request, response );
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
forward( request, response );
}
/**
* Set a few URL parameters and objects for the request to see what happens
* to them during a forward.
*/
protected void forward(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
log( "A request arrived for " + req.getServletPath( ) );
// Put some objects into request scope
req.setAttribute( "firstName", "John" );
req.setAttribute( "lastName", "Doe" );
String redirectStr = "/result.jsp?username=foo&password=bar";
this.log( "forwarding to " + redirectStr );
RequestDispatcher dispatcher = req.getRequestDispatcher( redirectStr );
dispatcher.forward( req, resp );
}
}
When the servlet in Example 2-3 receives either a GET or POST request, it calls the forward( ) method and passes the HttpServletRequest and HttpServletResponse objects to it. As with the redirect example, two String objects are set into the request. However, in contrast with the redirect example, the objects will be available after the forward. Next, the servlet creates a redirect path to which the request will be passed. A RequestDispatcher is created, and the forward( ) method is invoked on it. We'll use the same JSP page from Example 2-2. In the browser, we enter a URL like http://localhost:8080/servlet/com.oreilly.struts.chapter2examples.ForwardServlet. The browser output should look similar to Figure 2-9.
There are two things to note about Figure 2-9. The first is that the first and last name fields have values. This is because the objects were placed into the request before the forward occurred, and result.jsp was able to retrieve the values and use them in the page.
The second is the URL in the address bar. Notice that the URL back in Figure 2-6 showed the new URL, whereas it didn't change in Figure 2-9. This shows further that the client is not aware that a forward occurred—the browser was not notified that a different servlet handled the request.
|
It's difficult to decree whether Struts applications should use redirects or forwards, as we haven't discussed the framework yet. However, there are some key points about using each approach. Both mechanisms have their pros and cons within Struts and web applications in general. For many situations, a forward is recommended over a redirect, because objects that are stored in the request scope are easily available to the presentation components. In fact, using a forward is the default for the Struts framework. Another advantage of forwards is that they're much more efficient, as the client is not required to issue a new request.
There are some situations, however, where a redirect is necessary or preferred over a forward. When forwards are used, the URLs are not always consistent with the state of the client application. If the browser's Refresh button is pressed, an incorrect request may occur. This problem will be explored further in Chapter 5.
If you are using relative paths for images and other resources in your JSP pages, forwards can be problematic. Because the browser has no indication that a forward has occurred, the relative paths will be relative to the initial servlet, not to the JSP to which it was forwarded. As you'll see in Chapter 8, the Struts framework provides a JSP custom tag that can help to alleviate the hassle associated with this behavior.