Quantcast

controller action exception handlers and command object handling

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

controller action exception handlers and command object handling

Jeff Scott Brown-2
I spent some time today working on GRAILS-11576 and have come up with an idea that I would like some feedback on.

When you write a controller like this:

class SomeController {
    def someAction(SomeCommandObject sco) {
        // your code...
    }
}

Grails compiles that to something like this:

class SomeController {
    def someAction() {
        def co = initializeCommandObject(SomeCommandObject)
        // parse the body, do data binding, do DI

        someAction(co)
    }

    def someAction(SomeCommandObject sco) {
        try {
            // your code...
        } catch (Exception e) {
            // check to see if there is a corresponding
            // exception handling method.  if there is
            // call it, otherwise throw e
        }
    }

    def initializeCommandObject(Class type) {
        def obj = type.newInstance()
        // if the request has a body, parse it and
        // do data binding with it, otherwise do
        // data binding with params

        obj
    }
}

(There is other stuff, I am only including the bits relevant to this discussion)

I made a change today to do something like this:

class SomeController {
    def someAction() {
        def co = initializeCommandObject(SomeCommandObject)
        // parse the body, do data binding, do DI

        someAction(co)
    }

    def someAction(SomeCommandObject sco) {
        try {
            // your code...
        } catch (Exception e) {
            // check to see if there is a corresponding
            // exception handling method.  if there is
            // call it, otherwise throw e
        }
    }

    def initializeCommandObject(Class type) {
        def obj = type.newInstance()
        try {
                // if the request has a body, parse it and
                // do data binding with it, otherwise do
                // data binding with params
        } catch (DataBindingSourceCreationException e) {
            obj.errors.addError(new ObjectError(…))
        }

        obj
    }
}

With that change, if a DataBindingSourceCreationException occurs (for example, if malformed JSON or XML are in the body) then instead of an exception being thrown, an error is added to the command object and the controller action is still invoked.

I think that is useful and an improvement over what we had previously.

Tonight I worked up a small addition to that, which does something like this…

class SomeController {
    def someAction() {
        try {
            def co = initializeCommandObject(SomeCommandObject)
            // parse the body, do data binding, do DI

            someAction(co)
        } catch (Exception e) {
            // check to see if there is a corresponding
            // exception handling method.  if there is
            // call it, otherwise throw e
        }
    }

    def someAction(SomeCommandObject sco) {
        // your code...
    }

    def initializeCommandObject(Class type) {
        def obj = type.newInstance()
        try {
                // if the request has a body, parse it and
                // do data binding with it, otherwise do
                // data binding with params
        } catch (DataBindingSourceCreationException e) {
            // maybe should catch java.lang.Exception instead of narrowing
            // to DataBindingSourceCreationException
            if(controllerHasExceptionHandlerMethodForDataBindingSourceCreationException) {
                throw e
            }
            obj.errors.addError(new ObjectError(…))
        }

        obj
    }
}

There are 2 changes there to note.  One is that the try/catch that was inside of the someAction(SomeCommandObject) method has been moved up to the no-arg version of the method.  The other is that the initializeCommandObject(Class) method now checks to see if an exception handler method exists that is compatible with DataBindingSourceCreationException.  If one does exist, the exception is thrown and will eventually be passed to that method instead of invoking the controller action.  If one does not exist, then fall back to populating the command object with an error and invoking the action.  That will allow applications to do something like this:

class SomeController {
    def someAction(SomeCommandObject sco) {
        // your code...
    }

    // as of GRAILS-11453 if this is used in several 
    // controllers it could be defined in a trait...
    def handleDataBindingException(DataBindingSourceCreationException e) {
        // do whatever makes sense for your application here...
        response.status = 400
        flash.message = 'caught a DataBindingSourceCreationException’
        render view: ‘/someViewWeUseForThisSortOfThing'
    }
}

I think that it makes sense to offer applications the ability to deal with exceptions that are generated while processing command objects if they want to do so.  Some applications might want that sort of thing and for those who don’t, this isn’t imposing any complexity on them.  The code in the framework to support that is minimal.

What say ye?



JSB



Jeff Scott Brown
[hidden email]

