문제

I want to add a custom validator to a field in order to enforce a conditional non-null constraint. Since this requirement is shared across multiple objects, I want to reuse existing global (meaning not object- or field-specific) message keys. In the below example, I want to use default.null.message and not something like objectA.fieldC.null.message.

The documentation for the Grails validator seems to say that one can signal an error case by returning a string value that represents a key from message.properties. The documentation indicates (emphasis mine):

The closure can return:

  • null or true (or no return value) to indicate that the value is valid
  • false to indicate an invalid value and use the default message code
  • a string to indicate the error code to append to the "classname.propertyName." string used to resolve the error message. If a field-specific message cannot be resolved, the error code itself will be resolved allowing for global error messages.
  • a list containing a string as above, and then any number of arguments following it, which are used as formatted message arguments in the grails-app/i18n/message.properties file. The mapping of the arguments is as follows: Parameters 0 to 2 are automatically mapped to 0: property name, 1: class name, 2: property value. Additional parameters are mapped starting at parameter 3.

I've also found a Grails bug closed back in 1.0 that seems to request pretty much exactly what I am trying to do.

Here's an example of what I am doing. As you can see, I got one approach to work, but it really bugs me that the seemingly cleaner first two options do not work.

class ObjectA {
    ObjectB fieldB
    ObjectC fieldC
    static constraints = {
        fieldC(nullable: true, validator: { value, object, errors ->
            if (object.fieldB != ObjectB.SOMETHING && !value) {
                // Doesn't work
                //return "default.null.message"
                
                // Doesn't work
                //return ["default.null.message"]

                // This works
                errors.rejectValue("defaultBrand", "default.null.message", ["defaultBrand"] as Object[], "The field \"{0}\" cannot be null")
            }
        })
    }
}

Here's the entry in my message.properties:

default.null.message=The field "{0}" cannot be null

I've seen a couple questions on here related to Grails custom field validators with custom error messages, but nothing seems to address my specific situation.

도움이 되었습니까?

해결책

When using the three parameter (value, object, errors) version of the validate closure, the underlying code assumes that all errors will be added to the errors object. In order to returning a string or list of strings representing a message key.

The behavior is the same if using Command Objects as well.

Here's a working example of the two-parameter validate closure:

class ObjectA {
    ObjectB fieldB
    ObjectC fieldC
    static constraints = {
        fieldC(nullable: true, validator: { value, object->
            if (object.fieldB != ObjectB.SOMETHING && !value) {
                return "default.null.message"
                // You can also use the list style return (especially if you need to pass additional parameters used by the message)
                //return ["default.null.message"]
            }
        })
    }
}

And here is a working example of the three-parameter validate closure:

class ObjectA {
    ObjectB fieldB
    ObjectC fieldC
    static constraints = {
        fieldC(nullable: true, validator: { value, object, errors ->
            if (object.fieldB != ObjectB.SOMETHING && !value) {
                // When "errors" is passed into the closure, you must use it to signal error cases
                errors.rejectValue("defaultBrand", "default.null.message", ["defaultBrand"] as Object[], "The field \"{0}\" cannot be null")
            }
        })
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top