Intercepting Exceptions

Coordinator
May 3, 2012 at 11:16 PM
Edited May 3, 2012 at 11:20 PM

I received a request for better support around exceptions from And3rson.

I have begun working on adding some visibility into the exceptions that occur. As of todays source code, you can do the following.

namespace JsonRpcTest{
    public class Global : System.Web.HttpApplication {
        // Start services
        static object[] services = new object[] {
           new TestServer.HelloWorldService(),
           new TestServer.TestService(),
           new AustinHarris.JsonRpc.MetadataService()
        };        

        protected void Application_Start(object sender, EventArgs e) {
            // Intercept exceptions
            AustinHarris.JsonRpc.Config.SetErrorHandler(OnJsonRpcException);
        }

        private JsonRpcException OnJsonRpcException(JsonRequest rpc, JsonRpcException ex)
        {
            // Log, or transform
            return ex;
        }
}
}

The method OnJsonRpcException will be called anytime an exception is throw from within one of your [JsonRpcMethod]'s, It will also be called if there is an exception when trying to invoke one of your [JsonRpcMethod]'s. However it will not be called when there is a malformed request to the server.

Additionally, you can now do the following:

        [JsonRpcMethod]
        private string throwsException2(string s)
        {
            throw new JsonRpcException(-27000,"This is will be mapped to the JsonRpc error object", null /*Any data can go here*/);
            return s;
        }

This will allow you to explicitly set the error that is returned through the JSON-RPC response. 

 

 

 

 

May 10, 2012 at 9:57 AM
Edited May 10, 2012 at 10:03 AM

Thank you for a prompt action taken.

I have followed the guide lines and done the following:

public class Global : System.Web.HttpApplication
{
    // Start services
    private static MyWebService = new MyWebService(); // Why do we need this static reference ?

    protected void Application_Start(object sender, EventArgs e) {
        // Intercept exceptions
        AustinHarris.JsonRpc.Config.SetErrorHandler(OnJsonRpcException);
    }

