Question What is the difference between CustomExceptionFilter and GlobalExceptionHandler?

raysefo

Well-known member
Joined
Feb 22, 2019
Messages
361
Programming Experience
10+
Hi,

In my asp.net web API, I am using both GlobalExceptionHandler and CustomExceptionFilter. I think they do the same job, hiding the actual error from the client and send them "HTTP 500 - Internal Server Error. Please Contact your Administrator." response. I wonder if I remove GlobalExcepitonHandler and add Nlog into CustomExceptionFilter, can I still send the same response and log errors? What are your opinions?

UnhandledExceptionLogger can't catch all the errors that's why I would like to add logging inside of one of them.

CustomExceptionFilter:
C#:
public class CustomExceptionFilter : ExceptionFilterAttribute
    {

        public override void OnException(HttpActionExecutedContext actionExecutedContext)
        {
            base.OnException(actionExecutedContext);


            var response = new HttpResponseMessage(HttpStatusCode.InternalServerError)
            {
                //Content = new StringContent("An unhandled exception was thrown by service."),
                ReasonPhrase = "HTTP 500 - Internal Server Error. Please Contact your Administrator."
            };

            actionExecutedContext.Response = response;
        }
    }

GlobalExceptionHandler:
C#:
public class GlobalExceptionHandler : ExceptionHandler
    {
        
        public override void Handle(ExceptionHandlerContext context)
        {
            var result = new HttpResponseMessage(HttpStatusCode.InternalServerError)
            {
                Content = new StringContent("HTTP 500 - Internal Server Error. Please Contact your Administrator."),
                ReasonPhrase = "Exception"
            };

            
            context.Result = new ErrorMessageResult(context.Request, result);
        }

        public class ErrorMessageResult : IHttpActionResult
        {
            private HttpRequestMessage _request;
            private readonly HttpResponseMessage _httpResponseMessage;

            public ErrorMessageResult(HttpRequestMessage request, HttpResponseMessage httpResponseMessage)
            {
                _request = request;
                _httpResponseMessage = httpResponseMessage;
            }

            public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
            {
                return Task.FromResult(_httpResponseMessage);
            }
        }

      }
UnhandledExceptionFilter:
C#:
public class UnhandledExceptionLogger : ExceptionLogger
    {
        private static readonly Logger logger = LogManager.GetCurrentClassLogger();

        public override void Log(ExceptionLoggerContext context)
        {
            var timestamp = DateTime.UtcNow;

            //NLOG
            NLog(logger, context.Exception, timestamp);
        }

        private void NLog(Logger logger, Exception message, DateTime timestamp)
        {
            var sb = new StringBuilder();
            sb.AppendLine(message.ToString());
            sb.AppendLine(timestamp.ToLongDateString());
            logger.Error(sb.ToString());
        }
    }
 
No, you don't debug while load testing at first, you would simply run a load test without your debugger being attached, and when your application crashes. Check your log for the line it crashed on. Then wrap that line in a try catch, and in the catch block, you would place a break point on the catch (exceptiontype ex) { opening bracket. Then debug your code and run the load tester again with the debugger attached, and when it hits the same error. You should be able to identify the cause.
Instead of debugging, Can I log IsCancellationRequested here?
You would use IsCancelationRequested to check if a cancel request was called in/from your code. It returns a bool value, and requests a token. You can see how its used here CancellationTokenSource Class (System.Threading)
 
Last edited:
@Sheepings I got this, what does it mean?

2019-08-22 23_06_56-Game (Debugging) - Microsoft Visual Studio.png
 
It means something is cancelling your operations/tasks. Welcome to parallel programming.
make sure your HttpClient is not being disposed of while using async/await. A quick check shows that this error can arise if the client is disposed of.
Check all your logic, and trace where the call to cancel initiated.
IsCancellationRequested property returns true, the task interprets this as acknowledging cancellation and transitions to the Canceled state. If you do not use a Wait or WaitAll method to wait for the task, then the task just sets its status to Canceled.
You can find the answer to that in the documentation for task cancellation Task Cancellation

