Search code examples
grailsgroovy

Grails rejectvalue error and domain error validation


I'm trying to do some serverside validation in grails and pass my errors back to the frontend as json to be processed by angularjs.

Error conditions

Department - required 
Department - unique
Description - foobar not allowed

I have the following code.

Controller

def saveDepartment() {
    def errors = []
    def success = true      
    def department

    try{
        department = departmentService.save(request.JSON);  

        if(department.hasErrors()) {
            success = false
            errors = department.errors.fieldErrors;         
        }           
    } catch(Exception e){
        e.printStackTrace()
        errors = "Unknown"
        success = false

        if(log.errorEnabled){
            log.error("save department encountered unknown error: ", e)
        }

        response.status = 500
    } finally {
        respond ([success:success, errors:errors, department:department]) as JSON;
    }
}

Service

def save(jsonObj) {
    def dept = new Department();
    dept.setName(jsonObj.name);     
    dept.setDescription(jsonObj.description);

    if(dept.description.equals('foobar')) {
        dept.errors.rejectValue('description', 'foobar', 'Foobar is not allowed')
    }

    if (!dept.save()) {
        dept.discard();
    }

    return dept;
}

Service Method Attempt 2 with debugging code

def save(jsonObj) {
    def dept = new Department();
    dept.setName(jsonObj.name);
    dept.setDescription(jsonObj.description);

    if(dept.description.equals('foobar')) {
        println 'rejected value '
        dept.errors.rejectValue('description', 'foobar', 'Foobar is not allowed')
    }

    println 'dept errors ' + dept.errors.allErrors.size();

    if (dept.errors.hasErrors()) {
        dept.errors.allErrors.each {FieldError error ->
            println error
        }
    }

    if (!dept.save(true)) {
        println 'dept errors 2 ' + dept.errors.allErrors.size();

        if (dept.errors.hasErrors()) {
            dept.errors.allErrors.each {FieldError error ->
                println error
            }
        }
    }

    return dept;
}

Output

..................rejected value 
dept errors 1
Field error in object 'org.hri.leaverequest.Department' on field 'description': rejected value [foobar]; codes [foobar.org.hri.leaverequest.Department.descripti
on,foobar.description,foobar.java.lang.String,foobar]; arguments []; default message [Foobar is not allowed]
dept errors 2 1
Field error in object 'org.hri.leaverequest.Department' on field 'name': rejected value [null]; codes [org.hri.leaverequest.Department.name.nullable.error.org.h
ri.leaverequest.Department.name,org.hri.leaverequest.Department.name.nullable.error.name,org.hri.leaverequest.Department.name.nullable.error.java.lang.String,or
g.hri.leaverequest.Department.name.nullable.error,department.name.nullable.error.org.hri.leaverequest.Department.name,department.name.nullable.error.name,depart
ment.name.nullable.error.java.lang.String,department.name.nullable.error,org.hri.leaverequest.Department.name.nullable.org.hri.leaverequest.Department.name,org.
hri.leaverequest.Department.name.nullable.name,org.hri.leaverequest.Department.name.nullable.java.lang.String,org.hri.leaverequest.Department.name.nullable,depa
rtment.name.nullable.org.hri.leaverequest.Department.name,department.name.nullable.name,department.name.nullable.java.lang.String,department.name.nullable,nulla
ble.org.hri.leaverequest.Department.name,nullable.name,nullable.java.lang.String,nullable]; arguments [name,class org.hri.leaverequest.Department]; default mess
age [Property [{0}] of class [{1}] cannot be null]

Issues

If department is null and description has foobar with rejectValue, only one error, "department null" is returned, foobar does not appear in the errors.

If department contains existing value and description contains foobar, the unique constraint is returned but foobar does not appear in the errors.

If department has a good value and foobar still exist, the rejectValue doesn't prevent the save from happening and no errors are thrown. Now if I output dept.errors after the rejectValue, I can see the error actually exist.

Goal

My goal is to return all my errors and not save to the db if an error exist, what am I missing to achieve that goal?


Solution

  • You can do it this way:

    dept.validate()
    
    if(dept.description.equals('foobar')) {
        dept.errors.rejectValue('description', 'foobar', 'Foobar is not allowed')
    }
    
    if(!dept.errors.hasErrors()) {
        dept.save()
    }
    
    return dept