Find The Cause ~ Find The Cure  
http://www.autismspeaks.org/


--
You received this message because you are subscribed to the Google Groups "Grails Dev Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To post to this group, send email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/grails-dev-discuss/etPan.53d19bce.643c9869.63c0%40JSB-MBP-August-2012.local.
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: controller action exception handlers and command object handling

zyro
looks fine for me. allowing the users to catch a
(DataBindingSourceCreation-) Exception if they want to makes much sense.

i would also welcome the widening of this behavior to any Exception
(during command object initialization) delegating the exception handler
lookup to the same code that does it for exceptions thrown from user
code inside controller actions.

thanks again! zyro

-------- Original Message  --------
Subject: controller action exception handlers and command object handling
From: Jeff Scott Brown <[hidden email]>
To: [hidden email]
Date: Thu, 24 Jul 2014 18:50:38 -0500

> I spent some time today working on GRAILS-11576 and have come up with an idea that I would like some feedback on.
>
> When you write a controller like this:
>
> class SomeController {
>      def someAction(SomeCommandObject sco) {
>          // your code...
>      }
> }
>
> Grails compiles that to something like this:
>
> class SomeController {
>      def someAction() {
>          def co = initializeCommandObject(SomeCommandObject)
>          // parse the body, do data binding, do DI
>
>          someAction(co)
>      }
>
>      def someAction(SomeCommandObject sco) {
>          try {
>              // your code...
>          } catch (Exception e) {
>              // check to see if there is a corresponding
>              // exception handling method.  if there is
>              // call it, otherwise throw e
>          }
>      }
>
>      def initializeCommandObject(Class type) {
>          def obj = type.newInstance()
>          // if the request has a body, parse it and
>          // do data binding with it, otherwise do
>          // data binding with params
>
>          obj
>      }
> }
>
> (There is other stuff, I am only including the bits relevant to this discussion)
>
> I made a change today to do something like this:
>
> class SomeController {
>      def someAction() {
>          def co = initializeCommandObject(SomeCommandObject)
>          // parse the body, do data binding, do DI
>
>          someAction(co)
>      }
>
>      def someAction(SomeCommandObject sco) {
>          try {
>              // your code...
>          } catch (Exception e) {
>              // check to see if there is a corresponding
>              // exception handling method.  if there is
>              // call it, otherwise throw e
>          }
>      }
>
>      def initializeCommandObject(Class type) {
>          def obj = type.newInstance()
>          try {
>        // if the request has a body, parse it and
>        // do data binding with it, otherwise do
>        // data binding with params
>          } catch (DataBindingSourceCreationException e) {
>              obj.errors.addError(new ObjectError(…))
>          }
>
>          obj
>      }
> }
>
> With that change, if a DataBindingSourceCreationException occurs (for example, if malformed JSON or XML are in the body) then instead of an exception being thrown, an error is added to the command object and the controller action is still invoked.
>
> I think that is useful and an improvement over what we had previously.
>
> Tonight I worked up a small addition to that, which does something like this…
>
> class SomeController {
>      def someAction() {
>          try {
>              def co = initializeCommandObject(SomeCommandObject)
>              // parse the body, do data binding, do DI
>
>              someAction(co)
>          } catch (Exception e) {
>              // check to see if there is a corresponding
>              // exception handling method.  if there is
>              // call it, otherwise throw e
>          }
>      }
>
>      def someAction(SomeCommandObject sco) {
>          // your code...
>      }
>
>      def initializeCommandObject(Class type) {
>          def obj = type.newInstance()
>          try {
>        // if the request has a body, parse it and
>        // do data binding with it, otherwise do
>        // data binding with params
>          } catch (DataBindingSourceCreationException e) {
>              // maybe should catch java.lang.Exception instead of narrowing
>              // to DataBindingSourceCreationException
>              if(controllerHasExceptionHandlerMethodForDataBindingSourceCreationException) {
>                  throw e
>              }
>              obj.errors.addError(new ObjectError(…))
>          }
>
>          obj
>      }
> }
>
> There are 2 changes there to note.  One is that the try/catch that was inside of the someAction(SomeCommandObject) method has been moved up to the no-arg version of the method.  The other is that the initializeCommandObject(Class) method now checks to see if an exception handler method exists that is compatible with DataBindingSourceCreationException.  If one does exist, the exception is thrown and will eventually be passed to that method instead of invoking the controller action.  If one does not exist, then fall back to populating the command object with an error and invoking the action.  That will allow applications to do something like this:
>
> class SomeController {
>      def someAction(SomeCommandObject sco) {
>          // your code...
>      }
>
>      // as of GRAILS-11453 if this is used in several
>      // controllers it could be defined in a trait...
>      def handleDataBindingException(DataBindingSourceCreationException e) {
>          // do whatever makes sense for your application here...
>          response.status = 400
>          flash.message = 'caught a DataBindingSourceCreationException’
>          render view: ‘/someViewWeUseForThisSortOfThing'
>      }
> }
>
> I think that it makes sense to offer applications the ability to deal with exceptions that are generated while processing command objects if they want to do so.  Some applications might want that sort of thing and for those who don’t, this isn’t imposing any complexity on them.  The code in the framework to support that is minimal.
>
> What say ye?
>
>
>
> JSB
>
>
> —
> Jeff Scott Brown
> [hidden email]
>
> Find The Cause ~ Find The Cure
> http://www.autismspeaks.org/
>
>

