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.

Evil Code #13 : Tuple Deconstruction Assignment

It is not often that I end up writing two back to back posts on Evil Code series, and that too on the same topic. But like I said in the previous post, Tuples are really an interesting topic (if you are reading Jon Skeet’s book, then everything is interesting)

In this post, we will explore a certain behavior of tuples observed during deconstruction assignment

Consider the following code.

var person = new Person{Name="Jia",Age=4};
var personCopy = person;
(person,person.Age) = (new Person{Name="Naina"},0.2);

Console.WriteLine($"Student => Name: {person.Name}, Age: {person.Age}");
Console.WriteLine($"StudentCopy => Name: {personCopy.Name}, Age: {personCopy.Age}");

The output might surprse you at the first glance, but seems reasonable as you attempt to understand what goes behind the deconstruction assignment. Let us look at the output first before we see what happens during the deconstruction.

Student => Name: Naina, Age: 0
StudentCopy => Name: Jia, Age: 0.2

This behavior attributes itself to the 3 stage process involved in the deconstruction assignment.

Note that unlike User defined type deconstruction, which requires invocation of Deconstruct method with appropriate number of parameters.

As Microsoft has put down in the feature description,

The evaluation order can be summarized as: (1) all the side-effects on the left-hand-side, (2) all the Deconstruct invocations (if not tuple), (3) conversions (if needed), and (4) assignments.

From the above, (2 & 3) aren’t part of our concern (it is a tuple and no conversion if required as the source/destination types are the same). In the first step, the side-effect of LHS is evaluated while only in the last step the actual assignments happen.

So in this case, each of the expresion on the Left hand side, is evaluated one by one, and a storage location is reserved for it.After this, the RHS is evaluated, and any conversion to destination type is done if required.

Finally the conversion results or values from the RHS is assigned to the storage location identified in the first step.

This would mean the above code gets roughly translated to

Person person = new Person
{
    Name = "Jia",
    Age = 4.0
};
Person personCopy = person;
// LHS is evaluated and storage for person.age is reserved
Person person2 = person;
//RHS is evaluated and converted if required
Person obj = new Person
{
    Name = "Naina"
};
double num = 0.2;

// assignments
person = obj;
person2.Age = num;

Notice that during the evaluation of LHS, space reservation and evaluation is done only for person.age and not for  person from (person,person.age). This is because when assigning to a property of a variable, an extra step is required to evaluate the value of variable. This step could be skipped when the target is a variable and not property of a variable.

That explains the output we received for the earlier code.

Note: If you are a C# developer, and haven’t read Jon Skeet’s C# in Depth, do it immediately. This book is a MUST read for any C# developer.

Evil Code #12: Tuple Parameter – Overloads and Overrides

Tuples is an interesting subject to go back to due to certain peculiar qualities it possess. In this section, we would address some of them.

To begin with, however, we will address how to tuples behave during method overloads – a pattern it share with rest of .Net. Consider the following code.

public class Foo
{
	public void Bar((int,object) param)
	{
	}
	
	public void Bar((int,dynamic) param)
	{
	}
}

Would the above code compile ? If not, why ?

Well, it doesn’t compile – of course. The compiler would complain that

Type 'Foo' already defines a member called 'Bar' with the same parameter types

This is because there is an identity conversion between object and dynamic, which results in compiler detecting both methods as signature with same parameter types. This is similiar to how it would have been if you had defined an overload as following.

public void Bar(object param)
{
}

public void Bar(dynamic param)
{
}

As one would guess, that isn’t allowed either.

The second scenario we would be checking is with regard to inheritance or interface implementations. Consider the following code.

public interface IBar
{
	void Foo((object left,object right) param);
}

public class Bar:IBar
{
	public void Foo((object left, dynamic right) param)
	{
	}
}

Now considering the identity conversion between object and dynamic, it is easy to guess that this would be allowed. However, the nasty part shows when you vary the name of the tuple variable. Consider the following code, where i have renamed the right parameter in tuple as center.

public interface IBar
{
	void Foo((object left,object right) param);
}

public class Bar:IBar
{
	public void Foo((object left, object center) param)
	{
	}
}

Notice, I have retained the types of each element and arity of the tuple. The only thing that has changed is the name of one of the elements in the tuple. This breaks the code with following exception.

The tuple element names in the signature of method 'Bar.Foo((object left, object center))' must match the tuple element names of interface method 'IBar.Foo((object left, object right))' (including on the return type).

Isn’t that interesting ? I would rather use the word scary. Imagine as a library developer you rename of the tuple parameters in your API and it breaks all the clients.

This would be one change I wish the elite team working .Net manages to fix as this is quite unlike rest of the .Net.

A2Z.Net : A – Anonymous Types

This article marks the beginning of a new series of posts, which aims to dig deeper into 26 different features of .Net. For the first article, or the A – I have selected Anonymous Types. I would have chosen asynchronous programming, but I have whole dedicated series on asynchronous programming which could followed in this link.

Introduced in .Net 3.x, Anonymous Types has made the life easy for the developers by handling out the ability to work on an encapsulated type, without having to actually define one. I should say encapsulate is a wrong word here (but have used it so that I could highlight why) because you cannot place any behavior with the properties.

Anonymous Types and Syntax

Anonymous Type provides an easy way to encapsulate a set of properties without having to explicitly define a type. Notice that I used the word explicitly here – that is because the compiler creates the type for you under the hood. We will look into what happens behind the scenes a little later, to begin with, let us introduce the syntax of defining an anonymous type.

var user = new
{
    UserName ="Anu Viswan",
    Age = userRecord.Age,
    JoiningDate= DateTime.Now
};

The above code uses object initializer (without having to specify the data type) to create an instance of an anonymous type initialized with 3 properties – namely UserName(string),Age(int), and JoiningDate (DateTime). You could also create an anonymous type.

As I mentioned earlier, the compiler generates a type under the hood and ensure the code is still statically typed.

Since the type of anonymous Type is known statically, they must be stored using variables with var allowing type inference. You could also store them as object, but that would defeat the whole purpose of anonymous types.

In addition to the creation syntax mentioned above, framework also provides another way initializing an anonymous type. This syntax is known as projection initializer, and it allows you to use the property/field names from somewhere else.

var person = new Person
{
    FirstName = "Anu",
    LastName = "Viswan",
    Age = 36
};

var user = new
{
    person.FirstName,
    person.Age,
};

The above code uses a part of type Person to create an anonymous type. In this case, as you can notice, you are not explictly providing names for properties, instead, they uses the same name as the Person. This is of course equavalent of writing as

var user = new
{
    FirstName = person.FirstName,
    Age = person.Age
};

The projection initializer syntax is highly useful in Linq queries when you need to project a subset of a query or combine properties from multiple objects. For Example,

var personList = new List<Person>
{
	new Person {UserName="Anu Viswan",    Age = 36},
	// Other Persons
};

var employeeList = new List<Employee>
{
    new Employee{UserName="Anu Viswan",JoiningDate=new DateTime(2020,2,2)},
    // Other employee records //
};

var user = employeeList.Join(personList,
							e=>e.UserName,
							p=>p.UserName,
							(e,p)=> new
								{
									e.UserName,
									UserDetail = new  // An example of Nested Anonymous Type
									{
									e.JoiningDate,
									p.Age,
									QueryTimeStamp=DateTime.Now
									}
								});

As you can observe the creation of anonymous types are pretty simple. You could also create a type combining multiple objects as seen in above example. The above code also demonstrates that the anonymous types could be nested. UserDetail is an anonymous type within the user.

Behind the Scenes

Alright, enough of syntax, let us now look behind the scenes now. Let us bring back our first example of anonymous type and see how the type generated by the compiler looks like.

var user = new
{
    UserName = "Anu Viswan",
    Age = 36,
    JoiningDate= DateTime.Now
};

The compiler generated (_generated using ILSpy) type would look like

[CompilerGenerated]
[DebuggerDisplay("\\{ UserName = {UserName}, Age = {Age}, JoiningDate = {JoiningDate} }", Type = "<Anonymous Type>")]
internal sealed class <>f__AnonymousType0<<UserName>j__TPar, <Age>j__TPar, <JoiningDate>j__TPar>
{
	[DebuggerBrowsable(DebuggerBrowsableState.Never)]
	private readonly <UserName>j__TPar <UserName>i__Field;

	[DebuggerBrowsable(DebuggerBrowsableState.Never)]
	private readonly <Age>j__TPar <Age>i__Field;

	[DebuggerBrowsable(DebuggerBrowsableState.Never)]
	private readonly <JoiningDate>j__TPar <JoiningDate>i__Field;

	public <UserName>j__TPar UserName => <UserName>i__Field;

	public <Age>j__TPar Age => <Age>i__Field;

	public <JoiningDate>j__TPar JoiningDate => <JoiningDate>i__Field;

	[DebuggerHidden]
	public <>f__AnonymousType0(<UserName>j__TPar UserName, <Age>j__TPar Age, <JoiningDate>j__TPar JoiningDate)
	{
		<UserName>i__Field = UserName;
		<Age>i__Field = Age;
		<JoiningDate>i__Field = JoiningDate;
	}

	[DebuggerHidden]
	public override bool Equals(object value)
	{
		// Hidden : Will be discussed later
	}

	[DebuggerHidden]
	public override int GetHashCode()
	{
		// Hidden : Will be discussed later
	}

	[DebuggerHidden]
	public override string ToString()
	{
		// Hidden : Will be discussed later
	}
}


Characteristics of Generated Code

Let us break down the generated code and examine it in parts.

It is a Generic Class

As you would have already observed, the generated type is a class, name of which is generated by the compiler and cannot be accessed from your source code.

internal sealed class <>f__AnonymousType0<<UserName>j__TPar, <Age>j__TPar, <JoiningDate>j__TPar>
{
    public <>f__AnonymousType0(<UserName>j__TPar UserName, <Age>j__TPar Age, <JoiningDate>j__TPar JoiningDate)
	{
		<UserName>i__Field = UserName;
		<Age>i__Field = Age;
		<JoiningDate>i__Field = JoiningDate;
	}
}

It is also interesting to note that the generated type is a Generic class, with one type parameter for each of the properties. This would mean that if there is a second string, it would still have a different type parameter than the original one. For example,

var user = new
{
    UserName = "Anu Viswan",
    FirstName = "Anu"
};

The above would translate to

internal sealed class <>f__AnonymousType0<<UserName>j__TPar, <FirstName>j__TPar>
{
}

Note that the class is marked internal and sealed. Now both these characterstics aren’t guaranteered as per Jon Skeet’s book C# in Depth. This is actually interesting, as to know when and why the compiler could not guarante a consistent behavior.

The generated type also has a constructor which accepts parameters which are in consistent with all the properties in the type.

Read Only Properties

The other characterstics of the generated type that is quite obvious is that all properties are read-only. One cannot reassign properties of an anonymous type. If the need arise, you would have to recreate the entire object.

var user = new
{
    UserName = "Anu Viswan",
    Age = 36,
    JoiningDate = DateTime.Now,
};

user.Age = 37; // This line would throw error

user = new  // The correct approach would be recreate
{
    UserName = user.UserName,
    Age = 37,
    JoiningDate = user.JoiningDate,
};

Derieved from System.Object & Overrides Equals()/GetHashCode()

The generated class for anonymous types is derieved from System.Object. It cannot be, hence, cast to any other type other than Object. The generated type also overrides the Equals() and GetHashcode() methods.

public override bool Equals(object value)
{
    <>f__AnonymousType0<<UserName>j__TPar, <Age>j__TPar, <JoiningDate>j__TPar> anon = value as <>f__AnonymousType0<<UserName>j__TPar, <Age>j__TPar, <JoiningDate>j__TPar>;

    return anon != null && EqualityComparer<<UserName>j__TPar>.Default.Equals(<UserName>i__Field, anon.<UserName>i__Field) && EqualityComparer<<Age>j__TPar>.Default.Equals(<Age>i__Field, anon.<Age>i__Field) && EqualityComparer<<JoiningDate>j__TPar>.Default.Equals(<JoiningDate>i__Field, anon.<JoiningDate>i__Field);
}

public override int GetHashCode()
{
    return ((459989953 * -1521134295 + EqualityComparer<<UserName>j__TPar>.Default.GetHashCode(<UserName>i__Field)) * -1521134295 + EqualityComparer<<Age>j__TPar>.Default.GetHashCode(<Age>i__Field)) * -1521134295 + EqualityComparer<<JoiningDate>j__TPar>.Default.GetHashCode(<JoiningDate>i__Field);
}

These ensures that two instances of AnonymousTypes are equal only if all the properties are equal. Do note that the exact implementation of GetHashCode() might also vary. For example,

var user = new
{
    UserName = "Anu Viswan",
    Age = 36,
    JoiningDate = new DateTime(2020,10,10),
};

var anotherInstance = new
{
    UserName = "Anu Viswan",
    Age = 36,
    JoiningDate = new DateTime(2020, 10, 10),
};

Console.WriteLine($"user & anotherInstance - Equality :{user.Equals(anotherInstance)}"); // True

Reuse of Anonymous Types

If two anonymoys types are created with same Property Names, in same order and type, then the compiler would treat the two objects as of same type, ie, essentially reuse the generated type.

For example, the preceeding code would reuse the same generated type.

Console.WriteLine($"user & anotherInstance - Type Equality :{user.GetType() == anotherInstance.GetType()}"); // True

This could verified when we check the generated code.

<>f__AnonymousType0<string, int, DateTime> user = new <>f__AnonymousType0<string, int, DateTime>("Anu Viswan", 36, new DateTime(2020, 10, 10));

<>f__AnonymousType0<string, int, DateTime> anotherInstance = new <>f__AnonymousType0<string, int, DateTime>("Anu Viswan", 36, new DateTime(2020, 10, 10));

However, if you change the order of Properties in either of the anonymous types, the compiler would generate another type. Consider the following code.

 var user = new
 {
     UserName = "Anu Viswan",
     Age = 36,
     JoiningDate = new DateTime(2020,10,10),
 };
  var differentInstance = new
 {
     UserName = "Anu Viswan",
     JoiningDate = new DateTime(2020, 10, 10),
     Age = 36,
 };

As you can observe, while the PropertyNames and Types are preserved in the two anonymous types, the order is changed.The generated code would be as following.

<>f__AnonymousType0<string, int, DateTime> user = new <>f__AnonymousType0<string, int, DateTime>("Anu Viswan", 36, new DateTime(2020, 10, 10));

f__AnonymousType1<string, DateTime, int> differentInstance = new <>f__AnonymousType1<string, DateTime, int>("Anu Viswan", new DateTime(2020, 10, 10), 36);

As seen in the generated code, the compiler generates and use different internal types for the two anonymous types.

This holds true only when the two anonymous types are in the same assembly. If the two anonymous types are defined in two different assemblies, compiler would generate two different types for each. We will explore this later in this article.

Points Of Interest

The above section outlined the characterstics of Anonymous Types. There are however, certain points which is worth noting while learning about Anonymous Types.

Pass as Parameter/ Return Type

Since the generated type is not accessible/addressed by the developer in his code, it to an extend limits the transfer an anonymous types from one method to another as you cannot express the type as Parameter. However, you could make use of Generics to pass the anonymous types around. For example,

 public void Print<T>(T data)
 {
     var properties = typeof(T).GetProperties();
     foreach(var property in properties)
     {
         Console.WriteLine($"{property.Name} = { property.GetValue(data)}");
     }
 }

The compiler here treats the generated type as any reference type in the system. This allows us to use generics to pass anonymous types around.

You could also pass the anonymous type as an object or dynamic parameter as well, however, that would take away any benefits associated to anonymous types.

Similiar Anonymous Types in Two Assemblies

In the earlier section we noticed how the compiler would reuse the same generated type if the Property Names, Order and Types are the same. However, there is a caveat. The generated types are localized to the assembly.

If the two types resides in different assembly, then the compiler would generate different Anonymous Types for each of them.

Consider the following scenario.

// Assembly 1
 var user = new
 {
     UserName = "Anu Viswan",
     Age = 36,
     JoiningDate = new DateTime(2020,10,10),
 };
 var bar = new Bar();
 bar.Print(user);


// Assembly 2
public class Bar
{
    public void Print(object data)
    {
        var typeSchema = new { UserName = string.Empty, Age = default(int), JoiningDate = default(DateTime) };
        var castInstance = Cast(typeSchema, data);
        Console.WriteLine(data.GetType() == castInstance.GetType());
    }
    private static T Cast<T>(T typeHolder, Object x) where T : class
    {
        return (T)x;
    }
}

The above casting would raise an InvalidCastException as the two types resides in different assemblies, resulting in compiler generating two different anonymous types.

This would have been a succesful cast if the code resided in the same assembly.

Conclusion

As observed, the implement of Anonymous Types is pretty simple, and so is its usage. The compiler generates a generic class for each unique anonymous type within the assembly. The feature has been extensively used since Linq came along as it allows to create subsets from queries without having to define an explicit type.

The complete code citd in this article is available in my Github.