Matt Follett

Grails Custom Validation Messages

Grails’ GORM provides convenient ways to define many different constraints for inputing properties including basic ones like max, equal, & notEqual to more specific ones like creditCard, url, & email (full list here under Quick Reference). But sooner or later you’ll want to create your own custom validator. This can be done by providing a validator closure to your domain, for example:

class Groovyist {
String username
String password
String email
Long favoritePrime
static constraints = {
username blank: false, unique: true
password size: 5..15, blank: false
email email: true, blank: false
favoritePrime validator: { BigInteger(it).isProbablePrime(99) }
}

Now, in this example a domain will validate all of the fields, including the user’s favorite prime (to a degree of certainty). However, if the prime number certainly isn’t prime then the error message provided when validation fails (through validate or save) will be:

favoritePrime does not pass custom validation.

That probably isn’t a helpful message to eventually pass back to the user, they might guess what the mysterious “custom validation” is in this case, but certainly not in all others.

So what is the solution? You might try passing an error message back, but that won’t work; you’ll get the same message again. It turns out the proper approach is to return a key from your message.properties file. If the key exists in the proper localization file (e.g. French is messages_fr.properties) then it will use that string. A simple modification to the validator above will solve this:

favoritePrime validator { BigInteger(it).isProbablePrime(99) ?: 'my.localized.not.prime.message' }

Adding the line:

my.localized.not.prime.message=The number {2} is not prime.

Will cause Grails to generate a much more useful validation failed message. As you can probably guess the {2} will be replaced with the value inputed by the user.

You can include additional values to your message by returning an array. The first element should be a message key (e.g. ‘my.localized.not.prime.message’) and the subsequent entries will be interpolated into the message starting at index 3. So, if you returned ['my.localized.not.prime.message', 5] you could have the message: my.localized.not.prime.message=The number {2} is not prime, {3} is an example of a prime. And in this case if the user passed in 4 they would end up with the message:

The number 4 is not prime, 5 is an example of a prime.

Of course, if you aren’t using a prime number validator multiple times in your code it may seem silly to have a special key/value pair dedicated to primes. Another option is to use a key/value pair dedicated to that field. You can do this simply by using the code in the first example and addding a field to your message.properties file like:

Groovyist.favoritePrime.validator.invalid=The number {2} is not prime.

Much of this is documented in the validator docs.