Displaying Grails Field Errors - grails

Does anybody know how I could get the fieldError to print out in the example below.
for each item with an error, I would like to print custom error messages that I have defined in the messages.properties file
at the moment all this does is print the default error codes
item.errors?.allErrors?.each{
println it.toString()
}
I have seen other examples where you can lookup an error code for a field e.g.
it.getFieldError('title').code
but I would like to convert the default message into my new error message and print that.

You need access to the messageSource bean, e.g. with
def messageSource
in your controller or service. Then you can access the messages with
def locale = Locale.getDefault()
for (fieldErrors in bean.errors) {
for (error in fieldErrors.allErrors) {
String message = messageSource.getMessage(error, locale)
}
}

A somewhat simplier solution with a better performance would be;
MessageSource messageSource //Inject the messageSource class
e.errors.allErrors.each {
String message = messageSource.getMessage(it, Locale.default)
}
OR
If you want to deal only with field errors:
e.errors.fieldErrors.each {
String message = messageSource.getMessage("modified.invalid.validator.message", [it.field, 'message'] as Object[], Locale.default))
}
Where modified.invalid.validator.message is the local string in your messages.properties. In this particular example, this message reads something like...
modified.invalid.validator.message=Property [{0}] of [{1}] does not pass validation

Related

I get an error message when i try to parse an XML response using REST assured

Iam using this simple function to get and print out the value at an XML response:
#Test
void validateXMLResponse() {
String book = RestAssured.given().when()
.get("https://chercher.tech/sample/api/books.xml")
.then().extract().path("bookstore.book.title");
System.out.println(book);
}
But iam getting this error in the console:
java.lang.ClassCastException: class io.restassured.internal.path.xml.NodeChildrenImpl cannot be cast to class java.lang.String (io.restassured.internal.path.xml.NodeChildrenImpl is in unnamed module of loader 'app'; java.lang.String is in module java.base of loader 'bootstrap')
Since you have more than book node in your xml, you should use corresponding index to define which book's title you want to get. Like this: "bookstore.book[0].title". Or iterate through index to get all the books titles.
#Test
public void validateXMLResponse() {
String book = RestAssured.given().when()
.get("https://chercher.tech/sample/api/books.xml")
.then().extract().body().path("bookstore.book[0].title");
System.out.println(book);
}

GroovyCastException metaclass i18n

This question is connected with another.
I'd like to add properties to constructor and overwrite getLocalisedMessage() function to get proper translated message with error. First I want to overload constructor to set properties, but when I add:
GroovyCastException.metaClass.constructor = { Object objectToCast, Class classToCastTo ->
def constructor = GroovyCastException.class.getConstructor(Object, Class)
def instance = constructor.newInstance(objectToCast, classToCastTo)
// ... do some further stuff with the instance ...
println "Created ${instance} and executed!"
instance
}
and then get thrown GroovyCastException I don't get println in console.
Why?
How to overload constructor, set properties (objectToCast, classToCastTo) and then overload getLocalizedMessage?
I tried also:
def originalMapConstructor = GroovyCastException.metaClass.retrieveConstructor(Map)
GroovyCastException.metaClass.constructor = { Map m ->
// do work before creation
print "boot do work before creation "
m.each{
print it
}
print "boot do work before creation 2"
def instance = originalMapConstructor.newInstance(m)
// do work after creation
print "boot do work after creation"
instance
}
I 've put it in controller (right before catching exception) and in Bootstrap.groovy. Unfortunatelly there is no printlns in console output.
You're better off not using meta-programming to do internationalization. In grails, you should do it in the view layer with the <g:message> tag if possible. If not, the next best choice is the controller layer.
If you just want to display localized messages on an error page when an exception occurs, the best practice is to have a "500" URL mapping, and render the exception with a <g:renderException> in the view.
If you want to intercept the exception, you can change the "500" URL mapping to a controller and wrap it there before passing it to the view. Example:
// UrlMappings.groovy
class UrlMappings {
static mappings = {
...
"500"(controller:"error", method: "serverError")
}
}
// ErrorController.groovy
class ErrorController {
def serverError() {
def exception = request.exception.cause
if (exception instanceof GroovyCastException) {
exception = new LocalizedGroovyCastException(exception)
}
[exception: exception]
}
}
And then do your localization in a new class LocalizedGroovyCastException.

MongoDB Collection Access

