Reputation: 49629
I'm trying to create a custom marshaller for org.springframework.validation.FieldError so I can avoid putting extraneous and possibly sensitive data in my JSON response, which includes MyCommandObject.errors
.
However my FieldError marshaller isn't working at all, this is what I have in my BootStrap.groovy under the init method.
def fieldErrorMarshaller = {
log.info("field error marshaller $it")
def returnArray = [:]
returnArray['field'] = it.field
returnArray['message'] = it.message
return returnArray
}
JSON.registerObjectMarshaller(FieldError, fieldErrorMarshaller)
I am not seeing any explicit errors or the marshaller logging. Any idea what might be going wrong here?
Upvotes: 4
Views: 1466
Reputation: 1
//usage in grails controller
render domainObject.errors as JSON
//resources.groovy
jsonErrorsMarshaller(MyErrorsMarshaller)
errorsJsonMarshallerRegisterer(ObjectMarshallerRegisterer) {
marshaller = { MyErrorsMarshaller om -> }
converterClass = grails.converters.JSON
}
//grails.converters.JSON marshaller class
import grails.converters.JSON
import grails.validation.ValidationErrors
import org.codehaus.groovy.grails.web.converters.exceptions.ConverterException
import org.codehaus.groovy.grails.web.converters.marshaller.ObjectMarshaller
import org.codehaus.groovy.grails.web.json.JSONWriter
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
import org.springframework.context.i18n.LocaleContextHolder
import org.springframework.validation.Errors
import org.springframework.validation.FieldError
import org.springframework.validation.ObjectError
class MyErrorsMarshaller implements ObjectMarshaller<JSON>, ApplicationContextAware {
ApplicationContext applicationContext;
public boolean supports(Object object) {
return object instanceof Errors;
}
public void marshalObject(Object object, JSON json) throws ConverterException {
JSONWriter writer = json.getWriter();
try {
List<ObjectError> errors = ((ValidationErrors)object).getAllErrors()
writer.object();
writer.key("errors").array();
Locale locale = LocaleContextHolder.getLocale();
for(FieldError error : errors){
FieldError fe = (FieldError) error;
writer.object();
writer.key("field").value(fe.getField()).key("message").value(applicationContext.getMessage(fe, locale));
writer.endObject();
}
writer.endArray();
writer.endObject();
} catch (ConverterException ce) {
throw ce;
} catch (Exception e) {
e.printStackTrace()
throw new ConverterException( "Error converting Bean with class " + object.getClass().getName(), e);
}
}
}
Upvotes: 0
Reputation: 49629
For anyone curious, this is the validator I ended up writing in Bootstrap.groovy based off of Sérgio Michels' answer
def validationErrorsMarshaller = {
return it.getAllErrors().collect {
if (it instanceof FieldError) {
Locale locale = LocaleContextHolder.getLocale();
def msg = messageSource.getMessage(it, locale)
return [ field: it.getField(), message: msg ]
}
}
}
JSON.registerObjectMarshaller(ValidationErrors, validationErrorsMarshaller)
I was able to simplify this from the original Java marshaller by using messageSource, which is injected by adding def messageSource
to the BootStrap class.
Upvotes: 3
Reputation:
Grails register a instance of ValidationErrorsMarshaller
that handle all errors, so your FieldError marshaller will never get called.
//convertAnother is not called for each error. That's the reason of your custom marshalled not been called.
for (Object o : errors.getAllErrors()) {
if (o instanceof FieldError) {
FieldError fe = (FieldError) o;
writer.object();
json.property("object", fe.getObjectName());
json.property("field", fe.getField());
json.property("rejected-value", fe.getRejectedValue());
Locale locale = LocaleContextHolder.getLocale();
if (applicationContext != null) {
json.property("message", applicationContext.getMessage(fe, locale));
}
else {
...
Looking at ConvertersGrailsPlugin
(descriptor of the plugin) this is registered as a Spring Bean, so you can create another bean and register with the same name, overriding the marshalObject()
to fit your needs (not tested, may need more code).
class MyErrorsMarshaller implements ObjectMarshaller<JSON>, ApplicationContextAware {
ApplicationContext applicationContext;
public boolean supports(Object object) {
return object instanceof Errors;
}
public void marshalObject(Object object, JSON json) throws ConverterException {
...
}
}
resources.groovy
jsonErrorsMarshaller(MyErrorsMarshaller)
errorsJsonMarshallerRegisterer(ObjectMarshallerRegisterer) {
marshaller = { MyErrorsMarshaller om -> }
converterClass = grails.converters.JSON
}
Reference for ValidationErrorsMarshaller.
Reference for ConvertersGrailsPlugin.
Upvotes: 5