Tuesday, April 17, 2018

Web API Default Behaviour Overriding

Overriding Text Encoding

In one of our project we had an issue where one of invalid text character in web api was throwing exception instead of removing or replacing that bad character. After investigating we figure out that the default Encoding was UTF8 but EncoderFallback was set to throw Exception for invalid character instead of replacing with default Character.

Therefore, we just added this one below line to replace the text encoding behaviour

            config.Formatters.JsonFormatter.SupportedEncodings[0] = Encoding.UTF8;

The default system UTF8 Encoding fallback set to replace invalid character with "?"

public static void ConfigureWebApi(HttpConfiguration config)
        {
            ...
            ...

        // HACK: To allow JSON serialization to handle invalid characters
            config.Formatters.JsonFormatter.SupportedEncodings[0] = Encoding.UTF8;
            config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
            config.Services.Replace(typeof(IExceptionLogger), new GlobalExceptionLogger());
        }

Overriding GlobalExceptionHandler

We are using Azure Telemetry to log all of our errors therefore, we wanted to override the global exception handler to log all errors to telemetry. This is similar concept of Global.ascx function for web api.

the below line will replace the GlobalExceptionHandler for WebApi

            config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());


   public class GlobalExceptionHandler : ExceptionHandler
    {
        public override void Handle(ExceptionHandlerContext context)
        {
            var exception = context.Exception;
            var message = context.Exception.GetType().Name + ": " + context.Exception.Message;

            if (exception is HttpException httpException)
            {
                context.Result = new CustomErrorResult(context.Request, (HttpStatusCode)httpException.GetHttpCode(), message);
                return;
            }

            // Return HttpStatusCode for other types of exception.
            context.Result = new CustomErrorResult(context.Request, HttpStatusCode.BadRequest, message);
        }
    }
 


public class CustomErrorResult : IHttpActionResult
    {
        private readonly string _errorMessage;
        private readonly HttpRequestMessage _requestMessage;
        private readonly HttpStatusCode _statusCode;

        public CustomErrorResult(HttpRequestMessage requestMessage, HttpStatusCode statusCode, string errorMessage)
        {
            _requestMessage = requestMessage;
            _statusCode = statusCode;
            _errorMessage = errorMessage;
        }

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            return Task.FromResult(_requestMessage.CreateErrorResponse(_statusCode, _errorMessage));
        }
    }
  


Overriding GlobalExceptionLogger

We also wanted to replace the GlobalExceptionLogger to be our ExceptionLogger to push all errors to Azure Telemetry.

            config.Services.Replace(typeof(IExceptionLogger), new GlobalExceptionLogger());


public class GlobalExceptionLogger : ExceptionLogger
    {
        public override void Log(ExceptionLoggerContext context)
        {
            new TelemetryClient().TrackException(context.Exception);
        }
    }