Axios Interceptors and Exception Handling

Handling exceptions globally and gracefully is essential part of any application. This reduces the scattered try-catch cases and enable to handle everything from one point.

In this article, we will explore how to implement global exception handling when making Api calls in an SPA design. We will use VueJs for demonstration along with Axios packages to assist the Http calls.

Axios Interceptors

Interceptors in axios provide a great oppurtunity to handle responses at a single point. Interceptors are methods which is invoked for every axios request and hence, allows you to intercept and transform the response before it makes its way to calling method.

To begin with, let us head over to our main.js and import axios

import axios from "axios";

Axios exposes two types of interceptors, one each for request and response.

Response Interceptor

For our purpose, we need the response interceptor (we will breifly speak about the request interceptor later). Axios invokes the response interceptor after the request has been send and response recieved. You could intercept this response using axios.interceptors.response.use, modify it to suit your needs before sending it back to the calling method.

axios.interceptors.response.use(
  (response) => successHandler(response),
  (error) => exceptionHandler(error)
);

The method accepts two parameters (second one is optional), each corresponding to a valid and error Response, which would be invoked depending on whether the request has succeeded or failed.

Let us plugin our interceptor in main.js to intercept the errors.

axios.interceptors.response.use(
  (response) => {
    return response;
  },
  (error) => {
    switch (error.response.status) {
      case 401:
        // Handle Unauthorized calls here
        // Display an alert
        break;
      case 500:
        // Handle 500 here
        // redirect
        break;
      // and so on..
    }

    if (error.response.status == 400) {
    }
    return Promise.reject(error);
  }
);

I ususually handle my BadRequst calls within the component. I could hence write a special response in the switch case for Error Code 400, which does the same for me.

Modifying the switch case,

switch (error.response.status) {
  case 400:
    return {
      data: null,
      hasError: true,
      error: [error.response.data],
    };
  case 401:
    // Handle Unauthorized calls here
    // Display an alert
    break;
  case 500:
    // Handle 500 here
    break;
  // and so on..
}

As seen, the interceptors provides us a global platform the handle exceptions.

Request Interceptor

A word about the request interceptor before signing off – The request interceptors are called each time before the request is made.

This provides an great oppurtunity to inject/modify headers to include authentication tokens. For example,

axios.interceptors.request.use((request) => {
   const header = getHttpHeader(); // get your custom http headers with auth token
  request.headers = header;
  return request;
});

The interceptors could be also used to show busy indicators while the request is being processed as both request interceptor and response interceptor provies us a window of oppurtunity to display a indicator before the request is send out and hide the indictor as soon as the response is recieved.

Hope that was useful.

Revisiting Exception Handling in async methods

It is interesting to observe how the Exceptions behave in async code. While the .Net framework attempts to ensure the exprerience of handling failures in async methods are as similar to the synchronous methods, there are subtle differences which is worth understanding.

Let us examine the following code.

async Task<string> Foo()
{
	var client = new HttpClient();
	try
	{
		return await client.GetStringAsync("http://www.InvalidUrl.com");
	}
	catch(HttpRequestException ex)
	{
		Console.WriteLine($"Exception of Type {ex.GetType()} has been raised");
	}
	catch(AggregateException ex)
	{
		Console.WriteLine($"Exception of Type {ex.GetType()} has been raised");
	}
	catch(Exception ex)
	{
		Console.WriteLine($"Exception of Type {ex.GetType()} has been raised");
	}
	return default;
}

What could be the output of the above code ? Specifically, what type of an exception would be caught assuming the Url is invalid ?

Before we get to the answer, let us examine how the returned Task<string> object indicate the failure.

Property/MethodIndication
StatusFaulted
IsFaultedtrue
ExceptionAggregateException
Wait()Throws AggregateException
ResultThrows AggregateException
await TaskThrows First Exception with AggregateException

The last 3 rows holds importance to our question. Let us reexamnine the method call.

return client.GetStringAsync("http://www.InvalidUrl.com").

In the above case, we would recieve an AggregateException with an HttpRequestException within it.

When Exception causes Status |= Faulted

As mentioned above, the status of Task would be set to Faulted in most cases, except for one particular kind of Exception,the OperationCancelledException.

Let us write some code before we discuss this further.

async void Main()
{
	var task = Foo();
	try
	{
		await task;
	}
	catch(Exception Ex)
	{
		Console.WriteLine($"Task Status:{task.Status}, Exception:{Ex.Message}");
	}
}

async Task Foo()
{
	throw new OperationCanceledException();
}

Examining the output

Task Status:Canceled, Exception:The operation was canceled.

The TPL uses OperationCanceledException when a Token from CancelationTokenSource is canceled by the calling method. If a method like the code above, decides to throw this special exception, then instead of the Status being set to Faulted, the Status is set to Canceled.

Lazy Exceptions

There is another aspect of Exception Handling in async methods that are worth examining. An async task would not directly throw an exception, instead it would return a faulted Task. Significance of the method could be better understood with help of a bit of code.

async void Main()
{
	try
	{
		var fooTask = Foo(-3);
		Console.WriteLine("Task is not awaited yet");
		await fooTask;
		Console.WriteLine("Task Completed");
	}
	catch(ArgumentException ex)
	{
		Console.WriteLine($"{nameof(ArgumentException)} has been raised");
	}
}


public async Task<string> Foo(int value)
{
	Console.WriteLine($"Method {nameof(Foo)} Invoked");
	if(value<0)
	{
		throw new ArgumentException();
	}
	
	Console.WriteLine($"Method {nameof(Foo)} mocking real task via Delay");
	await Task.Delay(1000);
	return default;
}

The Foo() method has a precondition check which validates if the passed arguement is a positive number. Otherwise, it raises an ArgumentException. With the example code, the method invoker is passing a negative value to the method, and should hit the precondition block.

Let us examine the output and discuss further.

Method Foo Invoked
Task is not awaited yet
ArgumentException has been raised

As you can observe, the message “Task is not awaited yet” is displayed before the exception thrown. This is because exceptions would not be raised untill the task is awaited (or completed). This lazy nature of evaluation of exceptions could be useful at most times, but in times such as above, where preconditions needs to be evaluated and the developer would prefer an early evaluation, this would need a slight workaround.