Hope this helps resolve this one.
 
Thank you @Sheepings for your suggestions.

Check all your logic, and trace where the call to cancel initiated.
The thing is I am not calling any cancellation token. If I am calling a cancellation token, It would be in the method signature, right? But there is no code like below.

C#:
public static async Task<HttpResponseMessage> CallRazer(GameRequest gameRequest, string url, [B][I]CancellationToken cancellationToken[/I][/B])
        {
[B]            [/B]FormUrlEncodedContent content = null;

            if (url == "Product/")
            {
                try
                {
                   
                    //Timeout
                    _httpClient.Timeout = TimeSpan.FromMinutes(3);
                    //Call Game Sultan
                    var response = await _httpClient.PostAsync("https://teststore.gamesultan.com/" + url, content,[B] [I]cancellationToken[/I]);
                    [/B]return response;
                }
                catch (Exception e)
                {...
                   
                    //code here

I think there is only one option which is disposal of http client. But how can it be disposed? Next time when break point hits, I will check the httpclient.

Is this the proper way to use http client?

C#:
public class UtilitiesTest
    {
        private static readonly HttpClient _httpClient = new HttpClient();


        public static GameRequest CreateSignature(GameRequestDto game, GameServicesTest.RequestType type)
        {
        //some code
        }

        public static string GetMd5Hash(HashAlgorithm md5Hash, string input)
        {
         //some code   
        }

        public static async Task<HttpResponseMessage> CallRazer(GameRequest gameRequest, string url)
        {
            FormUrlEncodedContent content = null;

            if (url == "Product/")
            {
                try
                {
                    //some code
                    //Timeout
                    _httpClient.Timeout = TimeSpan.FromMinutes(5);
                    //Call Game Sultan
                    var response = await _httpClient.PostAsync("https://teststore.gamesultan.com/" + url, content);
                    return response;

                    
                }
                catch (Exception e)
                {
                    
                    string time;
                    if (_httpClient.Timeout.TotalHours > 1)
                    {
                        time = $"{_httpClient.Timeout.TotalHours:N1} hours";
                    }
                    else if (_httpClient.Timeout.TotalMinutes > 1)
                    {
                        time = $"{_httpClient.Timeout.TotalMinutes:N1} minutes";
                    }
                    else if (_httpClient.Timeout.TotalSeconds > 1)
                    {
                        time = $"{_httpClient.Timeout.TotalSeconds:N1} seconds";
                    }
                    else
                    {
                        time = $"{_httpClient.Timeout.TotalMilliseconds:N0} milliseconds";
                    }

                    throw new TimeoutException($"No response after waiting {time}.");
                }
              
            }
            
        }
    }

And here is how I call the Utilities.
C#:
private async Task<HttpResponseMessage> CallProducts()
        {
            //some code
            //Create signature
            var products = UtilitiesTest.CreateSignature(productsDto, RequestType.Product);

            #region Call Razer for products

            //Call Razer for initiation
            var response = await UtilitiesTest.CallRazer(products, "Product/");

            var htmlResponse = await response.Content.ReadAsStringAsync();
            var model = JsonConvert.DeserializeObject<ProductResponseDto>(htmlResponse);

            //some code

            return response;
        }
 
Last edited:
There is nothing worse that giving someone the documentation for something, and then they come back and ask you how to do it. Then you tell me in the same line The thing is I am not calling any cancellation token.. Which you clearly didn't give much consideration to my post when I gave you the answer :
If you do not use a Wait or WaitAll method to wait for the task, then the task just sets its status to Canceled.
But how can it be disposed?
It may be disposed. Check your classes or maybe a using statement you have defined somewhere in your classes. This is what a debugger is for. As I said, there is only so much we can do to help.
 
I changed the calling 3rd party as follows:

C#:
HttpResponseMessage response;

//Call Game
using (var cts = new CancellationTokenSource(new TimeSpan(0, 5, 0)))
{
      response = await _httpClient.PostAsync("https://test.com/" + url, content,cts.Token);
}

I am getting the same error "System.Threading.Tasks.TaskCanceledException: A task was canceled." Still no reply from the 3rd party admin.
 
One thing not to do. Don't wrap your httpClient in a using block. httpClient is actually a shared object, and you should make sure it is declared somewhere independently in a non IDisposable block and remains static.
you can check CancellationToken.IsCancellationRequested Property (System.Threading) and if that returns false, you can bet your money something somewhere is timing out for sure.
There is no point waiting on the 3rd party. If IsCancellationRequested were returning false, you would be anticipating a timeout in your httpclient. Since you're wrapping it in a using statement, it will be disposed of and this will likely leave some calls in a TIME_WAIT hang, and is likely why IsCancellationRequested is returning true. Make the alterations and see what error you get next...
 
Don't wrap your httpClient in a using block. httpClient is actually a shared object, and you should make sure it is declared somewhere independently in a non IDisposable block and remains static.
It is not the HttpClient that is the resource used and disposed in that code, it is the CancellationTokenSource. HttpClient is static in UtilitiesTest class it appears from post 19, as it should be.
 
I changed the calling 3rd party as follows:

C#:
HttpResponseMessage response;

//Call Game
using (var cts = new CancellationTokenSource(new TimeSpan(0, 5, 0)))
{
      response = await _httpClient.PostAsync("https://test.com/" + url, content,cts.Token);
}

I am getting the same error "System.Threading.Tasks.TaskCanceledException: A task was canceled." Still no reply from the 3rd party admin.

Well according to the documentation, if the time passed into the cancellation token elapses, then the token will be cancelled. Looks like https://test.com is not responding to your post within 5 minutes.

To me, that makes sense. If you are doing burst testing, are you sure that the 3rd party API that you are calling can also support that level of burst traffic?
 
I wouldn't be surprised that if you read the Terms of Service for that 3rd Party that they specifically say that you shouldn't be doing load testing against their APIs.
 
There is no such thing :) I asked 2 times and they just said, this is a test server and its capacity may not be as good as the production server :)
By the way, I wonder if there is an outbound connection limit? I just want to be sure this is not due to my server which is hosting my API. I read this article outbound connection limit and should I use something like this?

C#:
// ServicePointManager setup
ServicePointManager.UseNagleAlgorithm = false;
ServicePointManager.Expect100Continue = false;
ServicePointManager.DefaultConnectionLimit = int.MaxValue;
ServicePointManager.EnableDnsRoundRobin = true;
ServicePointManager.ReusePort = true;
 
it is the CancellationTokenSource. HttpClient is static in UtilitiesTest class it appears from post 19, as it should be.
Indeed it is. My bad for not looking back.

Right, what I've noticed after checking with the debugger is that if your server was timing out, you would get System.OperationCanceledException OperationCanceledException Class (System) - This is not the correct exception for an error of this type, and one Microsoft need to fix in my opinion. I'm only summarising here, but there may be an exception override method in the httpclient's exception handler. Because the stacktrace shows another method (HandleFinishSendAsyncError) being called to cancel the current task when a timeout happens :
C#:
   at System.Net.Http.HttpClient.HandleFinishSendAsyncError(Exception e, CancellationTokenSource cts)
   at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
I haven't looked into the method yet on MSDN References, but I did do a quick google and found this link which may be of interest to you in tackling possible timeouts : HttpClient: OperationCanceledException hides previous exceptions · Issue #33957 · dotnet/corefx

Screenshot_8.jpg


However, if any error takes place inside your async method; IsCancellationRequested will also return true, and thus sets the cancellation of your token to true and aborts the task. The bad news for you is, this makes it harder for you to workout what is causing the termination of the task. Check out the github link above. What I do know is that the task can and will terminate if there is a timeout. If memory serves me, we should be getting a totally different error? Here is how i tested it :
Screenshot_6.jpg

I picked a closed port and used a service I know will respond. Using google.com:81 will cause a timeout. Because port 81 isn't open. The following screenshot shows regardless what error actually occurs, the httpclient will ensure IsCancellationRequested is set to true, and as I pointed out previously, this caused the method to enter the cancellation process of the running task which was not something I was expecting to find for a client timeout...
Screenshot_2.jpg

It appears any exception can cause the IsCancellationRequested to be set to true. I believe this is a possible bug, and one that should be fixed. I believe this is done under the covers of the httpclient, however the error handler is configured there... It's something I will need more time to look into.
 
Just to add a little more clarity on the stacktrace above. The method in which is being called is not referenced on MSDN Docs, however it is referenced in the .Net Git repo. The link shows, if an error occurs in an async method for the httpclient, it will bounce and throw this error : OperationCanceledException : as seen in this undocumented event. If its not evident already, this means that the wrong error message is being thrown for errors relating to connectivity issues reported by the httpclient. When I read this back; to me, its kinda like trowing an exception for an exception that isn't part of the first exception. This is a method that lacks superior information about other possible exceptions, and only because HandleFinishSendAsyncError is only checking bool for IsCancellationRequested regardless of the type of elaborate exception being passed in.
C#:
            if (NetEventSource.IsEnabled) NetEventSource.Error(this, e);

            // If the cancellation token was canceled, we consider the exception to be caused by the
            // cancellation (e.g. WebException when reading from canceled response stream).
            if (cts.IsCancellationRequested && e is HttpRequestException)
            {
                if (NetEventSource.IsEnabled) NetEventSource.Error(this, "Canceled");
                throw new OperationCanceledException(cts.Token);
            }
That's just poor on Microsoft's part. If it's not clear to you @raysefo, this issue you're experiencing is never going to report the actual exception to you until they patch how the error is handled, and extend it to cater for other possible scenarios. For now, there really is nothing you can do. Earlier on, when I thought you had using blocks on your client, here is why this would be an issue. Your connections would poll up and stay in the TIME_WAIT just like this :
Screenshot_9.jpg

But you won't see that if you are not wrapping them in IDispossible code. Like follows :
Screenshot_10.jpg

My point is to ensure with a basic console application that you can actually hit your gamesultan api by using something like this (and don't run this in your rest app, use a console for testing) :
C#:
private static readonly HttpClient httpc = new HttpClient();
C#:
private static async Task RunWithoutUsingHttpClient()
        {
            Console.WriteLine("Polling Begins");
            for (int x = 0; x < 50000; x++)
            {
                HttpResponseMessage message = await httpc.GetAsync("http://google.com");
                 Console.WriteLine(message.StatusCode);
            }
            Console.WriteLine("Connections Established");
            Console.ReadLine();
        }
And avoid writing bad code like this :
C#:
        private static async Task RunWithUsingHttpClient()
        {
            Console.WriteLine("Polling Begins");
            for (int x = 0; x < 50000; x++)
            {
                using (HttpClient httpc = new HttpClient())
                {
                    HttpResponseMessage message = await httpc.GetAsync("http://google.com");
                    Console.WriteLine(message.StatusCode);
                }
            }
            Console.WriteLine("Connections Established");
        }
Remember to change the address to the url of the server you want to hit. You can manage your connection interactions by running netstat in windows command. If you can connect without issue, you know that the problem resides in your own server setup or in your code of your restapi. I will check back tomorrow to see how it goes. Btw, you would need to be in live communication with your 3rd party for them to check if you're hitting their servers or not, especially if others are allegedly using this secret non-public API.
 
Back
Top Bottom