--
You received this message because you are subscribed to the Google Groups "Grails Dev Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To post to this group, send email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/grails-dev-discuss/53D1F3CC.7060808%40zyro.net.
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: controller action exception handlers and command object handling

Mathias Fonseca
I also find this very useful and interesting.

The only observation I would like to make, is that the error added to the object if the DataBindingSourceCreationException is not catched, should be clear and easy to tell apart from other validation errors.

Thanks!

On Friday, July 25, 2014 3:06:10 AM UTC-3, zyro wrote:
looks fine for me. allowing the users to catch a
(DataBindingSourceCreation-) Exception if they want to makes much sense.

i would also welcome the widening of this behavior to any Exception
(during command object initialization) delegating the exception handler
lookup to the same code that does it for exceptions thrown from user
code inside controller actions.

thanks again! zyro

-------- Original Message  --------
Subject: controller action exception handlers and command object handling
From: Jeff Scott Brown <<a href="javascript:" target="_blank" gdf-obfuscated-mailto="EUxuOoFLp80J" onmousedown="this.href='javascript:';return true;" onclick="this.href='javascript:';return true;">jbr...@...>
To: <a href="javascript:" target="_blank" gdf-obfuscated-mailto="EUxuOoFLp80J" onmousedown="this.href='javascript:';return true;" onclick="this.href='javascript:';return true;">grails-de...@googlegroups.com
Date: Thu, 24 Jul 2014 18:50:38 -0500