The idea, similar to how we made iterator methods to evalute exceptions early (and as John Skeets mentions in his invaluable book series C# in Depth), lies in introducing a synchronous method which does the arguement validation, and which in-turn calls the original method. If the original method is moved as an internal method of proposed method, the original method can now safely assume that the arguements are validated.

public Task<string> Foo(int value)
{
	Console.WriteLine($"Method {nameof(Foo)} Invoked");
	if(value<0)
	{
		throw new ArgumentException();
	}
	
	async Task<string> FooAsync()
	{
		Console.WriteLine($"Method {nameof(Foo)} mocking real task via Delay");
		await Task.Delay(1000);
		return default;
	}
	return FooAsync();
}

This ensures the validation and subsequent exception is evaluated early. Let’s hit F5 and run our code now.

Method Foo Invoked
ArgumentException has been raised

As observed, the exception has been evaluated early and we get the much desired result.

That’s all for now, see you soon again

Early Exceptions for Iterator and async Methods

One of the first things you would need to be vary of while working with Iterator methods and async methods is exception handling. Both Iterator methods and async methods delay the execution of your code, right until you need the result, but it also results in delayed exceptions.

Late Exception With Iterator Methods

Let’s consider a typical example. The following method generates positive integers from 1 to ‘n’. Of course, you could do this with Enumerable.Range but, that’s not the whole point.

void Main()
{
var maxValue = 0;
var generatedCollection = GenerateSequence(maxValue);
Console.WriteLine("No Exception raised yet");
foreach(var item in generatedCollection)
Console.WriteLine(item);
}

IEnumerable<int> GenerateSequence(int maxValue)
{
if(lastNumber <= 0)
throw new Exception($"Parameter {nameof(maxValue)} should be positive, non-zero");

for(int i=0;i<maxValue;i++)
yield return i;
}

The above method has a precondition which validates if the maxValue parameter is a positive, non-zero number. However if one was to execute the code, one can notice that exception isn’t raised until the result is consumed. This is expected. But, there could be scenarios where one might require you to have methods raise early exceptions.

Early Exception With Iterator Methods

Let’s rewrite the method with a little bit of tweak1.

void Main()
{
var maxValue = 0;
var generatedCollection = GenerateSequence(maxValue);
Console.WriteLine("No Exception raised yet");
foreach(var item in generatedCollection)
Console.WriteLine(item);
}

IEnumerable<int> GenerateSequence(int lastNumber)
{
if(lastNumber <= 0)
throw new Exception($"Parameter {nameof(lastNumber)} should be positive, non-zero");

return Generate();
IEnumerable<int> Generate()
{
for(int i=0;i<lastNumber;i++)
yield return i;
}
}

Try executing the code, and you can notice that now it supports early exception. This very approach could be used for async method. Let’s do the problematic code first.

Late Exception with async Methods

async Task<int> Increment(int numberToPrint)
{
if(numberToPrint <= 0)
throw new Exception($"Parameter {nameof(numberToPrint)} should be positive, non-zero");

return await Task.FromResult(++numberToPrint);
}

// Client code
var value = 0;
var task = Increment(value);
Console.WriteLine("No Exception raised yet");
await task;

The problem with late exceptions is visible in the above code as well. The exception is not raised where the method is called (in code), but rather it is raised when the Task is awaited.

Early Exception with async Methods

Task<int> Increment(int numberToPrint)
{
if(numberToPrint <= 0)
throw new Exception($"Parameter {nameof(numberToPrint)} should be positive, non-zero");

return Increment();
async Task<int> Increment()
{
return await Task.FromResult(++numberToPrint);
}
}

// Client Code
var value = 0;
var task = Increment(value);
Console.WriteLine("No Exception raised yet");
await task;

Reference


  1. Reference : More Effective C# : version 7.0 

Exception Handling in Web API (Part 2): Exception Filter (Extended)

The Exception Filter implementation mentioned in the Part1 of the article is fairly simple and straightforward.  But as you start supporting more and more Exceptions, the Cyclomatic Complexity of the “OnException” method would increase because of the “if” condition nature.

A cleaner implementation of the Filter is shown below using Dictionary.

 public class DemoExceptionFilter : ExceptionFilterAttribute
 {
 public DemoExceptionFilter()
 {
 this.ExceptionDictionary = new Dictionary();
 this.ExceptionDictionary.Add(typeof(NotImplementedException), HttpStatusCode.ServiceUnavailable);
 this.ExceptionDictionary.Add(typeof(ArgumentOutOfRangeException), HttpStatusCode.BadRequest);
 }
 public IDictionary ExceptionDictionary
 {
 get;
 private set;
 }
 public override void OnException(HttpActionExecutedContext ExecutedContext)
 {

 if (ExceptionDictionary.ContainsKey(ExecutedContext.GetType()))
 {
 ExecutedContext.Response = new HttpResponseMessage(ExceptionDictionary[ExecutedContext.GetType()]);
 
 }
 else
 {
 throw new HttpResponseException( new HttpResponseMessage(HttpStatusCode.InternalServerError));
 }
 base.OnException(ExecutedContext);
 }

 }

Isn’t that cleaner ?
 

Exception Handling in Web API (Part 1): Exception Filter

The Web World, especially Web Services is fast moving towards the more abstract simpler Web API. This article is not focused on comparing Web Service and Web API, but rather it focuses on Exception Handling in Web API.

By default,  the most common exception that an API Controller raises are translated into an HTTP Status Code 500 (Internal Server Error) (HTTP Status Code ). But what if we want to customize the Error Code ? There are couple of ways to achieve it, the first part of this article focuses on Exception Filters.
An Exception Filter is probably the easiest way to handle the exception efficiently and it would handle any exception which the controller throws except for the HttpResponseException (Why HttpResponseException is not handled by Exception Filter will be discussed later).
The simplest way to create you own customized Exception Filter is to derive from ExceptionFilterAttribute Class under System.Web.Http.Filters namespace and override the OnException Method
An Example of Exception Filter is shown below.
public class DemoExceptionFilter : ExceptionFilterAttribute
{
public override void OnException( HttpActionExecutedContext ExecutedContext)
{
if (ExecutedContext.Exception is NotImplementedException)
{
ExecutedContext.Response = new HttpResponseMessage (HttpStatusCode.ServiceUnavailable);
ExecutedContext.Response.ReasonPhrase += " : Function Not Implemented";
}
base.OnException(ExecutedContext);
}
}
The code is pretty self explanatory. I have modified all the NotImlementedException to change the Http Status Code to 503 (Service Unavailable). I have also appended “Function Not Implemented”  message to the Reason Phrase . The next obvious step is to ensure our Exception Filter is used by the WebAPI Pipeline. To ensure that the DemoExceptionFilter is used globally, all we need to do is to add it to the list of filters in WebApiConfig. We use the Filters.Add Method in HttpConfiguration to do the same. For Example,
	public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            config.Filters.Add(new ExceptionManagement.DemoExceptionFilter());
        }
That’s it !!!. Now every Exception your APIController throws , with the exception of HttpResponseException , would have to go through your DemoExceptionFilter. To test the code, I have thrown an exception from my controller as seen in example below.
    public class ValuesController : ApiController
    {
        // GET api/values
        public IEnumerable  Get()
        {
            throw new NotImplementedException ();
        }

        // GET api/values/5
        public string Get( int id)
        {
            return "value" ;
        }
     }
     
Now run your application and check the response in Fiddler.
 NotImplemented
Wasn’t that easy ? Yes, but Exception Filters has its own short-comings, which would be explained in the next part. Happy Coding !!