Grails[1] is an awesome full stack, web application framework on JVM. It has good documentation but not excellent documentation. I am working on a business project where I am using Grails REST services with MongoDB as the backend. I will cover that setup in another blog post but this one is for providing information about setting up validation in Grails with custom validation messages.
Some background about Domain objects in Grails:
Often we have requirements where certain fields in the request need to have certain values. In other words, we need to validate the data in the incoming request. Grails provides constraints
[1:1] on domain and validatable
[1:2] objects. Below is an example.
package com.javawithravi.bookstore
class Book {
String title
String description
static constraints = {
title nullable: false, blank: false, size: 10..50
description nullable: false, blank: false, size: 100..500
}
}
For example: #9 above states that title
cannot be:
- empty
- null or
- less than 10 chars or more than 50 chars in length.
One line and you are done with constraints on title
property. No need to write custom validation logic (not that you can't write custom constraint in Grails but you are provided with some common constraints out of the box).
Default messages for constraint violation:
Now, Grails comes with default messages.properties
that provides default validation message(s) for each of the in-built constraints. A few examples:
default.invalid.min.message=Property [{0}] of class [{1}] with value [{2}] is less than minimum value [{3}]
default.invalid.max.size.message=Property [{0}] of class [{1}] with value [{2}] exceeds the maximum size of [{3}]
default.invalid.min.size.message=Property [{0}] of class [{1}] with value [{2}] is less than the minimum size of [{3}]
So if you provide a title
less than 10 chars long then you will get a message like:
Property [title] of class [com.javawithravi.bookstore.Book] with value [Yo] is less than the minimum size of [10]
Custom constraint message:
Pretty decent. But not very user friendly. So here's the good news. You can provide your own custom message for each domain class's constraint(s). To quote Grails doc:
If a constraint is violated Grails will by convention look for a message code of the form:
[Class Name].[Property Name].[Constraint Code]
Following the instructions above we can add lines below to messages.properties
:
book.title.size.toosmall=Title should be less than [{3}] chars.
book.title.size.toobig=Title cannot be greater than [{4}] chars.
Okay, so the class name and property name is pretty intuitive. But what's not intuitive and isn't immediately clear and does not have easily accessible documentation is: what constraint code pertains to which constraint
? Here's what you need to do: figure out the constraint you are using, go to that constraint's documentation [2] and get the constraint code from the section stating Error code
. For example:
Name | Constraint code | Documentation | Example |
---|---|---|---|
Min Size | minSize.notmet | http://grails.org/doc/latest/ref/Constraints/minSize.html | children minSize: 25 |
Size | size.toosmallsize.toosmall or size.toosmallsize.toobig | http://grails.org/doc/latest/ref/Constraints/size.html | book size: 5..15 |
NOTE: You have to use camel case for class and property name. For instance:
blogPost.postTitle.size.toosmall=Yo, your blog post too small yo! ;-)
Sending custom constraint validation messages back to the user:
Once you validate a domain class (or a Validateable
object), you can then access the constraint errors with object.errors
for example: book.errors
. And to access it in your controller you will do:
def jsonRequest = JSON.parse(request.JSON.toString())
def book = new Book(jsonRequest)
book.validate()
if (book.hasErrors()) {
response.status = 422
def errors = book.errors.allErrors.collect {
message(error: it)
}
respond errors
}
The crucial lines are 6-8. Where you are iterating through all the errors on the book
object and creating a list of messages using VaidationTagLib.message
closure which is getting an instance of FieldError
class. I had to scrounge through various blog sites [1:3] [1:4] because I could not find documentation about how to access the custom message that was fully initiated!
Does this make sense? Did you find using custom constraints in Grails challenging? Thoughts? Feedback? NOTE: This is tested with Grails 2.4.4.