> I spent some time today working on GRAILS-11576 and have come up with an idea that I would like some feedback on.
>
> When you write a controller like this:
>
> class SomeController {
>      def someAction(SomeCommandObject sco) {
>          // your code...
>      }
> }
>
> Grails compiles that to something like this:
>
> class SomeController {
>      def someAction() {
>          def co = initializeCommandObject(SomeCommandObject)
>          // parse the body, do data binding, do DI
>
>          someAction(co)
>      }
>
>      def someAction(SomeCommandObject sco) {
>          try {
>              // your code...
>          } catch (Exception e) {
>              // check to see if there is a corresponding
>              // exception handling method.  if there is
>              // call it, otherwise throw e
>          }
>      }
>
>      def initializeCommandObject(Class type) {
>          def obj = type.newInstance()
>          // if the request has a body, parse it and
>          // do data binding with it, otherwise do
>          // data binding with params
>
>          obj
>      }
> }
>
> (There is other stuff, I am only including the bits relevant to this discussion)
>
> I made a change today to do something like this:
>
> class SomeController {
>      def someAction() {
>          def co = initializeCommandObject(SomeCommandObject)
>          // parse the body, do data binding, do DI
>
>          someAction(co)
>      }
>
>      def someAction(SomeCommandObject sco) {
>          try {
>              // your code...
>          } catch (Exception e) {
>              // check to see if there is a corresponding
>              // exception handling method.  if there is
>              // call it, otherwise throw e
>          }
>      }
>
>      def initializeCommandObject(Class type) {
>          def obj = type.newInstance()
>          try {
>                 // if the request has a body, parse it and
>                 // do data binding with it, otherwise do
>                 // data binding with params
>          } catch (DataBindingSourceCreationException e) {
>              obj.errors.addError(new ObjectError(…))
>          }
>
>          obj
>      }
> }
>
> With that change, if a DataBindingSourceCreationException occurs (for example, if malformed JSON or XML are in the body) then instead of an exception being thrown, an error is added to the command object and the controller action is still invoked.
>
> I think that is useful and an improvement over what we had previously.
>
> Tonight I worked up a small addition to that, which does something like this…
>
> class SomeController {
>      def someAction() {
>          try {
>              def co = initializeCommandObject(SomeCommandObject)
>              // parse the body, do data binding, do DI
>
>              someAction(co)
>          } catch (Exception e) {
>              // check to see if there is a corresponding
>              // exception handling method.  if there is
>              // call it, otherwise throw e
>          }
>      }
>
>      def someAction(SomeCommandObject sco) {
>          // your code...
>      }
>
>      def initializeCommandObject(Class type) {
>          def obj = type.newInstance()
>          try {
>                 // if the request has a body, parse it and
>                 // do data binding with it, otherwise do
>                 // data binding with params
>          } catch (DataBindingSourceCreationException e) {
>              // maybe should catch java.lang.Exception instead of narrowing
>              // to DataBindingSourceCreationException
>              if(controllerHasExceptionHandlerMethodForDataBindingSourceCreationException) {
>                  throw e
>              }
>              obj.errors.addError(new ObjectError(…))
>          }
>
>          obj
>      }
> }
>
> There are 2 changes there to note.  One is that the try/catch that was inside of the someAction(SomeCommandObject) method has been moved up to the no-arg version of the method.  The other is that the initializeCommandObject(Class) method now checks to see if an exception handler method exists that is compatible with DataBindingSourceCreationException.  If one does exist, the exception is thrown and will eventually be passed to that method instead of invoking the controller action.  If one does not exist, then fall back to populating the command object with an error and invoking the action.  That will allow applications to do something like this:
>
> class SomeController {
>      def someAction(SomeCommandObject sco) {
>          // your code...
>      }
>
>      // as of GRAILS-11453 if this is used in several
>      // controllers it could be defined in a trait...
>      def handleDataBindingException(DataBindingSourceCreationException e) {
>          // do whatever makes sense for your application here...
>          response.status = 400
>          flash.message = 'caught a DataBindingSourceCreationException’
>          render view: ‘/someViewWeUseForThisSortOfThing'
>      }
> }
>
> I think that it makes sense to offer applications the ability to deal with exceptions that are generated while processing command objects if they want to do so.  Some applications might want that sort of thing and for those who don’t, this isn’t imposing any complexity on them.  The code in the framework to support that is minimal.
>
> What say ye?
>
>
>
> JSB
>
>
> —
> Jeff Scott Brown
> <a href="javascript:" target="_blank" gdf-obfuscated-mailto="EUxuOoFLp80J" onmousedown="this.href='javascript:';return true;" onclick="this.href='javascript:';return true;">jbr...@...
>
> Find The Cause ~ Find The Cure
> <a href="http://www.autismspeaks.org/" target="_blank" onmousedown="this.href='http://www.google.com/url?q\75http%3A%2F%2Fwww.autismspeaks.org%2F\46sa\75D\46sntz\0751\46usg\75AFQjCNHLOilSQBYB1lzLN6Ms6K6DtQY5DQ';return true;" onclick="this.href='http://www.google.com/url?q\75http%3A%2F%2Fwww.autismspeaks.org%2F\46sa\75D\46sntz\0751\46usg\75AFQjCNHLOilSQBYB1lzLN6Ms6K6DtQY5DQ';return true;">http://www.autismspeaks.org/
>
>

--
You received this message because you are subscribed to the Google Groups "Grails Dev Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To post to this group, send email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/grails-dev-discuss/6fa8653c-86eb-4f55-b194-12b29c38f9ff%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Loading...