Action Handler

The Studs MVC component is focused on receiving HTTP requests (typically from a web browser), deciding what business logic function is to be performed, and then delegating responsibility for producing the next phase of the user interface to an appropriate view component. Instead of writing an individual servlet for each request and mapping it to a request URI in the web.xml file, Studs acts as a Front Controller, parsing the URL and marshalling the requests to an implementation of the Action base class that matches the path of the <action> element in the struts-config.xml file. Using this model, each action has access to the Studs MVC configuration and the resources that it makes available, such as form validation, data sources, exception handlers, message resources and flow control.

Goals

The purpose of the action implementation is to provide application-specific processing of a request. Generally, each Action class provides a very specific unit of work. However, it is possible to combine the processing of several actions into one by extending the DispatchAction class instead. Check out Grouping Actions for more details.

Although it is certainly possible to code domain or business logic inside the action class, best practices suggest that you should abstract domain logic away from the controller and place it in its own domain-specific class. Therefore, most of the this functionality should be delegated to a business logic class such as a manager or a data access object. The action is really just a bridge between the front controller and the model.

The primary concerns of the action are (not necessarily sequential):

  • validating the user session (if appropriate)
  • prepopulating form objects for the view
  • persisting form object data to the backend (typically by delegation to a business logic object)
  • adding data to the request or session for use in the view
  • deciding where to hand off control, either to a view or another action

Setting Up

In order for an action to be invoked, a developer needs to externalize the mappings of specific requests to Action classes in the struts-config.xml configuration file. When the ActionServlet is first loaded, it parses this file and prepares to accept requests. By convention, HTTP requests that end in .do get redirected to the ActionServlet instance for dispatching to the appropriate Action implementation. The following is a typical mapping:

<action>
  path="/postComment"
  type="weblog.PostCommentAction"
  name="commentForm"
  scope="request">
  <forward name="success" path="/comments.psp" />
</action>

In this case, when the request comes in with the URI ending in /postComment.do, the execute() method of the PostCommentAction classes is executed. If that method returns an ActionForward with the name success, the result is passed to the /comments.psp view.

Implementing an Action

The base action class, studs.action.Action, must be extended to provide a customized action handler for responding to a user request. Each implementation should override the execute() method. The goal of this method is to process the request by reading data either from the request object or from the form object, deciding what backend logic needs be executed, and then returning an instance of the ActionForward class that identifies where control should be handed off (typcially another action or a phase view).

function &execute(&$mapping, &$form, &$request, &$response) {}

The execute() method receives four objects with which to work:

  • request - An instance of HttpServletRequest; holds the HTTP headers, request parameters, and form data
  • response - An instance of HttpServletResponse; can be used to manipulate the output to the user (headers, redirects)
  • form - An instance of an ActionForm implementation; serves as an object representation of the html form, populated by the controller
  • mapping - An instance of ActionMapping; holds the mapping data specified in struts-config.xml for this action handler

Associating a Form

The form object is associated with the action handler in the struts-config.xml file. Prior to invoking the execute() method, the controller will take data from the request and stuff them into the form object by mapping the names of the parameters to setter methods. The controller will then trigger the validate() method to determine if the form object has the expected data. This processes is detailed on the Form Processing page.

Forwarding the Flow

Before the execute() method comes to a close, it must return an instance of ActionForward which dictates the next step in the control flow. Typically, this is done by calling the findForward() method on the mapping object.

return $mapping->findForward('nextPage');

The forwards are specified in the struts-config.xml file, either as global forwards (available to all actions) or local forwards (available only to the current action). All forwards are referenced by keyword so that the target is abtracted from the action and thus can be controlled from a centralized location. In the case above, the forward identified by nextPage could be a phase file such as /WEB-INF/pages/users.psp.

If a null value is returned, then the control flow halts. This is often necessary when the goal of the action is to push the contents of a file to the end user for downloading. With the recent popularily of “Ajax”, this can also be used to send header-only responses to the request.

Alternatively, an ActionForward can be instantiated by the action and returned. However, this method results in unnecessary coupling between the action and the next page in the work flow.

Token Processing

One way to prevent the “double submit” problem or to protect the client from stale data is to use form tokens. Tokens are essentially signatures assigned to a form to keep tabs on its state. If a user were to submit a form and then attempt to resubmit the form, the token would not validate on the second post, hence protecting the application from entering an invalid state.

Unlike form validation, token processing is not automatically handled by the controller. This logic is deferred to the action since it requires knowledge of the state of the business logic.

The first step in token processing is to save a token into the session when the form is being prepared. This obviously requires that each form must be controlled by an action. The call looks like:

function &execute(&$mapping, &$form, &$request, &$response)
{
    ...
    $this->saveToken($request);
    ...
}

In order for the token to be placed into the form, the html:form taglib must be used to create the form in the view.

The next step is the check the token for validity in the action handler that is responding to the form submission. It is typical to also clear the token from the session at this time. Typical action logic would look like:

function &execute(&$mapping, &$form, &$request, &$response)
{
    ...
    if (!$this->isTokenValid($request, true))
    {
        return $mapping->findForward('listUsers');
    }
    ...
}

Alternatively, a message could be saved to notify the user that the form was not accepted since its state had become invalid.

 
howto/action_handler.txt · Last modified: 2005/09/04 16:13 by 68.50.139.221 (dallen)
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki