Monday, November 25, 2013

Forward versus redirect

Controller (in this context, an implementation of HttpServlet) may perform either a forward or a redirect operation at the end of processing a request. It's important to understand the difference between these two cases, in particular with respect to browser reloads of web pages.Forward
  • a forward is performed internally by the servlet
  • the browser is completely unaware that it has taken place, so its original URL remains intact
  • any browser reload of the resulting page will simple repeat the original request, with the original URL
Redirect
  • a redirect is a two step process, where the web application instructs the browser to fetch a second URL, which differs from the original
  • a browser reload of the second URL will not repeat the original request, but will rather fetch the second URL
  • redirect is marginally slower than a forward, since it requires two browser requests, not one
  • objects placed in the original request scope are not available to the second request
In general, a forward should be used if the operation can be safely repeated upon a browser reload of the resulting web page; otherwise, redirect must be used. Typically, if the operation performs an edit on the datastore, then a redirect, not a forward, is required. This is simply to avoid the possibility of inadvertently duplicating an edit to the database.More explicitly (in terms of common SQL operations) :
  • for SELECT operations, use a forward
  • for INSERTUPDATE, or DELETE operations, use a redirect
In HTML, a 
 tag can either GET or POST its data. In this context, a GET corresponds to a SELECT-then-forward, and a POST corresponds to an edit-then-redirect.It's strongly recommended that forms for the input of search criteria should use GET, while forms for editing database records should use POST.
The most common symptom of not using forward/redirect properly is a warning message in a browser, asking the user if they really wish to POST their form data a second time.
Example
This example is after the style of the WEB4J Controller class. The important methods of the Servlet API are:

import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import hirondelle.web4j.action.Action;
import hirondelle.web4j.action.ResponsePage;
import hirondelle.web4j.request.RequestParser;
import hirondelle.web4j.model.BadRequestException;

public class RedirectForward extends HttpServlet {
  
  //..many items elided

  @Override public final void doGet(
      HttpServletRequest aRequest, HttpServletResponse aResponse
   ) throws ServletException, IOException {
    processRequest(aRequest, aResponse);
  }

  @Override public final void doPost(
      HttpServletRequest aRequest, HttpServletResponse aResponse
   ) throws ServletException, IOException {
    processRequest(aRequest, aResponse);
  }

  /**
  * Handle all HTTP GET and POST requests.
  */
  protected void processRequest(
    HttpServletRequest aRequest, HttpServletResponse aResponse
  ) throws ServletException, IOException {
    RequestParser requestParser = RequestParser.getInstance(aRequest, aResponse);
    try {
      Action action = requestParser.getWebAction();
      ResponsePage responsePage = action.execute();
      if ( responsePage.getIsRedirect() ) {
        redirect(responsePage, aResponse);
      }
      else {
        forward(responsePage, aRequest, aResponse);
      }
    }
    catch (BadRequestException ex){
      //..elided
      //use Response.sendError()
    }
    catch (Throwable ex) {
      //..elided
      //use Response.sendError()
    }
  }
  
  // PRIVATE //
  
  private void redirect(
    ResponsePage aDestinationPage, HttpServletResponse aResponse
  ) throws IOException {
    String urlWithSessionID = aResponse.encodeRedirectURL(aDestinationPage.toString());
    aResponse.sendRedirect( urlWithSessionID );
  }

  private void forward(
    ResponsePage aResponsePage, HttpServletRequest aRequest, HttpServletResponse aResponse
  ) throws ServletException, IOException {
    RequestDispatcher dispatcher = aRequest.getRequestDispatcher(aResponsePage.toString());
    dispatcher.forward(aRequest, aResponse);
  }
} 

No comments: