Hi,
So when an exception is thrown and later handled by CErrorHandler::handleException(), then if it is a CHttpException (or YII_DEBUG is not true), we have the option of displaying the error using CErrorHandler::errorAction. This is good, because it lets us decide for ourselves if we want to display CHttpExceptions via a custom view or using the full exception display.
However, if the request is an AJAX one, then unconditionally the display of the exception is done in the full display way. Here are lines 178-183 in CErrorHandler:
if($this->isAjaxRequest())
$app->displayException($exception);
else if($exception instanceof CHttpException || !YII_DEBUG)
$this->render('error',$data);
else
$this->render('exception',$data);
This gives the result that we cannot easily choose to display CHttpExceptions using a custom view for AJAX requests, like we can for non-AJAX requests. I am wondering what the reasoning behind this is?
Say for example that in an action I determine that the request was done in an incorrect way, like in the default webapp’s loadModel().
The normal way to handle this is to throw a CHttpException(400, “You should’t attempt this”), and if the request is a non-AJAX one, this will normally be displayed using the errorAction (usually SiteController::actionError).
This is a fine solution since the exception we are throwing is a “controlled one”, and what I mean by that is that it isn’t like a totally unexpected CException/Exception coming from somewhere else, and that shouldn’t have happened under any circumstances. I.e. we wish to display it in a controlled way.
However, this is impossible to do for an AJAX request, because CErrorHandler unconditionally renders the whole exception regardless of its type and/or what YII_DEBUG is set to. I consider this a problem because it would be very neat to do this in the JS code requesting the action:
jQuery.ajax(url, {
'data': ...
'success': function(result){
// Do stuff here on success.
},
'error': function(jqXHR){
// If the server returned a non-success HTTP code (which is usually the case when
// we throw CHttpException), this is considered a failure by jQuery, this callback
// is used instead of 'success', and we can/could use this to display the returned
// error message.
// This is where the problem lies though, because we always get a huge load of
// exception display instead of just the result of a custom view if the exception
// thrown was a CHttpException. There is no easy way to display the result of a
// custom view for the exception in this case.
// If the readyState is 0 it's most likely due to the user aborting the request. In that case do nothing.
if(jqXHR.readyState != 0)
{
// Display an error message to the user.
}
}
});
If CHttpExceptions (or other exceptions when YII_DEBUG is not true) were possible to display using a custom view/action for AJAX requests as well, we would be able to do the above, instead of on the server side having to add code that deals with AJAX/non-AJAX (catch the exception thrown and IF the request is AJAX return a response that the client-side JS code then inspects and determines if it’s an error inside the ‘success’ function in the code example above, and if it is displays the error).
Ok, 'nuf said. Am I missing something obvious here? Doesn’t what I say make sense?
Thanks!
Edit: I can indeed just override some stuff to handle this the way I want, but this has downsides of its own, and until I’m smart enough to see why AJAX requests are handled this specifically by CErrorHandler I’d like to discuss if it would be a good idea to change this.
Edit: MrSoundless kindly pointed out that CApplication::displayException(), which is what is used to output the exception when the request is an AJAX one, only outputs full stack trace and file information when YII_DEBUG is not true. This is accurate, but I argue that it still outputs more info than one might want, but foremost the base "problem" still exists in that you cannot at all control how the exception is rendered, like you can for non-AJAX requests.