class User {
String login
String password
String email
Integer age
static constraints = {
...
}
}
12 Validation
Version: 6.2.3-SNAPSHOT
Table of Contents
12 Validation
Grails validation capability is built on Spring’s Validator API and data binding capabilities. However Grails takes this further and provides a unified way to define validation "constraints" with its constraints mechanism.
Constraints in Grails are a way to declaratively specify validation rules. Most commonly they are applied to domain classes, however URL Mappings and Command Objects also support constraints.
12.1 Declaring Constraints
Within a domain class constraints are defined with the constraints property that is assigned a code block:
You then use method calls that match the property name for which the constraint applies in combination with named parameters to specify constraints:
class User {
...
static constraints = {
login size: 5..15, blank: false, unique: true
password size: 5..15, blank: false
email email: true, blank: false
age min: 18
}
}
In this example we’ve declared that the login
property must be between 5 and 15 characters long, it cannot be blank and must be unique. We’ve also applied other constraints to the password
, email
and age
properties.
By default, all domain class properties are not nullable (i.e. they have an implicit nullable: false constraint).
|
A complete reference for the available constraints can be found in the Quick Reference section under the Constraints heading.
Note that constraints are only evaluated once which may be relevant for a constraint that relies on a value like an instance of java.util.Date
.
class User {
...
static constraints = {
// this Date object is created when the constraints are evaluated, not
// each time an instance of the User class is validated.
birthDate max: new Date()
}
}
A word of warning - referencing domain class properties from constraints
It’s very easy to attempt to reference instance variables from the static constraints block, but this isn’t legal in Groovy (or Java). If you do so, you will get a MissingPropertyException
for your trouble. For example, you may try
class Response {
Survey survey
Answer answer
static constraints = {
survey blank: false
answer blank: false, inList: survey.answers
}
}
See how the inList
constraint references the instance property survey
? That won’t work. Instead, use a custom validator:
class Response {
...
static constraints = {
survey blank: false
answer blank: false, validator: { val, obj -> val in obj.survey.answers }
}
}
In this example, the obj
argument to the custom validator is the domain instance that is being validated, so we can access its survey
property and return a boolean to indicate whether the new value for the answer
property, val
, is valid.
12.2 Validating Constraints
Validation Basics
Call the validate method to validate a domain class instance:
def user = new User(params)
if (user.validate()) {
// do something with user
}
else {
user.errors.allErrors.each {
println it
}
}
The errors
property on domain classes is an instance of the Spring Errors interface. The Errors
interface provides methods to navigate the validation errors and also retrieve the original values.
Validation Phases
Within Grails there are two phases of validation, the first one being data binding which occurs when you bind request parameters onto an instance such as:
def user = new User(params)
At this point you may already have errors in the errors
property due to type conversion (such as converting Strings to Dates). You can check these and obtain the original input value using the Errors
API:
if (user.hasErrors()) {
if (user.errors.hasFieldErrors("login")) {
println user.errors.getFieldError("login").rejectedValue
}
}
The second phase of validation happens when you call validate or save. This is when Grails will validate the bound values against the constraints you defined. For example, by default the save method calls validate
before executing, allowing you to write code like:
if (user.save()) {
return user
}
else {
user.errors.allErrors.each {
println it
}
}
12.3 Sharing Constraints Between Classes
A common pattern in Grails is to use Command Objects for validating user-submitted data and then copy the properties of the command object to the relevant domain classes. This often means that your command objects and domain classes share properties and their constraints. You could manually copy and paste the constraints between the two, but that’s a very error-prone approach. Instead, make use of Grails' global constraints and import mechanism.
Global Constraints
In addition to defining constraints in domain classes, command objects and other validateable classes, you can also define them in grails-app/conf/runtime.groovy
:
grails.gorm.default.constraints = {
'*'(nullable: true, size: 1..20)
myShared(nullable: false, blank: false)
}
These constraints are not attached to any particular classes, but they can be easily referenced from any validateable class:
class User {
...
static constraints = {
login shared: "myShared"
}
}
Note the use of the shared
argument, whose value is the name of one of the constraints defined in grails.gorm.default.constraints
. Despite the name of the configuration setting, you can reference these shared constraints from any validateable class, such as command objects.
The '*' constraint is a special case: it means that the associated constraints ('nullable' and 'size' in the above example) will be applied to all properties in all validateable classes. These defaults can be overridden by the constraints declared in a validateable class.
Importing Constraints
Grails 2 introduced an alternative approach to sharing constraints that allows you to import a set of constraints from one class into another.
Let’s say you have a domain class like so:
class User {
String firstName
String lastName
String passwordHash
static constraints = {
firstName blank: false, nullable: false
lastName blank: false, nullable: false
passwordHash blank: false, nullable: false
}
}
You then want to create a command object, UserCommand
, that shares some of the properties of the domain class and the corresponding constraints. You do this with the importFrom()
method:
class UserCommand {
String firstName
String lastName
String password
String confirmPassword
static constraints = {
importFrom User
password blank: false, nullable: false
confirmPassword blank: false, nullable: false
}
}
This will import all the constraints from the User
domain class and apply them to UserCommand
. The import will ignore any constraints in the source class (User
) that don’t have corresponding properties in the importing class (UserCommand
). In the above example, only the 'firstName' and 'lastName' constraints will be imported into UserCommand
because those are the only properties shared by the two classes.
If you want more control over which constraints are imported, use the include
and exclude
arguments. Both of these accept a list of simple or regular expression strings that are matched against the property names in the source constraints. So for example, if you only wanted to import the 'lastName' constraint you would use:
...
static constraints = {
importFrom User, include: ["lastName"]
...
}
or if you wanted all constraints that ended with 'Name':
...
static constraints = {
importFrom User, include: [/.*Name/]
...
}
Of course, exclude
does the reverse, specifying which constraints should not be imported.
12.4 Validation on the Client
Displaying Errors
Typically if you get a validation error you redirect back to the view for rendering. Once there you need some way of displaying errors. Grails supports a rich set of tags for dealing with errors. To render the errors as a list you can use renderErrors:
<g:renderErrors bean="${user}" />
<g:hasErrors bean="${user}">
<ul>
<g:eachError var="err" bean="${user}">
<li>${err}</li>
</g:eachError>
</ul>
</g:hasErrors>
Highlighting Errors
It is often useful to highlight using a red box or some indicator when a field has been incorrectly input. This can also be done with the hasErrors by invoking it as a method. For example:
<div class='value ${hasErrors(bean:user,field:'login','errors')}'>
<input type="text" name="login" value="${fieldValue(bean:user,field:'login')}"/>
</div>
This code checks if the login
field of the user
bean has any errors and if so it adds an errors
CSS class to the div
, allowing you to use CSS rules to highlight the div
.
Retrieving Input Values
Each error is actually an instance of the FieldError class in Spring, which retains the original input value within it. This is useful as you can use the error object to restore the value input by the user using the fieldValue tag:
<input type="text" name="login" value="${fieldValue(bean:user,field:'login')}"/>
This code will check for an existing FieldError
in the User
bean and if there is obtain the originally input value for the login
field.
12.5 Validation and Internationalization
Another important thing to note about errors in Grails is that error messages are not hard coded anywhere. The FieldError class in Spring resolves messages from message bundles using Grails' i18n support.
Constraints and Message Codes
The codes themselves are dictated by a convention. For example consider the constraints we looked at earlier:
package com.mycompany.myapp
class User {
...
static constraints = {
login size: 5..15, blank: false, unique: true
password size: 5..15, blank: false
email email: true, blank: false
age min: 18
}
}
If a constraint is violated, Grails looks by convention for a message code:
Constraint | Error Code |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
In the case of the blank
constraint this would be user.login.blank
so you would need a message such as the following in your grails-app/i18n/messages.properties
file:
user.login.blank=Your login name must be specified!
The class name is looked for both with and without a package, with the packaged version taking precedence. So for example, com.mycompany.myapp.User.login.blank
will be used before user.login.blank
. This allows for cases where your domain class message codes clash with a plugin’s.
For a reference on what codes are for which constraints refer to the reference guide for each constraint (e.g. blank).
Displaying Messages
The renderErrors tag will automatically look up messages for you using the message tag. If you need more control of rendering you can handle this yourself:
<g:hasErrors bean="${user}">
<ul>
<g:eachError var="err" bean="${user}">
<li><g:message error="${err}" /></li>
</g:eachError>
</ul>
</g:hasErrors>
12.6 Applying Validation to Other Classes
Domain classes and Command Objects support validation by default. Other classes may be made validateable by defining the static constraints
property in the class (as described above) and then telling the framework about them. It is important that the application register the validateable classes with the framework. Simply defining the constraints
property is not sufficient.
The Validateable Trait
Classes which define the static constraints
property and implement the Validateable trait will be validateable. Consider this example:
package com.mycompany.myapp
import grails.validation.Validateable
class User implements Validateable {
...
static constraints = {
login size: 5..15, blank: false, unique: true
password size: 5..15, blank: false
email email: true, blank: false
age min: 18
}
}
Programmatic access
Accessing the constraints on a validateable object is slightly different. You can access a command object’s constraints programmatically in another context by accessing the constraintsMap
static property of the class. That property is an instance of Map<String, ConstrainedProperty>
In the example above, accessing User.constraintsMap.login.blank
would yield false
, while
User.constraintsMap.login.unique
would yield true
.