Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Help me better understand Struts2, validation, and stateful actions

As I understand it, Struts2 action class instances can (unlike Struts1) be stateful, because each GET or POST to an action creates a new instance of the backing action class.

I also see that there's a standard(?) idiom (Pattern?) to provide input forms: the same .jsp is used as the View component of two different actions, like this:

<action name="showForm" class="defaultActionThatDoesNothingExceptReturnSuccess">
  <result name="success">inputForm.jsp</result>
</action>

<action name="validateAndProcessForm" class="realAction">
  <result name="input">inputForm.jsp</result>
  <result name="success">formProcessed.jsp</result>
</action>

The first action just displays the form, without validating the input or processing it. The form in the .jsp posts to the second action:

<s:form action="validateAndProcessForm" method="post">

and that second action validates the posted fields/parameters, returning "input" if the form's inputs are incomplete or invalid, or actually calling the action class's execute if the inputs are complete and valid, thus processing the form and returning the (e.g.) formProcessed.jsp that displays something like "thanks for your input!".

So we have this sort of "picket fence" idiom:

defaultAction-           -> realAction-
             |           |     |       |
             -> input.jsp-  <---       -> success.jsp

This is done so that the first time input.jsp is displayed, validations aren't called (and so validation errors are not shown), but that after the submit button on that jsp is clicked, the "real" action will validate the input, possibly passing back errors calling out invalid input which the input.jsp will display.

Which brings us back to stateful, non-singleton actions; because the action is stateful and thus not shared across GETs or POSTs, and each instance is instantiated just for that GET or POST, the action has no way to know if a particular session has "GETted" the same page multiple times. So GETting showForm.action will never validate, and GETing validateAndProcessForm will always validate (and show errors if the parameters are invalid), even if that GET is the first time a particular session has "GETted" that URL.

Which is why we need the "fence post": the first action just to display the form, the second to capture the input.

Is my understanding correct? Is there a less verbose way to do this, to not validate input on the initial GET, but to validate on the POST, without having to have two actions for every form?

like image 526
tpdi Avatar asked Jan 13 '11 03:01

tpdi


People also ask

How does struts2 validation work?

When the user presses the submit button, Struts 2 will automatically execute the validate method and if any of the “if” statements listed inside the method are true, Struts 2 will call its addFieldError method. If any errors have been added, then Struts 2 will not proceed to call the execute method.

What is interceptor struts2?

Interceptor is an object that is invoked at the preprocessing and postprocessing of a request. In Struts 2, interceptor is used to perform operations such as validation, exception handling, internationalization, displaying intermediate result etc.

Which interceptor merely records the duration of the execution?

Timer. This simple interceptor merely records the duration of an execution.


1 Answers

There is another way to perform what you want without the picket fence. The validation interceptor, by default, does not fire for the input method. You can therefore update your struts.xml to the following:

<action name="*MyForm" method="{1}" class="realAction">
  <result name="input">inputForm.jsp</result>
  <result name="success">formProcessed.jsp</result>
</action>

With this setup, you do not need the empty action at all. When you first go to the form, you would go to the url "inputMyForm", and have your form action just point to "MyForm". The {1} in the method block just means that the framework will call whatever method matches the * from the action name. If the * match is empty, it defaults to the execute method. So you get the following kinds of actions:

  • inputMyForm would go to the input() method of your action class
  • MyForm would go to the execute() method of your action class
  • executeMyForm would go to the execute() method of your action class
  • customMethodNameMyForm would go to the customMethodName() method of your action class

Since the validator interceptor excludes any actions going to the input method, you can set up whatever validation you want for this action, and it will only look for it when you submit the form. Since every time you submit the form, it is going to the execute method, the validation will occur every time you submit the form.

Also, if you are extending the ActionSupport class, that class already defines the input() method, so you would not even need to change your action class to accomplish this.

like image 67
Dustin Wilhelmi Avatar answered Sep 18 '22 20:09

Dustin Wilhelmi