fishbone fishbone - 28 days ago 10
Java Question

JSR-303 / Spring MVC - validate conditionally using groups

I worked out a concept to conditionally validate using JSR 303 groups. "Conditionally" means that I have some fields which are only relevant if another field has a specific value.

Example: There is an option to select whether to register as a person or as a company. When selecting company, the user has to fill a field containing the name of the company.

Now I thought I use groups for that:

class RegisterForm
{
public interface BasicCheck {}
public interface UserCheck {}
public interface CompanyCheck {}

@NotNull(groups = BasicCheck.class)
private Boolean isCompany

@NotNull(groups = UserCheck.class)
private String firstName;

@NotNull(groups = UserCheck.class)
private String lastName;

@NotNull(groups = CompanyCheck.class)
private String companyName;

// getters / setters ...
}


In my controller, I validate step by step depending on the respective selection:

@Autowired
SmartValidator validator;

public void onRequest(@ModelAttribute("registerForm") RegisterForm registerForm, BindingResult result)
{
validator.validate(registerForm, result, RegisterForm.BasicCheck.class);
if (result.hasErrors()
return;
// basic check successful => we can process fields which are covered by this check
if (registerForm.getIsCompany())
{
validator.validate(registerForm, result, RegisterForm.CompanyCheck.class)
}
else
{
validator.validate(registerForm, result, RegisterForm.UserCheck.class);
}
if (!result.hasErrors())
{
// process registration
}
}


I only want to validate what must be validated. If the user selects "company" fills a field with invalid content and then switches back to "user", the invalid company related content must be ignored by the validator. A solution would be to clear those fields using Javascript, but I also want my forms to work with javascript disabled. This is why I totally like the approach shown above.

But Spring breaks this idea due to data binding. Before validation starts, Spring binds the data to registerForm. It adds error to
result
if, for instance, types are incompatible (expected int-value, but user filled the form with letters). This is a problem as these errors are shown in the JSP-view by
<form:errors />
tags

Now I found a way to prevent Spring from adding those errors to the binding result by implementing a custom
BindingErrorProcessor
. If a field contains
null
I know that there was a validation error. In my concept
null
is not allowed - every field gets annotated with
@NotNull
plus the respective validation group.

As I am new to Spring and JSR-303 I wonder, whether I am totally on the wrong path. The fact that I have to implement a couple of things on my own makes me uncertain. Is this a clean solution? Is there a better solution for the same problem, as I think this is a common problem?

EDIT

Please see my answer here if you are interested in my solution in detail: http://stackoverflow.com/a/30500985/395879

Answer

You are correct that Spring MVC is a bit picky in this regard,and it is a common problem. But there are work-arounds:

  • Make all your backing fields strings, and do number/date etc conversions and null checks manually.
  • Use JavaScript to set fields to null when they become irrelevant.
  • Use JavaScript to validate fields when they are entered. This will fix almost all of your problems.

Good luck!