Forms and the HST ActionURL
Introduction
Bloomreach Experience Manager's delivery tier supports the Post-Redirect-Get pattern for form submissions. This pattern describes the following three phases:
- The form is submitted to an action URL using the POST method.
- The form component's doAction method is invoked to process form data.
- The server responds to the form's POST request with a redirect to the form's original URL using the GET method.
The following sections explain in more detail how to use the pattern in your form components.
Form Submission
To use the PRG pattern a form must be submitted using the POST method to a special action URL generated by using the hst:actionURL tag.
In a JSP template:
<hst:actionURL var="actionLink" /> <form action="${actionLink}" method="post"> <!-- form fields here --> </form>
In a Freemarker template:
<@hst.actionURL var="actionLink"/> <form action="${actionLink}" method="post"> <!-- form fields here --> </form>
The action URL is unique to the component that renders the form, ensuring that the POST request will invoke the doAction method of said component. An action url looks something like:
/news?_hn:type=action&_hn:ref=r34_r1_r1
The above implies an action request to the pathInfo /news and then on the page that belongs to /news, invoke the doAction method of the java class belonging to component with reference r34_r1_r1.
Method must be POST
A <form> using a generated action URL should always use the POST method:
<form action="${actionLink}" method="post">
In Bloomreach Experience Manager 14, an (incorrect) GET request for the generated action URL will still trigger the doAction method, which in general is undesirable. As of Bloomreach Experience Manager 14.1.0, you can prevent this from happining by adding the following to your HST properties:
container.actionValve.method.post.only = true
This will cause any GET request at an action URL to be rejected with HTTP status 405 "Method Not Allowed", which is the recommended behavior.
As of Bloomreach Experience Manager 15.0, rejecting GET requests at action URLs is the default behavior.
Data Processing
To process submitted form data, the component that renders the form must override BaseHstComponent#doAction(HstRequest request, HstResponse response).
Form data is stored in a org.hippoecm.hst.component.support.forms.FormMap, which can also be used to access the data. Useful static methods are provided by org.hippoecm.hst.component.support.forms.FormUtils.
To access form data, create a new FormMap object and provide a list of form field names to the constructor:
FormMap map = new FormMap(request, new String[]{"inputFieldName"});
A FormMap contains FormField objects which in turn contain the field values. Validation error messages can be added to a FormMap and its FormField objects.
To persist the form data (including any messages that were added) so they are still available to the component after the redirect, use FormUtils#persistFormMap(HstRequest, HstResponse, FormMap, StoreFormResult):
FormUtils.persistFormMap(request, response, map, null);
Form data should only be persisted in doAction.
Once doAction is completed the request will be redirected to the original URL of the page containing the form, and the form component's doBeforeRender method will be invoked as for any normal GET request.
Rendering
To retrieve persisted form data in a form component's doBeforeRender method, a FormMap object can be populated using the static method FormUtils#populate(HstRequest, FormMap):
FormMap map = new FormMap(); FormUtils.populate(request, map);
The form data (including any messages) can then be passed to the templating engine to, for example, render error messages, prepopulate the form, list all submitted values for confirmation, and so on.
Sealing Form Data
Once the form data are no longer needed, they should be 'sealed' so they cannot be read anymore:
map.setSealed(true);
Form Data Storage
Form data is stored in the content repository in the folder /formdata. Different customers have different needs for the retention of this data, so this data is not cleaned up automatically. Each FormData element stores a timestamp, so this data can be cleaned up using the provided Form Data Cleanup Repository Job.