0
votes

I have read a lot about error handling in MVC (specifically about applying a HandleError attribute and about using the Application_Error on the Global.asax). I am interested in gracefully handling the following types of exception:

  • Exceptions thrown inside the controller
  • Exceptions thrown when performing binding of data in the view.

Currently my application behaves in the following way

  1. All exceptions thrown in the controller are unhandled. They reach the Application_Error method of the Global.asax
  2. All exceptions in the view are unhandled and reach the Application_Error method of the Global.asax
  3. Once in the Application_Error method, I log the exception, decide if the application is run locally or remotely. If so, I present the yellow screen to the user, or perform a Response.Redirect to custom error pages.

This logic works correctly for errors thrown inside controllers that render parent views or the parent view itself. The downside of this logic is that, when an error is thrown inside a Child Action which should render a PartialView the whole page becomes unusable. This because the yellow error screen or the custom error page occupies the complete page and wont allow the user to view the other sections of the webpage.

What I want to do/know is if it is possible to:

  1. Display the yellow error screen inside of a partial view but render the rest of the page correctly.
  2. Redirecting the user to a partial view error page that allows for the rest of the page to remain usable.
1
I got similar setup and never had any issues. We use partial view all around. So curious to know if you can provide an example code which I can test with my setup here for "when an error is thrown inside a Child Action which should render a PartialView the whole page becomes unusable."? - SBirthare
I think you can see the answer in your settings, global.asax is global to the application, if you want to handle an error inside the application you will need to change you application settings, and set up an error page that use your masterpage and plug it in a location depending on what you want to the client to have access to. You can have a partial view that renders a message etc.... - Jack M
@SBirthare just add the throw new Exception() line of code anywhere inside your controller action or inside the partial view and see what happens. @JackM I allready have that in place. The global error page is already defined and the user gets redirected to it. So, the entire page is substituted by my custom screen - Luis Becerril
Will try tomorrow but pretty sure its not so unusual case which we missed. I expect it to be handled gracefully by my existing configuration. - SBirthare
@LuisBecerril - I checked in my application, we handle such case gracefully. I have added information in my answer. Hope this will help. - SBirthare

1 Answers

0
votes

To handle this specific case:

"when an error is thrown inside a Child Action which should render a PartialView the whole page becomes unusable."

You can define a global Ajax event handler for "ajaxError" to handle exception thrown from an action that is called using "@Html.Partial".

Apart from handling all errors in "Application_Error" we have defined following global handler in _Layout.cshtml. This basically handle cases where a partial view was invoked from html and the action threw an exception. If an error is encountered, data is logged and toastr popup message is displayed.

_Layout.cshtml:

    //Ajax Global Event Loader
    globalVar: ajaxContainer = $('div[id=wrap]');
    $(document).bind("ajaxSend", function () {
        if ($('.k-loading-mask').is("visible") == false) {
            var loader = new ajaxLoader(ajaxContainer, { bgColor: '#fff', duration: 800, opacity: 0.3, classOveride: false });
        }
    }).bind("ajaxComplete", function () {
        if ($('div[class=ajax_overlay]').is(':visible') == true)
            $('div[class=ajax_overlay]').remove();
    }).bind("ajaxError", function (event, jqxhr, settings, thrownError) {
        //debugger;
        $('div[class=ajax_overlay]').remove();
        $('.k-loading-image').remove();
        if ((settings.url.indexOf('Notification/GetActiveNotificationByUserName') < 0)
            && (settings.url.indexOf('LogError/LogData') < 0)) {
            var errorData = 'URL: ' + settings.url + '; Type: ' + settings.type + '; Data: ' + settings.data + '; thrownError: ' + thrownError;
            var model = { errorData: errorData };

            $.ajax({
                url: '@Url.Action("LogData", "LogError")',
                contentType: 'application/json; charset=utf-8',
                type: 'POST',
                dataType: 'html',
                data: JSON.stringify(model)
            })
            .success(function (result) {
                $("div.overlay").hide();
            })
            .error(function (xhr, status) {
                $("div.overlay").hide();
                //alert(status);
            });

            // Set toastr for error notification and display error 1 at a time
            toastr.options.closeButton = true;
            toastr.options.positionClass = 'toast-top-full-width';
            toastr.options.showMethod = 'slideDown';
            toastr.options.hideMethod = 'slideUp';
            toastr.options.showDuration = '1000';
            toastr.options.hideDuration = '1';
            toastr.clear();
            toastr.error('Your request cannot be processed right now. Please try again later!');
        }

        $("div.overlay").hide();
    });