Consider a command object that has a single integer property 'foo'. In at least Grails 2.3.x, you can pass multiple 'foo' parameters to an action using such a command object. Rather than a typeMismatch error, the first value will be used and the rest ignored. I'd prefer the result to be a "cardinality error", that would cause '.hasErrors' to be true, for example.
@Validateable
class FooParams {
Integer foo
}
class CardinalityController {
def index(FooParams foo) { /* foo.hasErrors() should be true when passed multiple 'foo' query parameters */ }
}
So, I'd like a GET request to '/cardinality?foo=1&foo=2' should result in some kind of validation error, e.g. a typeMismatch error (Integer != List<Integer>). Since custom validators in the 'constraints' static field only see a single value, I can't enforce it there. I tried using '@BindUsing', but I can't figure out how to add a validation error in a way that's checkable from the calling controller/view.
I'm aware that there are other ways to enforce such a constraint. For example, by checking params.<param>.list().size(). However, I want this "matching cardinality" behavior for all command objects, so I don't want to have to add interceptors or take some ad-hoc copy-paste approach.
I'm not very familiar with Spring, but it seems like one should be able to replace the default binding strategy to check for cases where there are multiple source parameter values whilst binding to a type that does not implement Collection (or some laundry list of sequence-like types). If it's not possible to replace the default behavior, I see that I could implement a class that can be used to annotated command objects, i.e. '@BindUsing(<class implementing BindingHelper>). However, I don't see how to add validation errors. Here's what I tried when annotating just a field and not implementing a whole class:
@Validateable
class FooParams {
@BindUsing({obj, src ->
def value = src['foo']
def size = value?.size()
if (1 < size) {
def errors = new ValidationErrors(obj)
def error = new FieldError('fooParams', 'foo', 'too many values')
errors.addError(error)
obj.errors.addAllErrors(errors)
}
src['foo']
})
Integer foo
}