Handling Form Data and File Uploads

This section on the handling of user input comes at a rather early point in this User's Guide, since the following sample applications require the user to interact with the application through forms (as it is the nature with most web applications). Knowing how to access such information is therefore essential for understanding these latter examples.

As has already been mentioned earlier, all submitted form (and cookie) data are available as properties of the object 'req.data'. Submitted information always comes in the form of a key/value pair with the key-identifier being the name of the input field. The submitted string of a text input field with the name 'surname', will therefore be accessible through req.data.surname, resp. req.data["surname"]. The method req.get("someKey") can also be used to access that value, but is considered deprecated.

Put the following code into an application directory named 'formdemo', and start up that application by adding an according line to [HelmaHome]/apps.properties.

### Root/forms.js ### 
 
function getForm_action() { 
   if (req.data.submit01 || req.data.submit02) { 
      res.write("F01: " + req.data.field01 + "<br>"); 
      res.write("F02: " + req.data.field02 + "<br>"); 
      res.write("F03: " + req.data.field03 + "<br>"); 
      res.write("F04: " + req.data.field04 + "<br>"); 
      res.write("F05: " + req.data["field05_array"].join("/") + "<br>"); 
      res.write("S01: " + req.data.submit01 + "<br>"); 
      res.write("S02: " + req.data.submit02 + "<br>"); 
   } 
   this.renderSkin("getForm"); 
} 
 
function postForm_action() { 
   if (req.data.submit) { 
      res.write("F01: " + encode(req.data.field01) + "<br>"); 
   } 
   this.renderSkin("postForm"); 
} 
 
function uploadForm_action() { 
   if (req.data.submit) { 
      res.write("F01: " + req.data.field01 + "<br>"); 
      var mimepart = req.data.field01; 
      res.write("Name: " + mimepart.getName() + "<br />"); 
      res.write("Size: " + mimepart.getContentLength() + "<br />"); 
      res.write("Type: " + mimepart.getContentType() + "<br />"); 
   } 
   this.renderSkin("uploadForm"); 
} 
 
 
### Root/getForm.skin ### 
 
<form method="GET" action="/formdemo/getForm"> 
  <input type="text" name="field01"> 
  <input type="hidden" name="field02" value="a hidden value"> 
  <br /> 
  <select name="field03"> 
    <option value="1">1</option> 
    <option value="2">2</option> 
  </select> 
  <br /> 
  <input type="radio" name="field04" value="a">a  
  <input type="radio" name="field04" value="b">b 
  <br /> 
  <select name="field05" size="3" multiple> 
    <option value="x">x</option> 
    <option value="y">y</option> 
    <option value="z">z</option> 
  </select> 
  <br /> 
  <input type="submit" name="submit01" value="Go GET it! (01)"> 
  <input type="submit" name="submit02" value="Go GET it! (02)"> 
</form> 
 
### Root/postForm.skin ### 
 
<form method="POST" action="/formdemo/postForm"> 
  <textarea name="field01"></textarea> 
  <input type="submit" name="submit" value="Go POST it!"> 
</form> 
 
### Root/uploadForm.skin ### 
 
<form method="POST" action="/formdemo/uploadForm" enctype="multipart/form-data"> 
  <input type="file" name="field01" accept="image/*"> 
  <input type="submit" name="submit" value="Go UPLOAD it!"> 
</form>

The code defines three (Root-)actions, and accordingly three (Root-)skins, which are being rendered from within these actions. A request to http://localhost:8080/formdemo/getForm will render a simple form with text input fields, radio buttons, dropdowns and two submit-buttons. In Root/getForm.skin we specified the used method to be 'GET', and defined that same action as the action (i.e. the target) of that form. If the user clicks on one of the submit buttons, the browser will send the filled out form back to the getForm-action, which now renders each of the submitted values at the top of the page. If you take a look at the address bar of your browser, you will see that your browser actually submitted the information to the server by appending key/value-pairs at the end of the URL.

Generally it is common practice to use the POST-method instead of GET to submit data. In case that you have a textarea (which allows multi-line text strings to be inserted), or want to upload files, you actually must use the POST-form. The browser will then send the submitted information not as part of the URL, but appends it to the request body. Nevertheless the req.data-object provides a transparent way to access that information, no matter what method has been used. See http://localhost:8080/formdemo/postForm for an example. After submitting the form, you will remain on the same action, but the submitted text will now be displayed at the top of the page. Try hitting the refresh-button in your browser, and you will be prompted whether the browser should send the previously submitted information again to the server. Therefore it is common practice to perform a redirect, after a form has been processed, so that a browser loses its memory and that a unintentional re-submission of data is avoided. Note that the only reason why we call the global method 'encode' in our example.is being called on the submitted string is to transform the linebreaks of the textarea into actual HTML linebreaks, i.e. br-tags.

http://localhost:8080/formdemo/uploadForm demonstrates how to upload a file to the application. Firstly, you need to use the POST-method, secondly you need to set the enctype-attribute of the opening form-tag to "multipart/form-data", and thirdly you need to include (at least) one form-field of type "file". That way, the browser knows how to correctly submit binary data to the server. The according property in req.data now does not contain a string, but a mimepart-object, which can be further processed (usually written to disk). See the documentation on the mimepart-object for a full list of available methods.

As a final note on HTML forms two special cases should be pointed out. Firstly, the form type "checkbox" does not send any data at all, if it is not checked. Therefore it is not possible to detect whether a checkbox has not been checked, or whether it has not been present in the form at all. A way out of this dilemma is to always include a second (hidden) field input, that indicates the presence of that particular checkbox in the form. Secondly, a submitted multi-select input form will contain several key/value-pairs with identical keys. Accessing the according property in req.data will only return of the submitted values, but not all of them. Helma provides an additional req.data-property appended with the key-name being appended with '_array', that returns an array of all selected options. See the getForm-example above.