Skip to content
Model centric tools for Spring development Community portal
Skyway PerspectivesModel driven delivery

Recipes

Recipe: Validation in a Spring Web Flow

Validation in a flow belongs to the transition, not to the view. This recipe shows how to attach a validator, route errors back to the form and keep the flow state clean.

A Spring Web Flow state machine with start, view, action and end states
Figure 2.33. A failed validation transition returns to the view state instead of moving forward.

Spring Web Flow describes a user journey as a set of states and transitions. Validation is a guard on a transition. When the guard fails, the flow stays in the current view and exposes the error messages. This is different from a controller that renders a form again, and it keeps the whole journey in one definition.

Attaching a validator

Add a validation element to the view state or to the transition that leaves it. The model records the validator type and the fields it checks. The recipe writes the validator class and the flow definition entry that references it.

  • Validate the form object, not the raw request parameters.
  • Keep field checks in the validator and cross field rules in a service method.
  • Return a structured result so that the view can place each message next to its field.

Routing errors

A validation transition has two outcomes. On success the flow moves to the next state. On failure it stays on the view and adds the error messages to the flow scope. Name the failure transition explicitly, for example invalid, so that the flow diagram is readable.

public void validate(Customer customer, MessageContext context) {
    if (customer.getEmail() == null || customer.getEmail().isEmpty()) {
        context.addMessage("customer.email.required");
    }
    if (customer.getName() == null || customer.getName().trim().isEmpty()) {
        context.addMessage("customer.name.required");
    }
}
Message keys. Use message keys rather than inline text. The generator writes keys for common checks and leaves the text to the resource bundle. That keeps the flow usable in more than one language.

When a flow grows past a handful of states, split it into subflows. A subflow can be validated on its own and reused by more than one journey, which keeps each definition small.