Spring Web Flow – displaying your JSR-303 validation messages using FreeMarker

Instead of creating a custom validator in the traditional sense I’m relying on JSR-303 annotations to specify validation constraints, because, it is the future, and I shouldn’t have to do a load of coding to specify common validation requirements like an email address regular expression or a numeric value that is required to be within a defined range.

Using Spring web flow, I found that binding error messages weren’t available when I called “<@spring.showErrors ‘<br/>’/>” in my freemarker view (after I had bound the field I wanted to show errors for of course).

So where are my error messages?

It turns out that Spring Web Flow has a different way of providing the user with feedback messages. The Spring Web Flow reference documentation says: “Spring Web Flow’s MessageContext is an API for recording messages during the course of flow executions”.

The message context (with all your binding error messages in it) can be found here: flowRequestContext.messageContext. I’ve written a couple of macros to make retrieving error messages from this object a little easier:

<#--
 * Shows flow messages (which reside in flowRequestContext.messageContext)
 *
 * @param source Name of the field that caused the error
 * @param severity String representation of org.springframework.binding.message.Severity
 * @param separator the html tag or other character list that should be used to
 *    separate each option. Typically '<br>'.
 * @param classOrStyle either the name of a CSS class element (which is defined in
 *    the template or an external CSS file) or an inline style. If the value passed in here
 *    contains a colon (:) then a 'style=' attribute will be used, else a 'class=' attribute
 *    will be used.
 * @param tag The HTML tag to wrap the error in
-->
<#macro showFlowMessages source severity separator classOrStyle="" tag="">
  <#assign messages = flowRequestContext.messageContext.getMessagesBySource(source)/>
  <#if (messages?size > 0)>
    <#list messages as message>
      <#if message.severity?string == severity>
        <#if classOrStyle == "" && tag == "">
          ${message.getText()}
        <#else>
          <#if classOrStyle == "">
            <${tag}>${message.getText()}</${tag}>
          <#else>
            <#if tag == ""><#local tag = "span" /></#if>
            <#if classOrStyle?index_of(":") == -1><#local attr="class"><#else><#local attr="style"></#if>
            <${tag} ${attr}="${classOrStyle}">${message.getText()}</${tag}>
          </#if>
        </#if>
        <#if message_has_next>${separator}</#if>
      </#if>
    </#list>
  </#if>
</#macro>

<#--
 * Shows flow messages (which reside in flowRequestContext.messageContext) in an ordered or unordered list
 *
 * @param source Name of the field that caused the error
 * @param severity String representation of org.springframework.binding.message.Severity
 * @param classOrStyle either the name of a CSS class element (which is defined in
 *    the template or an external CSS file) or an inline style. If the value passed in here
 *    contains a colon (:) then a 'style=' attribute will be used, else a 'class=' attribute
 *    will be used.
 * @param ordered Whether or not the macro should output the list as an <ol> or <ul>
-->
<#macro showFlowMessagesList source severity classOrStyle="" ordered=false>
  <#local errorsList><@showFlowMessages source, severity, "", "", "li" /></#local>
  <#if errorsList?trim != "">
    <#if classOrStyle == "">
      <#local attr="">
    <#elseif classOrStyle?index_of(":") == -1>
      <#local attr=" class=" + classOrStyle>
    <#else>
      <#local attr=" style=" + classOrStyle>
    </#if>
    <#if ordered><ol${attr}><#else><ul${attr}></#if>
    ${errorsList}
    <#if ordered></ol><#else></ul></#if>
  </#if>
</#macro>

I’ve added these to my spring extensions, which you can download here: springx.ftl

How to use CakePHP Session data in your Model validate rules

CakePHP models don’t allow you to access the session object. It is argued that this isn’t the MVC way. I tend to agree. Your controller should really pass any needed session data to the model…somehow.

My use case is that my model needs to validate that when a new user is created, it is given a “group” with equal or lesser privileges than the creator’s group.

Whilst they wouldn’t be able to use the create user form to do this (as the form only lists groups they can assign), we still need to guard against it on the server as it would be trivial to submit a specially crafted POST request with a group ID of a group with higher privileges.

My model needed to access the logged in user’s group ID from the Auth component (which stores its data in the session). So that when my model data was validated, I could check for a valid group ID, based on the group ID of the current user.

I knew I needed to make use of custom validation methods, but how could I get hold of the current user’s group ID? The Auth component isn’t available in the Model, so, what could I do?

Luckily, CakePHP’s AppModel defines a function called “validates”. Which allows you to check whether a model’s data is valid, but crucially it allows you to pass custom options to be made available in the “beforeValidate” callback.

So instead of simply saving my model data to the database, I call this function, passing my current user’s group ID and THEN call save (passing it a parameter to tell it not to bother validating the data again).

In my model, I overrode the “beforeValidate” callback function, which defines (on the fly) a new validation rule: a custom validation method, that takes the current user’s group ID as a parameter. My custom validation method can then check the model data against the current user’s group ID to see if it is valid. WIN.

Below I’ve included some example code to illustrate how this works. In this example I pass the whole Session object to the custom validation function. Which is slightly overkill. I think you should really just pass the data you need. Create a model that looks something like:

class MyModel extends AppModel {

    var $validate = array(
        // Your usual validation rules for all fields
    );

    function beforeValidate($options) {
        
        if(!empty($options) && isset($options['Session'])) {
            
            $this->validate['fieldName'] = array(
                'ruleName' = array(
                    'rule' => array('validationFunction', $options['Session'])
                )
                // + your default validation rule(s) for this field
            );
            
        } else {
            
            // Either...
            unset($this->validate['fieldName']);
            // If you have no normal rules for this field
            // Or...
            //$this->validate['fieldName'] = array(
                // Your default validation rule(s) for this field
            //);
        }
    }

    function validationFunction($check, $session) {
        
        // Do your checks with session data
        return true; // or false
    }
}

You use this in your controller like so:

class MyController extends AppController {

    var $uses = array('MyModel');
    var $components = array('Session');

    function action() {
		
        if(!empty($this->data)) {

            $this->MyModel->create($this->data['MyModel']);

            // Validate, passing custom options to be made available in the beforeValidate callback
            if($this->MyModel->validates(array('Session' => $this->Session)) === true) {

                // Save, without validation
                $this->MyModel->save(null, false);
            }
        }
    }
}