I'd like to know, if (and how) I could append some own error-messages to the domain-object after (or before) a validation.
My intention is, I have to check the uploaded file in a form for some attributes (image size etc.) and if something is wrong, I would like to add an error-message which is displayed in the usual grails ".hasErrors" loop.
(And I think I need to have the possibility to express errors in some cross-domain check failure...)
Thanks in advance,
Susanne.
You can add custom validation errors as described in the errors docs as follows:
class SampleController {
def save() {
def sampleObject = new SampleObject(params)
sampleObject.validate()
if(imageSizeIsTooBig(sampleObject)) {
sampleObject.errors.rejectValue(
'uploadedFile',
'sampleObject.uploadedFile.sizeTooBig'
)
}
private def imageSizeIsTooBig(SampleObject sampleObject) {
// calculation on sampleObject, if size is too big
}
Perhaps, you could even handle your case with a custom validator, so you can call validate() one time and be sure, that all constraints are fulfilled.
Here is a real example with a custom domain error:
def signup(User user) {
try {
//Check for some condition
if (!params.password.equals(params.passwordRepeat)) {
//Reject the value if condition is not fulfilled
user.errors.rejectValue(
'password',
'user.password.notEquals',
'Default message'
)
//Throw an exception to break action and rollback if you are in a service
throw new ValidationException('Default message', user.errors)
}
//Continue with your logic and save if everything is ok
userService.signup(user)
} catch (ValidationException e) {
//Render erros in the view
respond user.errors, view:'/signup'
return
}
}
Related
My domain object booking has multiple attributes that are allowed to be null, because they will be set later after the object has been saved to the db.
Part of myService.action():
booking.properties = params
if (booking.contactFirstname?.length() <= 1) { booking.errors.rejectValue("contactFirstname", "empty") }
if (booking.contactLastname?.length() <= 1) { booking.errors.rejectValue("contactLastname", "empty") }
if (booking.contactPhone?.length() <= 1) { booking.errors.rejectValue("contactPhone", "empty") }
if (booking.contactMobile?.length() <= 1) { booking.errors.rejectValue("contactMobile", "empty") }
if (booking.contactEmail?.length() <= 1) { booking.errors.rejectValue("contactEmail", "empty") }
if (booking.hasErrors() || ! booking.validate()) {
return [success: false, model: booking]
} else {
booking.save(failOnError: true)
return [success: true, model: booking]
}
My controller does:
def result = myService.action(params)
if (result.success) {
flash.success = message(code: "msg.successfullySaved")
redirect(action: "registerEventConfirmation", id: result.model.uid, params: [lang: params.lang], mapping: "paginated")
} else {
flash.error = message(code: "msg.errorSavingCheckFields")
render(view: "registerEventStep3", params: [lang: params.lang], model: [booking: result.model])
I'm using
hasErrors(bean: booking,field:'contactFirstname', 'has-error')}
to mark error fields.
If I now submit the form without any values in textfields, all fields are red, booking.errors has >0 errors.
If I submit the form after with a firstname, booking.errors is NULL and no other field is marked.
Is this a Bug? I'm with Grails 2.3.6
additional information
I visit the form, submit it empty completely
I see all form fields in red, object.errors has >0 errors (VALID)
I enter a value in the first field, firstname and submit
I see none of the form fields in red, object.errors =0 errors (INVALID)
I re-submit the form with none changes
I see all empty form fields in red, object.errors has >0 errors (VALID)
Now that I fully understand the situation and since I was having trouble sleeping I thought I give you a very concise answer so that you can hopefully make full sense and use things properly.
Firstly I know creating a validation bean sounds like it will be a lot of work so let me teach you how to do it all relatively simply and why it is my preferred method.
It is my preferred method simply because when you do
class MyController {
def myAction(Mybean bean) {
// 1. the object allowed into this save action
// are all that is available objects withing MyBean.
// If it has user define but not telephone. Then
// if telephone is passed to myAction it will fail and not recognise
// field
// When declaring Date someField or User user then the params now
// received as bean this way is now actually properly bound
// to the data / domainType declared.
// Meaning user will now be actual user or someField actually Date
}
So now to explain how to best solve this issue. When creating beans simply copy over the actual domain class from your domain folder into src/groovy/same/package in grails 2 or src/main/groovy/same/package in grails 3
Change name / class or copy as from Booking to BookingBean so it has a different name.
Add #Validateable above actual BookingBean in grails 2 or add implements to main class like Class BookingBean implements Validateable { in grails 3
Now since it is copied all the objects are identical and at this point a save from the controller would be
class MyController {
def myAction(BookingBean bean) {
Booking booking = new Booking()
// this will save all properties
booking.properties = bean
booking.save()
}
}
But you have a special circumstance and you wanted to declare a transient field in the main domain class what I would do instead is
class BookingBean {
def id
String contactFirstname
String contactLastname
boolean secondSave=false
static constraints = {
id(nullable: true, bindable: true)
contactFirstname(nullable:true) //,validator:checkHasValue)
contactLastname(nullable:true) //,validator:checkHasValue)
secondSave(nullable:true,validator:checkHasValue))
}
//use the same validator since it is doing identical check
static checkHasValue={value,obj,errors->
// So if secondSave has a value but contactFirstName
// is null then complain about contactFirstName
// you can see how secondSave gets initialise below
//typical set this to true when you are about to save on 2nd attempt
//then when set run validate() which will hit this block below
// Check all the things you think should have a
// value and reject each field that don't
if (val) {
if ( !obj.contactFirstname) {
errors.rejectValue('contactFirstname',"invalid.contactFirstname")
}
if ( !obj.contactSecondname) {
errors.rejectValue('contactSecondname',"invalid.contactSecondname")
}
//and so on
}
}
So now in your controller:
class MyController {
def save1(BookingBean bean) {
Booking booking = new Booking()
// this will save all properties
booking.whatEver = bean.whatEver
booking.save()
// you can choose to validate or not here
// since at this point the secondSave has
// not been set therefore validation not called as yet in the bean
}
//you probably have id and it should bind with actual domain class
def save2(BookingBean bean) {
booking.secondSave=true
if (!bean.validate()) {
//this is your errors
//bean.errors.allErrors
return
}
//otherwise out of that loop since it hasn't returned
//manually set each object
booking.contactFirstname=bean.contactFirstName
booking.contactSecondname=bean.contactSecondname
booking.save()
}
}
e2a side note - above should answer
well don't validate it until you have created it. Only validate it after you created the object then added a value. Alternative create a function possibly in a validation bean that you run as part of your 2nd check. This Example bean is not validated until formatRequest is called as seen here
I don't grasp the specifics of your question, so I will give some general guidance since I have just dug into this.
Don't call hasErrors() before validate(). If you do, Grails won't hand you errors from domain constraints and you will only end up with the errors you set yourself using rejectValue().
Be careful with using rejectValue(). Try to set all your errors using domain constraints. If you have sophisticated constraints use the validator syntax and obj.getPersistentValue() might be your friend once in a while.
If you still have to use rejectValue(), understand that any later calls to validate() will start from scratch and erase your prior errors. I have written a workaround for this (to be placed in your domain object) although I can't assure you it is 100% ok:
def validateWithErrors(def fields = null) {
def existingErrors = this.errors
def ret = (fields ? this.validate(fields) : this.validate())
existingErrors?.allErrors?.each { error ->
this.errors.rejectValue(error.field, error.code)
}
return (existingErrors?.allErrors ? false : ret)
}
I'm using grails 2.4.2 and have in my controller-update method the following code:
#Transactional
def update(ErtIncommingInvoice ertIncommingInvoiceInstance) {
if (ertIncommingInvoiceInstance == null) {
notFound()
return
}
// Concurrent-Update Test
if (ertIncommingInvoiceInstance.version != params.version as int) {
flash.warning = "Another user changed the record! (Concurrent Update Error)"
ertIncommingInvoiceInstance.errors.rejectValue("ertInfo", "concurrent.update.error")
respond ertIncommingInvoiceInstance.errors, view:'edit'
return
}
even in the case, the error is detected and the errors-object is set and the method-flow does not execute the
ertIncommingInvoiceInstance.save flush:true, failOnError: true
the data is already changed in the database.
The edit-view is shown, but doesn't display the error, only the flash-message.
Where's my error in reasoning?
Grails will call validate before any save and overwrite whatever you set in the errors object. Additionally, Grails will call save automatically on your objects after your method finishes. You should either call discard() on any objects you've changed but don't wish to persist or create a transaction using a withTransaction block and manually roll it back.
As #Gregor Petrin answered, I use now the following code, to check for a concurrent-update and redisplay the changed data from the other user...:
#Transactional
def update(ErtIncommingInvoice ertIncommingInvoiceInstance) {
if (ertIncommingInvoiceInstance == null) {
notFound()
return
}
// Concurrent-Update Test
if (ertIncommingInvoiceInstance.version != params.version as int) {
ertIncommingInvoiceInstance.discard()
ertIncommingInvoiceInstance = ErtIncommingInvoice.get(params.id)
ertIncommingInvoiceInstance.errors.reject("concurrent.update.error")
respond ertIncommingInvoiceInstance.errors, view:'edit'
return
}
I am new to Grails, I tried to save domain object by calling save() in my *service.groovy as shown below
render " ${user.username}"
render " ${user.email}"
render " ${user.password}"
def savedUser = user.save(flush: true)
if(savedUser!=null) {
return savedUser
} else {
return user
}
The render shows all elements have the values which have been passed from controller.
but an NullPointerexception is thrown in save().
the actual error got is
ERROR errors.GrailsExceptionResolver - NullPointerException occurred when processing request: [POST]
Validation error might have occurred, but I checked all validation error in controller by using command class.
How can avoid the exception here?
That's pretty nonstandard code. Rather than checking for a null return value, it's more common (and sensible/useful) to check if there were validation errors. E.g.
user.save(flush: true)
if (user.hasErrors()) {
// do something with the invalid "user" instance
}
else {
// do something with the valid "user" instance
}
For your scenario, you can ignore the return value and just work with the original instance:
user.save(flush: true)
return user
If you're working with the standard templates (or something similar) then this should work fine since there's logic there to check if there are attached errors and display them.
I know how to do generic exception handling in Grails using UrlMappings and an ErrorController for generic exception handling, so that if an exception escapes a controller the user will be sent to a generic error page and the exception will be logged. I also know how to use try/catch blocks to handle specific exceptions and attempt to recover from them.
But in most controllers, I just want to give the user a slightly more specific error message if an exception occurs. So in the create action, I want to tell the user that the item wasn't created. Or in the import action, I want to tell the user that the import failed. Right now, the controllers look like:
class ThingController {
def create = {
try {
// The real controller code, which quickly hands it off to a service
} catch (Exception e) {
handleException(e, "There was an error while attempting to create the Thing")
}
}
def delete = {
try {
// The real controller code, which quickly hands it off to a service
} catch (Exception e) {
handleException(e, "There was an error while attempting to delete the Thing")
}
}
private void handleException(Exception e, String message) {
flash.message = message
String eMessage = ExceptionUtils.getRootCauseMessage(e)
log.error message(code: "sic.log.error.ExceptionOccurred", args: ["${eMessage}", "${e}"])
redirect(action:index)
}
}
Note that the catch blocks don't do anything different based on the type or content of the exception; they're just giving a slightly more descriptive error message based on the controller. The "real" controller code is usually 6-10 lines, so having an additional 4 lines of code just to change the error message seems excessive. In addition, the CodeNarc "CatchException" rule complains, which reinforces my opinion that there has to be a better way to do this. I assume other Grails applications have similar requirements. What is the idiomatic way to specify different error messages based on which action the exception bubbled out of?
I'm interested in answers that come from experience with a particular way of solving this problem, or even better, link to codebases where I can see the solution in practice.
Grails has the mechanism for general handling controller exceptions.
You can do this inside a dedicated Error controller. Regular controllers don’t need to use try/catch.
Controller:
class ThingController {
def create() {
def id = params.id as Long
if (id == null) {
throw new MissingPropertyException("thingId")
}
// The real controller code, which mostly parses things out and hands it
// off to a service.
// Service methods can throws exception
}
}
Add handling 500 error in UrlMappings:
class UrlMappings {
static mappings = {
// Exception handling in ErrorController
"500"(controller: "error")
}
}
ErrorController:
class ErrorController {
def index() {
def exception = request.exception.cause
def message = ExceptionMapper.mapException(exception)
def status = message.status
response.status = status
render(view: "/error", model: [status: status, exception: exception])
}
}
You can handle REST and non-REST exceptions using this approach.
Also there is Declarative Exception Handling plugin but I don’t have an
Update
You can get the specific error messages in Error controller.
When in controller throw new RuntimeException ("There was an error while attempting to delete the Thing"), then in error controller request.exception.cause.message will show message: "There was an error while attempting to delete the Thing".
See also How to know from where was thrown error 500 (Grails)
I create custom error pages based on annotations on the controllers, giving common exception handling procedures across several controllers.
class ErrorsController {
def index() {
def initialController = request.exception?.className
if (initialController) {
def controller = grailsApplication.getArtefact("Controller", initialController).getReferenceInstance()
// do some rendering based on the annotations
render "Controller: ${initialController}, annotations ${controller.getClass().getDeclaredAnnotations()}"
return
}
render 'no initial controller'
}
I'm new to Groovy & Grails, and I have a feeling that things don't have to be this ugly... so how can I make this code nicer?
This is a Grails controller class, minus some uninteresting bits. Try not to get too hung up that my Car only has one Wheel - I can deal with that later :-)
changeWheel is an Ajax action.
class MyController {
...
def changeWheel = {
if(params['wheelId']) {
def newWheel = Wheel.findById(params['wheelId'])
if(newWheel) {
def car = Car.findById(params['carId'])
car?.setWheel(newWheel)
if(car?.save()) render 'OK'
}
}
}
}
I'd actually start using Command Objects.
Try this:
class MyController {
def index = {
}
def changeWheel = { CarWheelCommand cmd ->
if(cmd.wheel && cmd.car) {
Car car = cmd.car
car.wheel = cmd.wheel
render car.save() ? 'OK' : 'ERROR'
} else {
render "Please enter a valid Car and wheel id to change"
}
}
}
class CarWheelCommand {
Car car
Wheel wheel
}
and then in your view use 'car.id' and 'wheel.id' instead of 'carId' and 'wheelId'
1) pull
params['wheelId']
and
params['carId']
out into their own defs
2) multiple nested ifs is never optimal. You can get rid of the outermost one by having a validateParams method and rendering some sort of response if wheelId and carId are not set. Or just do
if (carId == null || wheelId == null) {
// params invalid
}
3) Assuming everything is ok you could just do
def newWheel = Wheel.findById...
def car = Car.findById...
if (car != null && newWheel != null) {
car.setWheel(newWheel)
car.save()
render 'OK'
} else {
// either wheel or car is null
}
this gets rid of more nested structures...
4) finally, to make the code self documenting, you can do things like assign the conditional tests to appropriately named variables. So something like
def carAndWheelOk = car != null && newWheel != null
if (carAndWheelOk) {
// do the save
} else {
// car or wheel not ok
}
this might be overkill for two tests, but you only are taking care of one wheel here. If you were dealing with all 4 wheels, this type of things increases readability and maintainability.
Note that this advice works in any language. I don't think you can do too much with groovy's syntactic sugar, but maybe some groovy gurus can offer better advice.
There are a couple of things you could do like move some code to a service or command object. But without altering the structure too much, I (subjectively) think the following would make the code easier to read:
use dot notation instead of array indexing to reference params values (params.wheelId instead of params['wheelId'])
I would invert the if to reduce nesting, I think this makes it more clear what the exceptions are.
For example:
if(!params.wheelId) {
sendError(400, "wheelId is required")
return
}
....
....
if(!newWheel) {
sendError(404, "wheel ${params.wheelId} was not found.")
return
}
Now if you don't mind changing the structure and adding more lines of code...
The act of changing the wheel may be a common occurrence across more than just one controller action. In this case I'd recommend putting the GORM/database logic in a Service class. Then your controller only has to verify it has the correct params inputs and pass those on to the Service to do the actual tire changing. A Service method can be transactional, which you'd want in the case where you might have to dismount the old tire before mounting the new one.
In the Service I would throw exceptions for exceptional cases like when a wheel is not found, a car is not found, or if there's an error changing the tire. Then your controller can catch those and respond with the proper HTTP status codes.