java.lang.Objectorg.springframework.context.support.ApplicationObjectSupport
org.springframework.web.portlet.context.PortletApplicationObjectSupport
org.springframework.web.portlet.handler.PortletContentGenerator
org.springframework.web.portlet.mvc.AbstractController
org.springframework.web.portlet.mvc.BaseCommandController
org.springframework.web.portlet.mvc.AbstractFormController
All Implemented Interfaces:
Controller, PortletContextAware, ApplicationContextAware
Direct Known Subclasses:
AbstractWizardFormController, SimpleFormController
Form controller that auto-populates a form bean from the request.
This, either using a new bean instance per request, or using the same bean
when the sessionForm property has been set to
true.
This class is the base class for both framework subclasses such as SimpleFormController and AbstractWizardFormController and custom form controllers that you may provide yourself.
A form-input view and an after-submission view have to be provided programmatically. To provide those views using configuration properties, use the SimpleFormController .
Subclasses need to override showForm to prepare the form view,
processFormSubmission to handle submit requests, and
renderFormSubmission to display the results of the submit.
For the latter two methods, binding errors like type mismatches will be
reported via the given "errors" holder. For additional custom form validation,
a validator (property inherited from BaseCommandController) can be used,
reporting via the same "errors" instance.
Comparing this Controller to the Struts notion of the Action
shows us that with Spring, you can use any ordinary JavaBeans or database-
backed JavaBeans without having to implement a framework-specific class
(like Struts' ActionForm). More complex properties of JavaBeans
(Dates, Locales, but also your own application-specific or compound types)
can be represented and submitted to the controller, by using the notion of
a java.beans.PropertyEditors. For more information on that
subject, see the workflow of this controller and the explanation of the
BaseCommandController .
This controller is different from it's servlet counterpart in that it must take into account the two phases of a portlet request: the action phase and the render phase. See the JSR-168 spec for more details on these two phases. Be especially aware that the action phase is called only once, but that the render phase will be called repeatedly by the portal; it does this every time the page containing the portlet is updated, even if the activity is in some other portlet. (This is not quite true, the portal can also be told to cache the results of the render for a period of time, but assume it is true for programming purposes.)
Workflow
(and that defined by superclass):
bindOnNewForm is set to true)
Make sure that the initial parameters do not include the parameter that indicates a
form submission has occurred.sessionForm is not set,
formBackingObject is called to retrieve a form object. Otherwise,
the controller tries to find the command object which is already bound
in the session. If it cannot find the object, the action phase does a
call to handleInvalidSubmit which - by default -
tries to create a new form object and resubmit the form. It then sets
a render parameter that will indicate to the render phase that this was
an invalid submit.validateOnBinding is set, a registered Validator
will be invoked. The Validator will check the form object properties,
and register corresponding errors via the given Errors
object.handleInvalidSubmit and
renderInvalidSubmit if you want to change this overall
behavior.In session form mode, a submission without an existing form object in the session is considered invalid, like in the case of a resubmit/reload by the browser. The handleInvalidSubmit / renderInvalidSubmit methods are invoked then, by default trying to resubmit. This can be overridden in subclasses to show corresponding messages or to redirect to a new form, in order to avoid duplicate submissions. The form object in the session can be considered a transaction token in that case.
Make sure that any URLs that take you to your form controller are Render URLs, so that it will not try to treat the initial call as a form submission. If you use action URLs to link to your controller, you will need to override the isFormSubmission method to use a different mechanism for determining whether a form has been submitted. Make sure this method will work for both the ActionRequest and the RenderRequest objects.
Note that views should never retrieve form beans from the session but always from the request, as prepared by the form controller. Remember that some view technologies like Velocity cannot even access a HTTP session.
Exposed configuration properties
(and those defined by superclass):
| name | default | description |
| bindOnNewForm | false | Indicates whether to bind portlet request parameters when creating a new form. Otherwise, the parameters will only be bound on form submission attempts. |
| sessionForm | false | Indicates whether the form object should be kept in the session when a user asks for a new form. This allows you e.g. to retrieve an object from the database, let the user edit it, and then persist it again. Otherwise, a new command object will be created for each request (even when showing the form again after validation errors). |
| redirectAction | false | Specifies whether processFormSubmission is expected to call
ActionResponse.sendRedirect .
This is important because some methods may not be called before
ActionResponse.sendRedirect (e.g.
ActionResponse.setRenderParameter ).
Setting this flag will prevent AbstractFormController from setting render
parameters that it normally needs for the render phase.
If this is set true and sendRedirect is not called, then
processFormSubmission must call
setFormSubmit .
Otherwise, the render phase will not realize the form was submitted
and will simply display a new blank form. |
| renderParameters | null | An array of parameters that will be passed forward from the action
phase to the render phase if the form needs to be displayed
again. These can also be passed forward explicitly by calling
the passRenderParameters method from any action
phase method. Abstract descendants of this controller should follow
similar behavior. If there are parameters you need in
renderFormSubmission, then you need to pass those
forward from processFormSubmission. If you override the
default behavior of invalid submits and you set sessionForm to true,
then you probably will not need to set this because your parameters
are only going to be needed on the first request. |
Thanks to Rainer Schmitz and Nick Lothian for their suggestions!
John - A. LewisJuergen - HoellerAlef - ArendsenRob - Harrop2.0 - | Fields inherited from org.springframework.web.portlet.mvc.BaseCommandController: |
|---|
| DEFAULT_COMMAND_NAME |
| Fields inherited from org.springframework.context.support.ApplicationObjectSupport: |
|---|
| logger |
| Constructor: |
|---|
Subclasses should set the following properties, either in the constructor
or via a BeanFactory: commandName, commandClass, bindOnNewForm, sessionForm.
Note that commandClass doesn't need to be set when overriding
"cacheSeconds" is by default set to 0 (-> no caching for all form controllers). |
| Methods from org.springframework.web.portlet.mvc.AbstractController: |
|---|
| handleActionRequest, handleActionRequestInternal, handleRenderRequest, handleRenderRequestInternal, isRenderWhenMinimized, isSynchronizeOnSession, setRenderWhenMinimized, setSynchronizeOnSession |
| Methods from org.springframework.web.portlet.handler.PortletContentGenerator: |
|---|
| applyCacheSeconds, cacheForSeconds, check, checkAndPrepare, checkAndPrepare, getCacheSeconds, isRequireSession, preventCaching, setCacheSeconds, setRequireSession |
| Methods from org.springframework.web.portlet.context.PortletApplicationObjectSupport: |
|---|
| getPortletContext, getTempDir, isContextRequired, setPortletContext |
| Methods from org.springframework.context.support.ApplicationObjectSupport: |
|---|
| getApplicationContext, getMessageSourceAccessor, initApplicationContext, initApplicationContext, isContextRequired, requiredContextClass, setApplicationContext |
| Methods from java.lang.Object: |
|---|
| equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
| Method from org.springframework.web.portlet.mvc.AbstractFormController Detail: |
|---|
The properties of the form object will correspond to the form field values in your form view. This object will be exposed in the model under the specified command name, to be accessed under that name in the view: for example, with a "spring:bind" tag. The default command name is "command". Note that you need to activate session form mode to reuse the form-backing object across the entire form workflow. Else, a new instance of the command class will be created for each submission attempt, just using this backing object as template for the initial form. Default implementation calls |
Calls |
showNewForm.
Can be used directly when intending to show a new form but with
special errors registered on it (for example, on invalid submit).
Usually, the resulting BindException will be passed to
|
Default is an internal name, of no relevance to applications, as the form session attribute is not usually accessed directly. Can be overridden to use an application-specific attribute name, which allows other code to access the session attribute directly. |
Default implementation delegates to the |
|
|
|
isFormSubmission,
always treating requests without existing form session attribute
as new form when using session form mode. |
Default implementation simply tries to resubmit the form with a new form object. This should also work if the user hit the back button, changed some form data, and resubmitted the form. Note: To avoid duplicate submissions, you need to override this method. Most likely you will simply want it to do nothing here in the action phase and diplay an appropriate error and a new form in the render phase.
protected void handleInvalidSubmit(ActionRequest request, ActionResponse response) throws Exception {
}
If you override this method but you do need a command object and bind errors in the render phase, be sure to call setRenderCommandAndErrors from here. |
isFormSubmission,
always treating requests without existing form session attribute
as new form when using session form mode. |
|
Default implementation checks to see if this is an ActionRequest and treats all action requests as form submission. During the action phase it will pass forward a render parameter to indicate to the render phase that this is a form submission. This method can check both kinds of requests and indicate if this is a form submission. Subclasses can override this to use a custom strategy, e.g. a specific request parameter (assumably a hidden field or submit button name). Make sure that the override can handle both ActionRequest and RenderRequest objects properly. |
|
|
|
onBindOnNewForm version
with all parameters, after standard binding when displaying the form view.
Only called if bindOnNewForm is set to true.
Default implementation is empty. |
bindOnNewForm is true.
Default implementation delegates to |
|
handleRequestInternal
in case of a form submission, with or without binding errors. Implementations
need to proceed properly, typically performing a submit action if there are no binding errors.
Subclasses can implement this to provide custom submission handling like triggering a custom action. They can also provide custom validation or proceed with the submission accordingly. |
Default implementation returns null. Subclasses can override this to set reference data used in the view. |
handleRequestInternal
in case of a form submission, with or without binding errors. Implementations
need to proceed properly, typically showing a form view in case of binding
errors or rendering the result of a submit action else.
For a success view, call |
Default implementation simply tries to resubmit the form with a new form object. This should also work if the user hit the back button, changed some form data, and resubmitted the form. Note: To avoid duplicate submissions, you need to override this method.
Either show some "invalid submit" message, or call
protected ModelAndView handleInvalidSubmit(RenderRequest request, RenderResponse response) throws Exception {
return showNewForm(request, response);
}
You can also show a new form but with special errors registered on it:
protected ModelAndView handleInvalidSubmit(RenderRequest request, RenderResponse response) throws Exception {
BindException errors = getErrorsForNewForm(request);
errors.reject("duplicateFormSubmission", "Duplicate form submission");
return showForm(request, response, errors);
}
WARNING: If you override this method, be sure to also override the action phase version of this method so that it will not attempt to perform the resubmit action by default. |
|
|
|
|
|
This is necessary for either wizard-style controllers that populate a single form object from multiple pages, or forms that populate a persistent object that needs to be identical to allow for tracking changes. |
A typical implementation will call
For building a custom ModelAndView, call Note: If you decide to have a "formView" property specifying the view name, consider using SimpleFormController. |
In session form mode: Re-puts the form object in the session when returning to the form, as it has been removed by getCommand. Can be used in subclasses to redirect back to a specific form page. |
In session form mode: Re-puts the form object in the session when returning to the form, as it has been removed by getCommand. Can be used in subclasses to redirect back to a specific form page. |
|