Question What is the difference between CustomExceptionFilter and GlobalExceptionHandler?

raysefo

Well-known member
Joined
Feb 22, 2019
Messages
362
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());
        }
    }
 
Until the bug is fixed, it's time to fallback on the good old HttpWebRequest and HttpWebResponse classes. Under the covers, HttpClient still uses these classes. It just so happens that HttpClient has put together an interface that is so much easier to use and understand.
 
I installed AppOptics APM tool on my server. (Trial) Is there any help that I can get from this tool?

@Skydiver should I use something like this for HttpWebResponse:
C#:
var request = (HttpWebRequest)WebRequest.Create("http://url");
request.ContentType = "application/json";
request.Method = "POST";

using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
    string json = new JavaScriptSerializer().Serialize(new
                {
                    user = "Foo",
                    password = "Baz"
                });

    streamWriter.Write(json);
}

var response = (HttpWebResponse)request.GetResponse();
using (var streamReader = new StreamReader(response.GetResponseStream()))
{
        var result = streamReader.ReadToEnd();
}
 
Last edited:
Something like that yes.
 
Hi @Sheepings ,

I changed the code a little bit in order to hit the API correctly just for 10 iterations. I ran it on my server in the cloud.

2019-08-25 15_49_47-C__Users_197199_Documents_Visual Studio 2017_Projects_TestGameProduct_Test...png


Here is my code sample:
C#:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;

namespace TestGameProduct
{
    internal class Program
    {
        private static readonly HttpClient httpc = new HttpClient();

        static async Task Main(string[] args)
        {
            FormUrlEncodedContent content = null;

            var productsDto = new ProductRequestDto();

            var applicationCode = "fadc3727ed7";
            var secretKey = "72e61";
            var version = "V1";
            string source = applicationCode + version + secretKey;

            using (var md5Hash = MD5.Create())
            {
                var hash = GetMd5Hash(md5Hash, source);
                productsDto.signature = hash;
                productsDto.ApplicationCode = applicationCode;
                productsDto.version = version;
            }

            //Convert request
            var keyValues = productsDto.ToKeyValues();
            content = new FormUrlEncodedContent(keyValues);

            await RunWithoutUsingHttpClient(content);
        }

     
        private static async Task RunWithoutUsingHttpClient(FormUrlEncodedContent content)
        {
            httpc.DefaultRequestHeaders.Authorization =
                new AuthenticationHeaderValue("Basic", "j9qJjc=");
            Console.WriteLine("Polling Begins");
            for (int x = 0; x < 10; x++)
            {
                HttpResponseMessage message = await httpc.PostAsync("https://teststore.gamesultan.com/Product/", content);
             
                Console.WriteLine(message.StatusCode);
            }
            Console.WriteLine("Connections Established");
            Console.ReadLine();
        }

     
        public static string GetMd5Hash(HashAlgorithm md5Hash, string input)
        {
            // Convert the input string to a byte array and compute the hash.
            byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input));

            // Create a new Stringbuilder to collect the bytes
            // and create a string.
            StringBuilder sBuilder = new StringBuilder();

            // Loop through each byte of the hashed data
            // and format each one as a hexadecimal string.
            for (int i = 0; i < data.Length; i++)
            {
                sBuilder.Append(data[i].ToString("x2"));
            }

            // Return the hexadecimal string.
            return sBuilder.ToString();
        }


    }

    public static class ObjectExtension
    {
        public static IDictionary<string, string> ToKeyValues(this object metaToken)
        {
            if (metaToken == null)
            {
                return null;
            }

            JToken token = metaToken as JToken;
            if (token == null)
            {
                return ToKeyValues(JObject.FromObject(metaToken));
            }

            if (token.HasValues)
            {
                var contentData = new Dictionary<string, string>();

                foreach (var child in token.Children().ToList())
                {
                    var childContent = child.ToKeyValues();
                    if (childContent != null)
                    {
                        contentData = contentData.Concat(childContent).ToDictionary(k => k.Key, v => v.Value);
                    }
                }

                return contentData;
            }

            var jValue = token as JValue;
            if (jValue?.Value == null)
            {
                return null;
            }

            var value = jValue?.Type == JTokenType.Date
                ? jValue?.ToString("o", CultureInfo.InvariantCulture)
                : jValue?.ToString(CultureInfo.InvariantCulture);

            return new Dictionary<string, string> { { token.Path, value } };
        }
    }
    public class ProductRequestDto
    {
        public string version { get; set; }
        public string signature { get; set; }
        public string ApplicationCode { get; set; }
    }


}
 
