Exception handling in Grails controllers - grails

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'
}

Related

How to log custom exception with additional Data property to Elmah?

I have a custom exception, where I have overriden the Data property using reflection like the following...
public class MyCustomException : Exception
{
private readonly SomeModel _log;
public MyCustomException(SomeModel log)
: base(string.Format("Could not insert to some table"))
{
_log = log;
}
public override System.Collections.IDictionary Data
{
get
{
var data = new Dictionary<string, object>();
foreach (PropertyInfo pinfo in _log.GetType().GetProperties())
{
data.Add(pinfo.Name, pinfo.GetType().GetProperty(pinfo.Name));
}
return data;
}
}
}
When the above exception is thrown, it gets logged to elmah but the Data is not logged.
What changes do I have to make so that the Data is also logged to elmah ? Please advice.
The Detail property of the Elmah.Error object - which is then processed by an ErrorLog class - is built from the ToString() method of the exception.
// Elmah.Error
public Error(Exception e, HttpContext context)
{
// snip
this._detail = e.ToString(); // here
// snip
Add your data to an override of the ToString method in the MyCustomException to see it in Elmah.
Your question is currently the issue with most stars on the ELMAH issue tracker:
https://code.google.com/p/elmah/issues/detail?id=162
#samy may be more correct, but I have also found another possible option that works for my situation. I am using elmah in a webapi2 project where users are anonymous and in one particular controller I want to record some context of the request from the viewmodel (in my case, an email address but I could potentially record more data) and I want to be able to associate errors to email so I can determine, after an error, if the same user was able to submit an order successfully.
In my controller, I perform a number of database calls in a transaction and then submit an order to paypal, all within a try/catch block. In the catch, I create a new exception instance with a message containing the email and set the innerException property to the thrown exception and throw the new exception.
I know it is possible to lose some stack trace information, I tested this in my context and the stack trace seems to be maintained but exceptions occur inside the controller because there are not many layers to this particular controller and application. If anyone has a similar situation, this method might be the quickest and easiest.
catch (Exception ex)
{
Exception newException = new Exception(viewModel.ContactEmail, ex);
throw newException;
}
This assumes you have a exception filter, such as below (for webapi), and the filter is registered as global in global.asax.
public class LogExceptionAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext actionExecutedContext)
{
if (HttpContext.Current != null)
{
ErrorSignal.FromCurrentContext().Raise(actionExecutedContext.Exception);
}
}
}

populating own error-messages to the grails domain errors

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
}
}

Grails: NullPointerException while save()

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.

ASP.Net MVC Error handling using Action Filters Attributes

I am trying to implement Error handling using Action Filters Attributes as per ScottGu's blog
My code is as follows:
[HandleError]
[HandleError(ExceptionType = typeof(NullReferenceException), View = "CustomError")]
public class ArticlesController : Controller
{
public object OhDearACrash()
{
throw new Exception("Oh Dear");
}
public object NullRefCrash()
{
throw new NullReferenceException();
}
I am encountering an issue where I am never able to hit the CustomError view as I receive an exception when the exception is thrown
OhDearACrash: Exception was unhandled by user code
NullRefCrash: NullReferenceException was unhandled by user code
and so the unhandled exception is picked up by the Default [HandleError] which routes to View/Shared/Error.aspx which handles the error.
How do I handle the unhandled exception?
The action filters are executed one by one. In your case, the problem is probably that the generic HandleError action filter is executed before the specific one.
You can influence the order of execution by setting the 'Order' property of your action filter:
[HandleError(Order = 2)]
[HandleError(Order = 1, ExceptionType = typeof(NullReferenceException), View = "CustomError")]
public class ArticlesController : Controller
{
}

Streamlining entity lookup in Grails controllers (the typical get / findById)

Almost every controller action looks up one or more entities based on some user input. For some time I've been wanting to remove some of this boring, dry-breaking, boilerplate code:
def show = {
def entity = null
if (params.id && params.id.isLong() && params.id.toLong() >= 0)
entity = Book.get(params.id.toLong())
if (!entity) {
flash.message = "Could not find, blah blah blah"
return redirect(...)
}
// Useful code
}
My current approach involves injecting a findEntry method into all controllers.
void injectFindEntryMethod() {
grailsApplication.controllerClasses.each { def c ->
c.metaClass.findDomainEntry = { def domainClass, def entryId ->
def entry = null
if (entryId && entryId.isLong() && entryId.toLong() >= 0)
entry = domainClass.findById(entryId)
if (!entry)
throw new DomainInstanceNotFoundException(domainClass, entryId)
return entry
}
}
}
The basic idea behind this method is that it will throw an exception when an entry can't be found. Currently I'm "catching" this exception by using the "declarative error handling" functionality in Grails 1.1.
"500"(controller: "error", action: 'domainInstanceNotFound', exception: DomainInstanceNotFoundException)
The neat thing about this solution is that the bloated code in the show action is reduced to:
def show = {
def entry = findDomainEntry(BlogEntry, params.id)
// Useful code
}
Unfortunately this also comes with a few drawbacks, but hey, that's why we have Stackoverflow right? :-)
Issues / drawbacks:
This will cause the stacktrace of the exception to be logged, which is annoying since I'm handling the exception in my ErrorController.
I can't access the exception object inside the action method, but the object is accessible by ${exception} inside the view (I can't understand why it's implemented this way).
What do you guys think of this approach? Any ways to improve it? Any advices or soultions to the drawbacks I mentioned above? I'm sorry if the scope of my question(s) is too big, it just doesn't make sense to split it into multiple questions.

Resources