I'm using MongoDB exclusively with a Grails REST app and the domain shown below. This respond call fails:
#Secured(['permitAll'])
def index() {
respond Person.list()
}
with the error
ERROR errors.GrailsExceptionResolver - IllegalAccessException occurred when processing request: [GET] /teesheet/person
Class org.codehaus.groovy.grails.web.converters.marshaller.json.GroovyBeanMarshaller can not access a member of class java.util.Collections$UnmodifiableCollection with modifiers "public". Stacktrace follows:
Message: Class org.codehaus.groovy.grails.web.converters.marshaller.json.GroovyBeanMarshaller can not access a member of class java.util.Collections$UnmodifiableCollection with modifiers "public"
Attempting to convert the collection to JSON also fails with the same error.
def personList = Person.list() as JSON
The low level API works.
package com.tworks.teesheet
import grails.rest.Resource
class Person {
String name
String firstName
String lastName
String email
User userPerson
TimeZone timeZone = TimeZone.getTimeZone("America/Los_Angeles")
Date dateCreated = new Date()
Date dateModified = new Date()
}
Assuming you're using Mongo for Grails plugin, you need #Entity for domain classes...
import grails.persistence.Entity
#Entity
class Person {
static mapWith = "mongo"
String name
String firstName
String lastName
String email
}
I added mapWith="mongo" since I wasn't sure if you're using the hibernate plugin alongside the mongo plugin; if you're not, remove hibernate, otherwise, it may interfere.
I'm currently using the low level calls to iterate using the cursor but it seems like the respond Person.list() should work. This code is working:
def cursor = Person.collection.find()
def items = []
try {
while (cursor.hasNext()) {
items << com.mongodb.util.JSON.serialize(cursor.next())
}
} finally {
cursor.close()
}
log.info("items: ${items}")
render items

Instantiated a class, but it appears referenced to null

I'm trying to instantiate an object and set a single attribute on it, which comes from a request parameter, like so :
println "Question text from the request :" + params.question
def question = new SurveyQuestion()
question.question = params.question
println "this is our question" + question
This is my output in the console :
Question text from the request :test this is our
questionroosearch.SurveyQuestion : null
And this is the SurveyQuestion class :
class SurveyQuestion {
String question
static hasMany = [responses : SurveyQuestionResponse]
static belongsTo = [survey: Survey]
static constraints = {
}
}
The above seems to compile ok, however I get further classcast exceptions when I do a redirect at the end of my action, I believe this is due to the instantiating and setting of that SurveyQuestion, as if I comment out the above I don't get this failure behaviour.
Am I instantiating the SurveyQuestion object correctly? Why does it display as null when I print it to the console? Is that normal behaviour? At the least I'd expect it to print the object reference as Java would?
Thanks
The default toString() method on a domain instance will return a string which looks like class.name: id. As your newly created domain instance doesn't have id set is shows null.
Try overriding toString() in your SurveyQuestion domain:
String toString() {
return question
}

how to handle multiple return types in groovy Mehods?

I have a need to have a method to return Id in case of success and list of errors in case of fail.
ex code snippet:
def save = {
def errors = []
if(Employee.save(flush:true)){
return Employee.id
}else{
errors.add("Can't be saved")
return errors.
}
}
In Service class
ICalling
Employee.save() - .. so how to check if it is error or id that save method returns
Any suggestions around would be appreciated.
I agree with Burk not to return different types, it can lead to unexpected errors.
Another solution to the problem is using Java's exception handling mechanism. You can add a context field to the Exception which will hold the list of validation errors.After catching the exception you can extract the errors.
void save(Employee employee) {
// do save
// ...
// on error:
def errors = [ "terrible error nr. 5" ]
throw new ValidationException(errors)
}
try {
fooService.save(employee)
} catch(ValidationException e) {
def errors = e.erorrs
// do stuff with the errors
}
An additional advantage: When no validation error is expected, the try-catch block can be ommited in Groovy, which makes the code cleaner because you don't have to care about any validation error fields.
Don't do this - even if you can make it somewhat more usable with Groovy, it's a bad idea. In this case though, there are a few simple solutions. If you're just passing the Employee instance and saving it in the service method, you don't need to return anything:
void save(Employee employee) {
employee.save(flush:true)
}
This is because if it's successful, the id will be set on the instance you passed in, and if not there will be one or more validation errors in the errors property (there's no need for you to return a generic error message when there are actually useful error messages available).
For example this would be the code you'd have in a controller calling the service:
def employee = new Employee(...)
fooService.save(employee)
if (employee.hasErrors()) {
// do something with employee.errors
}
else {
// success - use the id if you need via employee.id
}
If you want to pass in the data to create and save the new instance and return an Employee (this is the approach I usually take), it's similar:
Employee save(String name, int foo, boolean bar, ...) {
Employee employee = new Employee(name: name, foo: foo, bar: bar, ...)
employee.save(flush:true)
return employee
}
In this second case it's important to separate the save call and the return, since if there is a validation error save returns null and you want to always return a non-null instance. So do not do this:
return employee.save(flush:true)
If you separate them you can check the errors and/or the id.
Also, make sure that you do not use closures in services like you have in your code (def save = { ...). Only methods will be transactional since the Spring transaction handling doesn't know about Groovy closures - they're just fields that Groovy calls as if they were methods, but they're not.

Resources