Last Saturday I attended .Net Summit Belarus in Minsk, contributing to the event with a talk on how to build powerful API gateways using the Ocelot library and ASP.Net Core. Before diving into the technical part I feel the need to underline that this was one of the nicest conferences I ever attended. Congrats and many thanks to the organizers and the entire speaker line-up.
As I tend to do after each conference, here’s a short technical summary of my talk.
Microservices – a short overview
I certainly won’t dive deep into what a microservices architecture should look like, but a certain baseline is needed to better understand the role of an API gateway. Commonly, in a microservices architecture we tend to split functionalities into self-sufficient services. Thinking about a e-shop application, it could be split into a service that handles the product catalogs, a service that is responsible for users, another service that is responsible for order processing and so on. Each service should be self-sufficient, so it should not rely on another service to perform its job.
This kind of setup implies that each service will be deployed on a different host or different host and port. But what does that mean for a client application?
Well, a client or consumer should be aware of all the different services and call them as they are needed. This is certainly a mess since each client/consumer needs to know all the hosts and ports on which different services are running. This is a total nightmare, taking into consideration that ports and hosts might change so the client would need to also keep track of that.
API gateways FTW!
This is precisely where API gateways come in. They should be a central entry point to the entire system. Which means that clients/consumers only need to keep track of the API gateway. Each time a client would need something it would make a request to the API gateway and the gateway would be responsible to pick the service that would be able to fulfill the request.
Having a central entry point to the system also brings other advantages. First of all, the gateway could be used to centralize some policies, error handling in responses, authentication, authorization, caching and so on. Further, an API gateway could be responsible to aggregate responses in order to avoid over or under-fetching.
What does this actually mean? Well let’s assume that the client would need to display a detailed order information to a user. To do that it might need information from the product catalog service, from the user service, from the order service and so on. So it would still need to make 3 different calls through the API gateway. To avoid this, an API gateway could aggregate responses. Hence the client would do only one request and the gateway would be responsible to fetch all the needed information.
To summarize, an API gateway should be able to perform routing, to transform headers, to handle authentication / authorization, to provide service discovery capabilities, rate limiting, load balancing and aggregate responses. To protect against internet-based threats without harming end-user experience, you can use a secure web gateway.
So, tough job if you, as a developer, need to develop an API gateway from scratch, isn’t it?
Ocelot to the rescue!
What if I told you that you could actually do this without writing any line of code? Sure, I’m exaggerating a little bit, but Ocelot is an open source library that provides all mentioned capabilities (and many more) and mostly everything is configured in a JSON file. So yes, theoretically you just need to write a bunch of JSON configuration and everything is set up.
Another cool thing about Ocelot is that it can be easily extended. Ocelot is nothing else than a bunch of middleware that is responsible to apply the defined configuration. Hence you can extend it either by placing your own middleware before the Ocelot middleware or by creating your custom delegating handlers (by the way, Steve Gordon has an excellent write up on delegating handlers in his HttpClientFactory series, so you might want to check it out).
To better understand how this should work, let’s take a look under Ocelot’s hood!
Ocelot under the hood
A cliche says that a picture is worth 1000 words. Well, in this case it’s really true. So here’s a brief overview of a request/response flow with Ocelot:
What we need to understand here is that as a request comes in, it is sent down the Ocelot middleware pipeline. The main role of each middleware is to shape the request in such a way that it is compliant to all configuration defined in the ocelot.json file. The last middleware in this pipeline is the so-called request builder middleware. The interesting thing of this middleware is that it is actually creating a request for the needed service based on the form of the incoming request. It waits then for the service to respond and it generates afterwards the response that is sent to to requesting client.
Configuring Ocelot is easy! And it’s really similar to the configuration we usually to in files like appsettings.json in ASP.Net core. The Ocelot configuration file should be however called ocelot.json and it should contain an ReRoutes array and a GlobalConfiguration object.
Sure, the configuration gets messier when you start working on it. This happens because each Ocelot feature that we want to use will be reflected in the configuration one way or another. In the end, a single re-route may look like this (and bare in mind that this is a very simple configuration)
However, don’t be afraid since re-routes will be very similar so once you have the first one ready, it’s easy to create all the other re-routes.
Talking about routing, are-route is nothing else than a bunch of configuration that defines where a certain information is to be found. That’s why a re-route configuration has two important parts:
- Upstreamm path: this is basically the URL to which a client makes the request. The host will be of course the API gateway, so the upstream path should identify the needed resource. Here we can specify the HTTP methods associated to that upstream path.
- The downstream path template represents the service to which the API gateway should route the request in order to get the needed information. Here you can specify the HTTP scheme as well as the host and port on which the service is running
Authentication and Authorization in Ocelot
If you worked with authentication and authorization in ASP.Net Core then this should be easy. Ocelot can be easily configured to work with providers like Identity Server 4, Auth0 and Okta, but in the end it’s just adding authentication to an ASP.Net Core application. The main difference here is that you need to specify for each ReReoute which authentication method should be used. You can however find a lot more detailed information in their documentation.
That’s mostly it. You can find the related slide deck HERE.
The basic setup that I used for the demo is on Github so you may take a look at it anytime.
Overall, Ocelot seems like a very good choice if you need an easy to configure yet extensible API gateway. I have used it now for several months, maybe almost a year, and till now everything worked really well.
How useful was this post?
Click on a star to rate it!
Average rating / 5. Vote count:
Latest posts by Dan Patrascu-Baba (see all)
- Configuration and environments in ASP.NET Core - 25/11/2019
- GraphQL in the .NET ecosystem - 19/11/2019
- A common use case of delegating handlers in ASP.NET API - 12/11/2019