    private JsonRpcException OnJsonRpcException(JsonRequest rpc, JsonRpcException ex)
    {
        /* Code where I log unhandled exception */
        return ex; // What is the meaning of returned exception?
    }


Here I have asked myself a question : Why do we need a static reference to the MyWebService instance? I guess this is the single instance that JsonRpcHandler uses. JsonRpcHandler implements IHttpAsyncHandler and is specified in <handlers> section in web.config. Is this single instance found via reflection?
This was just a side question. My itch is exception handling.

In JsonRpcService:

 

[JsonRpcMethod]
private string MyMethod(string a)
{
try
{
/* do stuff that should be done */
}
catch (Exception e)
{
 /* Log exception */
 // This is the trick to put error message in error object throw new JsonRpcException(-27000,"This is will be mapped to the JsonRpc error object", null /*Any data can go here*/); return s; // this code is unreachable so I cannot return a value } } }

 
I have caught the exception and  I have handled it. JsonRpc by it self sets the result object with null, which is ok. But, I have handled the exception, logged it but I need to raise another exception to put my message in the error object. This new exception now propagates to the handler defined in global.asax and there gets handled again (eg. logged twice). This is not ok. Of course one could check the exception for type, code or message and filter in global.asax but this seems wrong from a concept point of view. Those are just my thoughts.

Coordinator
May 10, 2012 at 4:56 PM
Edited May 10, 2012 at 5:00 PM
I have added comments to your post. I hope that I was able to come across clearly. 

and3rson wrote:

Thank you for a prompt action taken.

I have followed the guide lines and done the following:

public class Global : System.Web.HttpApplication
{
    // Start services
    private static MyWebService = new MyWebService(); // Why do we need this static reference ? 
    /* we need the static reference you could have multiple instances of Global created, and as the creation of MyWebService calls Handler.Current.Register for each method in our service that is attributed with [JsonRpcMethod]. If you did not name it a static instance, you would end up with Register being called many times for the same delegate. You could do this manually if wanted, but you can only register a delegate for a given JsonRpcMethod name once. So it only makes sense to use a static class, or a single instance. We are using a single instance here first to allow it to register before any calls come in. This is the earliest place we could register our services, and it prevents us from attempting to register our JsonRpcMethods multiple times. */
    protected void Application_Start(object sender, EventArgs e) {
        // Intercept exceptions
        AustinHarris.JsonRpc.Config.SetErrorHandler(OnJsonRpcException);
    }

    private JsonRpcException OnJsonRpcException(JsonRequest rpc, JsonRpcException ex)
    {
        /* Code where I log unhandled exception */
        return ex; // What is the meaning of returned exception?
	/* the return value of this method is the exeception that will be returned as the error property of the JsonRpc result. This method serves two purposes. You can add error logging to all of your JsonRpcMethods in one location. This is similar to Global.Application_Error in this regard. This is where I would add failure logging. The second this this method lets you do it to have a chance to change the error that is returned to the client. The inherent behavior is that *any* exception throw from one of your JsonRpcMethods will end up getting wrapped into a JsonRpcException before it is returned from the client. If you have unhandled excpetions being thrown from a jsonRpcMethod and you want to make it so the client does not receive the stack trace, or Watson buckets you could do that here. This makes it so that you do not have to add a try()catch() block to each of your JsonRpcMethods to control what the client receives. */
    }


Here I have asked myself a question : Why do we need a static reference to the MyWebService instance? I guess this is the single instance that JsonRpcHandler uses. JsonRpcHandler implements IHttpAsyncHandler and is specified in <handlers> section in web.config. Is this single instance found via reflection? 

--- There is some reflection involved in registering the handlers, but the JsonRpcService base class doesn't need to use reflection to find the instance, as it can just use 'this' to get a handle to it.

This was just a side question. My itch is exception handling.

In JsonRpcService:

 

[JsonRpcMethod]
private string MyMethod(string a)
{
    /* I would also against putting a large try{}catch{} in your JsonRpcMethods. */
    try
{
/* do stuff that should be done */
}
catch (Exception e)
{
/* I would recommend against doing exception logging here. Use the OnJsonRpcException method that you registered in Global to do that. It will catch everything. */
        // This is the trick to put error message in error object
	/* the only reason that I would explicitly throw a JsonRpcException is that you have a special case error that you want to communicate to the client. It is not required to throw your own JsonRpcException to get a general exception to be returned in the JsonRpc response. */ 
        throw new JsonRpcException(-27000,"This is will be mapped to the JsonRpc error object", null /*Any data can go here*/);
        return s; // this code is unreachable so I cannot return a value }
    }
}
I have caught the exception and  I have handled it. JsonRpc by it self sets the result object with null, which is ok. But, I have handled the exception, logged it but I need to raise another exception to put my message in the error object. This new exception now propagates to the handler defined in global.asax and there gets handled again (eg. logged twice). This is not ok. Of course one could check the exception for type, code or message and filter in global.asax but this seems wrong from a concept point of view. Those are just my thoughts.

 

If you need any examples, and I get some for you.

May 14, 2012 at 11:19 AM
Edited May 14, 2012 at 2:31 PM

Thank you for a reply. It helped me clear things a bit.

One reason why I have put the method body inside try-catch block was to catch exceptions before handler JsonRpcException OnJsonRpcException() was introduced.

Another reason is: I might be expecting an exception and in case it emerges I would like to catch it there and log it together with method name and some other data from app. Example might be Sql timeout exception which shouldn't emerge if all is well but it does sometimes. So I would like to know about that. The method name and other data from my app logic (method) are not accessible in OnJsonRpcException().

The top level exception catching is useful for those exceptions I have not considered yet. Those point to weaknesses in my application that should be addressed in future.

I am also thinking about another way to fill error object without raising an exception. I might have an error which does not originate from exception but from pure app logic. I just don't like to raise an exception because they are costly and not my tenet I guess.

Exception ex passed in OnJsonRpcException() has stack trace set to null. Although stack trace of originating request is available as data member I think it would be better to have Access to inner exception (origin) and from there to stack trace.

I welcome the release of a new build. It shows that this project is alive.

Coordinator
May 14, 2012 at 10:39 PM
Edited May 15, 2012 at 1:06 AM

I'm thinking about adding an optional parameter to the method signature for exceptions along with the one for context. It will likely require the JsonRpcMethod attribute to reference it.

Inital proof of concept is working.

Invoked with :

{"method":"testFloat","params":[4832.90940 ],id:1}
        [JsonRpcMethod]
        private List<string> testFloat(float input,ref JsonRpcException ex)
        {
            ex = new JsonRpcException(-1, "testing ref exception", new { fun = "this is fun", data="any data really" });
            return new List<string>() { "one", "two", "three", input.ToString() };
        }
Returns:

 

{"jsonrpc":"2.0","error":{"code":-1,"message":"testing ref exception","data":{"fun":"this is fun","data":"any data really"}},"id":1}
 

 

Coordinator
Jun 15, 2012 at 1:51 AM

I have checked in code to support returning exceptions through and optional ref parameter. It must be the last parameter of the method, and it must be typed as a JsonRpcException.