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.
Related
I am throwing a custom exception inside the withTransaction method based on a scenarion when author not found.But the issue I am facing is even if the code is enetering inside the exception block for non existent authors, it is not existing out of the flow but continuing with the flow.
Just wanted to check is there anything i am missing here or doing wrong.
Author.withTransaction() {
authStatus -> def author = Author.get(id)
if (!author) {
log.warn "author not found"
throw new NotFoundException('author not found')
}
author.status = 'completed'
author.save()
}
Thanks
Sam
do you really have authStatus -> def author = Author.get(id) all on one line ? or is authStatus -> on withTransaction line, usually a return stops something from continuing but since you are throwing there shouldn't be a need for that. Why not reverse that logic to
if (author) {
do something
return
}
//obviously we didn't have an author since we haven't returned so back to your throw
log.warn "author not found"
throw new NotFoundException('author not found')
I would change that to
Author.withTransaction {
def author = Author.get(id)
if (author) {
author.status = 'completed'
author.save()
return author
}
log.warn "author not found"
throw new NotFoundException('author not found')
}
Personally I would probably wrap entire thing around try catch and not even throw that specific case but instead try to capture get and save errors with one throw at bottom of try catch since you may have got the record but did you manage to save it correctly ?
I spent a long time trying to get the ASP.NET MVC [HandleError] attribute to work in my websites. It seemed like a good idea to go with the solution offered by the framework, but I just couldn't get it to do anything useful. Then I tried writing my own attribute (mainly so that I could step in to the code with the debugger), but although my code seemed to be doing all the right things, after it executed the framework took over and did mysterious things. Finally I tried the MVC Contrib's [Rescue] attribute, which was better but I still couldn't get it to do what I wanted.
One problem is that exceptions thrown in code embedded in aspx / ascx pages get wrapped in HttpException's and WebHttpException's.
Another problem for me was that the system is very opaque. I was essentially poking inputs in to a black box with some desired outputs in mind, but with no idea (other than the documentation, which doesn't seem very accurate / thorough) what the relationship was between them.
So, what to do?
I went for Dynamic Proxies in Castle Windsor, using the code below, which tries to handle Database errors, for which I have a specific Exception (AccessDBException).
The _alreadyAttemptedToShowErrorPage is to stop infinite recursion in the case where the error page throws an Exception.
The GetAccessDBException(...) method finds the relevant exception anywhere in the Exception stack, for the case when there are problems in aspx / ascx code.
The code requires that there is a BaseController class that all controllers derive from. This class is used to add a CreateErrorView(...) method (being as the standard View(...) method is protected)
public class AccessDBExceptionHandlingDynamicProxy : IInterceptor
{
private bool _alreadyAttemptedToShowErrorPage;
public AccessDBExceptionHandlingDynamicProxy()
{
_alreadyAttemptedToShowErrorPage = false;
}
public void Intercept(IInvocation invocation)
{
Contract.Requires(invocation.Proxy is BaseController);
try
{
invocation.Proceed();
}
catch (HttpException e)
{
if (_alreadyAttemptedToShowErrorPage == true) throw e;
_alreadyAttemptedToShowErrorPage = true;
var dbException = GetAccessDBException(e);
if (dbException != null)
{
var baseController = (invocation.Proxy as BaseController);
var view = baseController.CreateErrorView("AccessDBException", new AccessDBExceptionViewModel(dbException));
baseController.Response.Clear();
baseController.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
view.ExecuteResult(baseController.ControllerContext);
baseController.Response.End();
}
else
{
throw e;
}
}
}
private static AccessDBException GetAccessDBException(HttpException e)
{
AccessDBException dbException = null;
Exception current = e;
while (dbException == null && current != null)
{
if (current is AccessDBException) dbException = (current as AccessDBException);
current = current.InnerException;
}
return dbException;
}
}
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'
}
def handleLogin = {
def hashPassd = DU.md5Hex(params.password)
// Find the username
def user = User.findByUserNameAndPassword(params.userName, hashPassd)
if (!user) {
flash.message = "User not found for userName: ${params.userName}"
redirect(action:'index')
return
} else {
session.user = user
redirect(controller:'todo')
}
}
how come the if condition requires the return statement? and the else block don't require it?
In this case the return is not necessary. If the return isn't there it will continue to after the if, which is the end of the method, and thus returns.
The return is not strictly necessary but it's a good habit to get into. Calls like render and redirect in a controller action are usually intended to be the conceptual "end" of the action, and its easy to forget sometimes and treat the redirect as if it were a return
def someAction() {
if(!params.id) {
flash.message = "no ID provided"
redirect action:'error'
}
def myObj = MyObj.get(params.id)
//...
}
This will fail at runtime, probably with a rather obscure error message, and to avoid this possibility it's a good idea to remember to put an explicit return after any calls to redirect or render. It's the kind of thing that has bitten me when coming back to make changes to a controller action I originally wrote 6 months previously...
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.