Last edited:
Run it up to 50, then wait some minutes before doing it again. Then try 100, then 300, then 500 then 800 then 2000 then 5000 wait a few minutes before each execution. This will prove if you're meant to be load testing against their API, as I suggested before, it's possible they could be blocking your requests.
 
Test console app works without a problem but in my rest API, I am not calling this address: http://qa-api.mol.com/pinstore/. (I am disappointed now) I will install Jmeter on the server and test my API in the IIS. (I was testing on my laptop) Maybe APM tool which I installed on the server, can tell me something.

Should I use something like this?
ConfigureAwait(false)
 
Last edited:
A little common sense now.

Why would you send data to a server address where the traffic is redirected. Use the new URI.

Also use the old classes as skydiver suggested...
 
Also use the old classes as skydiver suggested...

Should it be async?
C#:
HttpWebRequest request = new HttpWebRequest(http://url);
request.Method = "POST";

string postString = String.Format("username={0}&password={1}&grant_type=password", HttpUtility.HtmlEncode(username), HttpUtility.HtmlEncode(password));
byte[] bytes = Encoding.UTF8.GetBytes(postString);
using (Stream requestStream = await request.GetRequestStreamAsync())
{
     requestStream.Write(bytes, 0, bytes.Length);
}

try
{
     HttpWebResponse httpResponse =  (HttpWebResponse)(await request.GetResponseAsync());
     string json;
     using (Stream responseStream = httpResponse.GetResponseStream())
     {
           json = new StreamReader(responseStream).ReadToEnd();
     }
     Model response = JsonConvert.DeserializeObject<Model>(json);
     return response;
}
catch (Exception ex)
{
    //exception
}
 
Lets not go down one of your code rabbit holes just yet. If you're still using the same old address to query their servers, then I will be stepping out of the topic now. Your solution to my questions always seems to be answered by throwing irrelevant code at me when you should be answering the implied question...
Why would you send data to a server address where the traffic is redirected.
If you're still using the same old address, then this is not going to work. If your API provider haven't setup their API to work on their address showing on their A record for 202.76.234.81, you won't be able to query against this hidden API. There is no point further testing your code until you get answers from gamesultan. Ask them why the test site they created now redirects traffic, and ask if their API works on the location of the redirected domain - which is qa-api.mol.com. Also ask for updated querying information.
teststore.gamesultan.com213.74.118.227
host-213-74-118-227.superonline.net
AS34984 Tellcom Iletisim Hizmetleri A.s.
Turkey
C#:
PORT     STATE    SERVICE
21/tcp   filtered ftp
22/tcp   filtered ssh
23/tcp   filtered telnet
80/tcp   filtered http
110/tcp  filtered pop3
143/tcp  filtered imap
443/tcp  open     https
qa-api.mol.com202.76.234.81AS24218 Global Transit Communications - Malaysia
Malaysia
C#:
PORT     STATE    SERVICE
21/tcp   filtered ftp
22/tcp   filtered ssh
23/tcp   filtered telnet
80/tcp   open     http
110/tcp  filtered pop3
143/tcp  filtered imap
443/tcp  open     https
Querying against the old server (213.74.118.227) is a lost cause. Instead of rushing stupidly into this, you need to find out more about the network you should be querying, then you will execute some test code in a separate application from your rest API. And then we can worry about merging your test code with your rest API once we know gamesultan have a working setup at the new address. The results above are from nmap and accurately reflects the current server states. At a glance, which one looks more responsive? With that question, why are you still querying the old address?
 
Last edited:
I did a load test on the test server (not on my laptop) with 1000 users to the new address. Only got 3 error. I installed the trial appoptics APM tool on the test server. Here is the most time-consuming thread. Does something wrong with the IIS?

appoptics.png
 

Latest posts

Back
Top Bottom