2009年8月16日星期日

RE: [fw-mvc] Exception handling with controller plugins

Ralph, I'm absolutely sure of this behavior. I think you guys gave me a workable solution to the basic problem, so if you aren't curious, stop reading here. If you are curious about this and want to know how to reproduce, read on below... :-) I'll add a bit more detail since you ask because I have traced it with Xdebug within Eclipse. I initially stumbled across this because some exceptions are handled differently, and normally I might see something like your typical exception page, like this:

However, in testing my ACLs I came across one case where the page actually rendered one of the admin forms, followed by a different exception output:

Scratching my head over this undesirable behavior, I spent a good bit of time stepping through the code one step at a time to try to figure out why different situations were handled differently. Both were tripped by a simple try/catch block that threw an exception so I didn't see any difference at first. The code was identical except for a different message. In stepping through, what I discovered is that at a high level, once thrown, the controller/action is still processed. After that the dispatch loop then essentially redirects to the error controller. Which then goes through a whole 'nuther round of the predispatch plugins, the ACL plugin included, this time dispatching to the errorController/errorAction.

What is different is that if the ACL plugin throws a second exception, then you get the former output. If the ACL plugin doesn't trip a second exception, you get the latter behavior. The reason why is that there is this little code block in Zend_Controller_Plugin_ErrorHandler

        if ($this->_isInsideErrorHandlerLoop) {
           
$exceptions = $response->getException();
           
if (count($exceptions) > $this->_exceptionCountAtFirstEncounter) {
               
// Exception thrown by error handler; tell the front controller to throw it
               
$frontController->throwExceptions(true);
               
throw array_pop($exceptions);
            }
        }

If there is only one exception logged in the Response object, then the error is handled inline within the page, so you get the controller/action rendered followed by ErrorController/errorAction being rendered, and the stack trace. But if there are multiple exceptions logged in the Response object, then it kicks out to the Front Controller and you see the former type of error output.

You can recreate this on your own with a simple bit of code. Create a contoller plugin and create a simple if/else block that looks something like this:

        if ('error' != $request->getControllerName()) {
            // some statement that will generate an exception
        } else {
            //do something else
        }

Then set breakpoints in your adminController/action (or whatever), in the errorController/errorAction, and also in the ACL plugin, as well as Zend_Controller_Plugin_ErrorHandler, and observe. This example may seem a little contrived, but I'm constructing dynamic ACLs in my plugin based on the request, so it is easily possible for the ACL to not generate an exception the second time when it is attempting to dispatch to the error controller. After that, then compare results by removing the if/else block in the ACL plugin and simply having some statement that always generates an exception.

Regards,

Seth

-----Original Message-----
From: Ralph Schindler [mailto:ralph.schindler@zend.com]
Sent: Sunday, August 16, 2009 3:46 PM
To: Atkins, Seth (RICH1:5278)
Cc: fw-mvc@lists.zend.com
Subject: Re: [fw-mvc] Exception handling with controller plugins

> I'm curious if anyone has a workaround for this issue. I'm using ACLs
> within a controller plugin, and this is important to my design since I
> want the ACLs to be checked prior to actual dispatch…that way I can
> essentially redirect based on permissions to other
> module/controller/action by simply modifying the request object. I'd
> like to keep this basic design, but I've found a fairly significant

This is a pretty common use case (ACL's and module/controller/action's being treated as ACL Resources) so much so that there is even a proposal for integration into ZF for it:

http://framework.zend.com/wiki/pages/viewpage.action?pageId=39025&showComments=true#comments

> limitation. If the plugin encounters any kind of exception, the plugin
> execution is halted and then the app continues on….which ultimately
> means that the controller/action is also processed and rendered. The
> significance of this problem is that if the exception occurs prior to
> the "isAllowed" method of the ACL, then the permissions are NEVER checked.

I am actually not seeing the issue here.  Assuming that 'throwException'
is set to false, and a plugin throws an uncaught exception, this effectively means that plugins would continue to process, but the final plugin the ErrorHandler (assuming it too has not been disabled), should change the request to point to the default module, error controller / error action.

Generally, any exceptions throw inside the Front Controller typically mean your application should return status code 500.  Exceptions being throw anywhere in the controller system that are not legitimately caught and handled properly SHOULD force the application to the ErrorController and display an error page.


> to this problem. What I want is for the plugin to continue execution
> even if an exception is encountered so that the $acl->isAllowed()
> method

This generally means that your ACL plugin should probably have its own try/catch block.  Since you want the system to continue running, it also sounds like you have a remedy for the "exceptional behavior" that you are encountering.  Your remedy would effectively be the code in the "Catch" side of the block.

> access the admin area and if an exception was encountered during ACL
> creation, the admin page renders whether or not they actually have

This should not be happening.  Exceptions during ACL creation should be throwing an exception, that would force the ErrorController to dispatch.
  Are you sure the ErrorHandler plugin is running last?

ACL's not being created correctly are effectively the same as the database connection not being available on database driven sites.  They should force a 500 error page to be shown to the user, and perhaps send off an email to the administrator.

-Ralph

没有评论: