.NET Backend

Microservices, HTTP forwarding and RestEase

There are some common questions when it comes to microservices. One of them is „how to forward request from API gateway to the particular microservice?”. There’s no rule of thumb here because everyone has a slightly different approach, but the most popular solution I know is:

  • For writes, so creates, updates and deletes (CUD) create a command and publish it to the service bus based on a queue like RabbitMQ.
  • For reads (GET) forward the HTTP request to the internal API (not publicly exposed) of the particular microservice.

The reason for that division is quite simple. If the user wants to read data on the UI, we want to make sure that the response will come as quick as possible. Therefore using service bus (even with request/response model) would not make any sense, because we had to be sure that our message is always the first in the queue. Otherwise, we would have to wait for the remaining ones to be processed. That’s why HTTP forwarding is a better solution – there’s a need for doing one more HTTP call, but overall it pays off.

Of course, for that case, we could use HttpClient provided by C#. If you’re not familiar with it, here’s some example usage. Unfortunately, there are some drawbacks of using it:

  • One call takes a lot of code lines so you’ll probably end up with an additional wrapper class (because you follow DRY, don’t you?)
  • Adding more support for features like Authorization headers, query parameters causes wrapper class to be even bigger and it pulls you away from the original task
  • It’s the developer’s responsibility to dispose the client
  • It’s the developer’s responsibility to deserialize the response

Fortunately, there are few great libraries already existing, which can do all this stuff for you. My personal favorite is called RestEase.

 

Using RestEase in ASP.NET Core apps

RestEase is type-safe REST client for .NET Framework and .NET Core which aims to make interacting with remote REST endpoints easy, without adding unnecessary complexity. Looks like a perfect tool for doing HTTP forwarding! Let’s jump into the code and see how it works. Below I put the implementation of simple ASP.NET Core controller class which will represent the internal API of the microservice:

 


    [Route("api/[controller]")]
    public class ItemsController : Controller
    {
        private readonly IEnumerable<ItemReadModel>  _items = new List<ItemReadModel>
        {
            new ItemReadModel { Id = 1, Name = "PC", Price = 2000.99M },
            new ItemReadModel { Id = 2, Name = "Macbook", Price = 5000.99M },
            new ItemReadModel { Id = 3, Name = "TV", Price = 1500.99M },
        };

        
        [HttpGet]
        public IEnumerable<ItemReadModel> GetItems()
            => _items;
    }

 

Our task is to call this action from the API Gateway. The first thing we need to do is obviously adding the ReastEase to API Gateway’s csproj file:

 


<PackageReferenceInclude="RestEase"Version="1.4.4"/>

 

Next step is creating an interface inside API Gateway project which will represent the internal API of our microservice. Notice that to make it work:

  • Interface needs to be public
  • Method declaration needs to be decorated with HTTP method attribute provided by RestEase in which we specify the URL (without host part).
  • The return type must Task or Task<T>

Here’s an interface for our needs:

 


    public interface IMicroserviceApi
    {
         [Get("api/Items")]
         Task<IEnumerable<ItemReadModel>> GetItemsAsync();
    }

 

In the last step, we need to create the HTTP client for our microservice inside API Gateway’s controller and use it to make a call:

 


    [Route("api/[controller]")]
    public class ItemsController : Controller
    {
        private readonly IMicroserviceApi _microserviceApi;
        
        public ItemsController()
        {
            _microserviceApi = RestClient.For<IMicroserviceApi>("http://localhost:5001");
        }
        
        [HttpGet]
        public async Task<IEnumerable<ItemReadModel>> GetItemsAsync()
            => await _microserviceApi.GetItemsAsync();
    }

 

Now it’s the time to do some test. I ran both apps on Kestrel:

  • API Gateway – http://localhost:5000
  • Microservice – http://localhost:5001

Let’s do a call to the API Gateway and see the result:

 

 

Simply awesome! Of course, the library supports much more. Let’s say we’d like to create an endpoint for getting an item giving the id. The action inside the microservice looks as follows:

 


        [HttpGet("{id}")]
        public ItemReadModel GetItem(int id)
            => _items.SingleOrDefault(i => i.Id == id);

 

Once again, we need to create a corresponding method inside the interface:

 


[Get("api/Items/{id}")]

Task<ItemReadModel> GetItemAsync([Path] int id);

 

Here’s another new thing. All arguments put into the URL need to have Path attribute. The last step is the same, inside API Gateway use the created HTTP client for making a call:

 


        [HttpGet("{id}")]
        public async Task<ItemReadModel> GetItemAsync(int id)
            => await _microserviceApi.GetItemAsync(id);    

 

Here’s the result:

 

 

Of course, ReastEase can be used in every project that needs to communicate with some external API. It’s not just for the microservices and GET methods. The above examples were just a tip of the iceberg so, I encourage everyone to visit the GitHub page, go through the table of contents and see how powerful the RestEase is.