[CLOSED] Global exception handling - wrong response or no failure

  1. #1

    [CLOSED] Global exception handling - wrong response or no failure

    Hello guys,

    since some time I encounter a very strange behaviour handling exceptions on client-side.

    I have attached a demo project which illustrates my problem.

    There are two buttons on the default page. If you click on the first button, an exception is being thrown within the assigned AjaxEvent. The second button does exactly the same, the only difference is that not an AjaxEvent but an AjaxMethod is invoked by the listener assigned which then throws an exception too.

    What i want to achieve is that wherever (AjaxMethod, AjaxEvent or page event) an exception occurs a message dialog is going to be displayed on the user interface to inform the user about the exception. To display an easier to understand error dialog I`ve overriden Coolite`s one as suggested in several threads here. It displays full stack information if the project is built within debug mode otherwise it only displays the error message itself without any stack information.
    A main goal is no need to implement any special behaviour needed to catch the exception and display it (like "Coolite.ScriptManager.Success = false" or such things).

            protected string GetExceptionHandlerScript(string onRequestFailureScript)
            {
                StringBuilder script = new StringBuilder();
    
                // overriding default exception handler here (AjaxEvent, AjaxMethod and Server-side event) to display a custom "beautified" error dialog (with full exception stack if in debug mode otherwise only displaying plain error message)
    
                script.Append(@"Coolite.AjaxEvent.override({
                                    showFailure: function(response,errorMsg) {");
                script.Append("alert('error');");
                script.Append("if (Ext.isEmpty(errorMsg)) errorMsg = Ext.decode(response.responseText).errorMessage;");
                script.Append("var stack0 = errorMsg.indexOf('\\r\\n');");
                script.Append("var stack1 = errorMsg.length;");
                script.Append("var desc0 = errorMsg.indexOf(':') + 1;");
                script.Append("var desc1 = stack0;");
                script.Append("Ext.Msg.show({");
                script.Append("title: 'Error',");
                script.Append("msg: errorMsg.substring(desc0, desc1),"); // error message
                script.Append("value: errorMsg.substring(stack0, stack1).trim(),"); // display stack
                script.Append("buttons: Ext.MessageBox.OK,");
    
    #if (DEBUG)
                script.Append("multiline: true,");
    #else
                    script.Append("multiline: false,"); // ==> "value" textfield is hidden
    #endif
    
                script.Append("width: 500,");
                script.Append("icon: Ext.MessageBox.ERROR");
                script.Append("});");
    
                if (!String.IsNullOrEmpty(onRequestFailureScript))
                    script.Append(onRequestFailureScript);
    
                script.Append("return false;");
    
                script.Append(@"}});");
    
                return script.ToString();
            }
            [AjaxMethod]
            public void ThrowExceptionMethod()
            {
                throw new Exception("test");    
            }
    
            protected void ThrowExceptionEvent(object sender, AjaxEventArgs e)
            {
                throw new Exception("test");
            }
    However what happens is very strange.

    While a click on the first button causes an unexpected response to be sent to the browser (plain Html instead of JSON including the error message), clicking on the second button returns the JSON data as expected, but the error dialog never gets displayed (success = false within response, but error event of Coolite is never invoked i think).

    I have this problems with both FF 3 and IE 8 using Coolite 0.8.2.19471 from your repository. I know some time ago it worked perfectly.

    If you can not reproduce this problems i will post some screenshots here.

    Can you please give me any suggestions on this ?

    Thank you !

    Greets,
    Peter
    Attached Files
    Last edited by geoffrey.mcgill; Jul 13, 2010 at 5:35 PM.
  2. #2
    Hi,

    AjaxEvents is never intercepted (it is always unhandled exception if you don't handle exception inside AjaxEvent handler). Therefore response is standard ASP.NET error page. I can suggest to handle exception in the AjaxEvent handler
    protected void ThrowExceptionEvent(object sender, AjaxEventArgs e)
            {
                try
                {
                    throw new Exception("456");
                }
                catch (Exception ex)
                {
                    Coolite.Ext.Web.ScriptManager.AjaxSuccess = false;
                    Coolite.Ext.Web.ScriptManager.AjaxErrorMessage = ex.ToString();
                }
            }
    AjaxMethods exceptions are always intercepted (because ajax method has different life cycle) but you must add failure handler to handle the error on the clint side
    Coolite.AjaxMethods.ThrowExceptionMethod({
               failure: function(error){
                    Ext.Msg.alert('Error', error);
               }
    });
  3. #3

    Finally worked out

    Hi vlad,

    thanks for your answer. I know that it`s basically not supported by Coolite (0.8) to automatically display error messages when unhandled exceptions occur.

    But because i have a lot of existing AjaxEvents and AjaxMethods (100 - 200) it would be a big pain in the neck to implement special exception handling logic for each of them.

    Finally i ended up in the following workaround/hack, which works for me. Regardless of where the exception occurs it displays an easier to understand message box with some details about the exception (if in debug full stock otherwise only error message) to the user.

    Here is how i did it (modify demo app):

    Default.aspx
    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Ilogs.Coolite.Sample.Default" %>
    
    <%@ Register Assembly="Coolite.Ext.Web" Namespace="Coolite.Ext.Web" TagPrefix="ext" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head id="Head1" runat="server">
        <title></title>
    </head>
    <body>
        <ext:ScriptManager ID="ScriptManager2" runat="server" Locale="de-DE" RethrowAjaxExceptions="true"  />
        
        <form id="form1" runat="server">
        <div>
            <ext:Button runat="server" Text="Event">
                <AjaxEvents>
                    <Click OnEvent="ThrowExceptionEvent"></Click>
                </AjaxEvents>
            </ext:Button>
            
            <ext:Button runat="server" Text="Method">
                <Listeners>
                    <Click Handler="Coolite.AjaxMethods.ThrowExceptionMethod()" />
                </Listeners>
            </ext:Button>
            
            <ext:Button runat="server" Text="Store">
                <Listeners>
                    <Click Handler="#{testStore}.reload();" />
                </Listeners>
            </ext:Button>
            
            
            <ext:Store runat="server" ID="testStore" OnRefreshData="ThrowExceptionStore">
            </ext:Store>
        </div>
        </form>
    </body>
    </html>
    Default.aspx.cs
            protected void Page_Load(object sender, EventArgs e)
            {
                ScriptManager.GetInstance(this).Listeners.AjaxRequestException.Handler = GetExceptionHandlerScript(String.Empty);
                ScriptManager.GetInstance(this).RegisterClientScriptBlock("Localization", "Coolite.AjaxEvent.override({showFailure: " + GetExceptionHandlerScript(String.Empty) + "});");
    
                //ScriptManager.GetInstance(this).RegisterClientScriptBlock("Localization", GetExceptionHandlerScript(String.Empty));
            }
    
            [AjaxMethod]
            public void ThrowExceptionMethod()
            {
                throw new Exception("123");    
            }
    
    
            protected string GetExceptionHandlerScript(string onRequestFailureScript)
            {
                StringBuilder script = new StringBuilder();
    
                // overriding default exception handler here (AjaxEvent, AjaxMethod and Server-side event) to display a custom "beautified" error dialog
    
                script.Append(@"function(response,errorMsg) {");
                script.Append("errorMsg = !Ext.isEmpty(response.responseText) && response.responseText.charAt(0) == '{' ? response.responseText : errorMsg.errorMessage;");
                script.Append("if(errorMsg.charAt(0) == '{') errorMsg = Ext.decode(errorMsg);");
                script.Append("if(!Ext.isEmpty(errorMsg.errorMessage)) errorMsg = errorMsg.errorMessage; else if(!Ext.isEmpty(errorMsg.serviceResponse)) errorMsg = errorMsg.serviceResponse.Msg;");
                script.Append("var stack0 = errorMsg.indexOf('\\r\\n');");
                script.Append("var stack1 = errorMsg.length;");
                script.Append("var desc0 = errorMsg.indexOf(':') + 1;");
                script.Append("var desc1 = stack0;");
                script.Append("Ext.Msg.show({");
                script.Append("title: 'Error',");
                script.Append("msg: errorMsg.substring(desc0, desc1),"); // error message
                script.Append("value: errorMsg.substring(stack0, stack1).trim(),"); // display stack
                script.Append("buttons: Ext.MessageBox.OK,");
    
    #if (DEBUG)
                script.Append("multiline: true,");
    #else
                script.Append("multiline: false,"); // ==> "value" textfield gets hidden
    #endif
    
                script.Append("width: 500,");
                script.Append("icon: Ext.MessageBox.ERROR");
                script.Append("});");
    
    
                if (!String.IsNullOrEmpty(onRequestFailureScript))
                    script.Append(onRequestFailureScript);
    
                script.Append("return false;");
    
                script.Append(@"}");
    
                return script.ToString();
            }
    
            protected void ThrowExceptionEvent(object sender, AjaxEventArgs e)
            {
                throw new Exception("456");
            }
    
            protected void ThrowExceptionStore(object sender, StoreRefreshDataEventArgs e)
            {
                throw new Exception("789");
            }
    I needed to add a global app handler to my project which modifies the response to prepare the exception text.

    Global.asax.cs
        public class Global : System.Web.HttpApplication
        {
            protected void Application_Error(object sender, EventArgs e)
            {
                // do not forget to register ExtHelper`s exception handler on every page (ExtHelper.Exception.SetExceptionHandling)!
                bool transmitStack = false;
                string exceptionText;
    
                HttpContext ctx = HttpContext.Current;
                Exception exception = ctx.Server.GetLastError();
    
                // outer exception is always an http exception so we need the inner exception which originally caused the exception
                if (exception.InnerException != null)
                    exception = exception.InnerException;
    
                // if an exception occured before page was rendered (response.Length == 0) we need to return plain html including 
                // information about the error as response because Coolite`s error handler displays this html response within it`s error window by default(=> response is
                // content of error window, which itself can`t be changed)
                string plainTemplate =
                    @"<table>
                    <tr>
                        <td colspan='2'>&nbsp;</td>
                    </tr>
                    <tr>
                        <td colspan='2' style='color: #FF0000; font-weight:bold; font-size:18px;'>{0}</td>
                    </tr>
                    <tr>
                        <td colspan='2' style='font-weight:bold; font-size: 16px;' >{1}</td>
                    </tr>
                    <tr>
                        <td colspan='2'>&nbsp;</td>
                    </tr>
                    <tr>
                        <td style='font-weight:bold; font-size:12px; visibility:{2}'>Stack:</td>
                        <td style='font-size: 11px; visbility:{2}'>{3}</td>
                    <tr/>
                    <tr>
                        <td style='font-weight:bold; font-size:12px;'>Adresse:</td>
                        <td style='font-size: 11px;'>{4}</td>
                    <tr/>
                </table>";
    
    #if DEBUG
                            transmitStack = true; // only show stack if in debug mode
    #endif
    
                if (HttpContext.Current.Request.TotalBytes == 0)
                    exceptionText = String.Format(plainTemplate, "Ein Fehler ist aufgetreten:", exception.Message, transmitStack ? "visible" : "hidden",
                        exception.StackTrace.Length > 400 ? exception.StackTrace.Substring(0, 400) : exception.StackTrace, ctx.Request.Url); // send plain html to display within`s Coolite default error window
                else
                    // if page was already rendered, we need to return a json response with full error message
                    // the error message then gets parsed in client-side ExtJs error handler (AjaxFailure listener of ScriptManager) and is displayed (formatted) within a message box
                    // the reason why we have to serialize FULL error message (including stack etc.) here is that there are two sorts of how an exception is handled by ExtJs/ASP
                    // 1) exceptions which are thrown within an AjaxMethod do not trigger global applications OnError-Handler so this part of code is NEVER executed in this case -> ExtJs transmits the full exception message 
                    //    to the client side error handler as parameter automatically then
                    // 2) an exception is thrown within an AjaxEvent, this part of code is executed
                    // Because both cases may happen we need to "emulate" case 1) here if case 2) happened
                    exceptionText = new JavaScriptSerializer().Serialize(new { errorMessage = exception.ToString() });
    
                ctx.Response.ClearContent();
                ctx.Response.StatusCode = 500;
                ctx.Response.StatusDescription = exception.Message;
                ctx.Response.Write(exceptionText);
                ctx.Response.Flush();
                ctx.Response.End();
            }
        }
    I know that this solution is a very tricky hack but it does it`s job for me in all situations i needed. Maybe anyone other find this useful too...

    Thanks vlad !

    Thx,
    Peter

Similar Threads

  1. [CLOSED] Javascript Global Error Handling
    By adelaney in forum 2.x Legacy Premium Help
    Replies: 1
    Last Post: Jun 20, 2012, 8:30 PM
  2. Global Error Handling
    By wdk in forum 1.x Help
    Replies: 2
    Last Post: Apr 03, 2012, 2:34 AM
  3. [CLOSED] Global Exception Handling How to?
    By ISI in forum 1.x Legacy Premium Help
    Replies: 1
    Last Post: May 11, 2011, 11:57 AM
  4. [CLOSED] [1.0] Global error handling of DirectMethods
    By jchau in forum 1.x Legacy Premium Help
    Replies: 6
    Last Post: Jul 27, 2010, 7:59 PM
  5. Replies: 4
    Last Post: Sep 17, 2009, 7:45 AM

Tags for this Thread

Posting Permissions