Building your own ASP.Net Core API gateway with Ocelot

I’m currently working on some very interesting stuff and one of it is building an Asp.Net Core API gateway. An API gateway might be a very useful part in a service oriented architecture using the micro services approach. In such an architecture you might have a lot of different APIs, each responsible only for one specific thing. Without an API gateway, consumers would need to send requests to each API and then aggregate the responses. An API gateway would do exactly this for the consumers (and many more of course) so that they would need to send only one request to one endpoint and the gateway would aggregate the needed response.

Building an API gateway from scratch is not that trivial. There are a lot of things that you need to take into consideration like authentication and authorization, service discovery, response aggregation, throttling and so on. Naturally when I started to work on this I was looking for a library that would help me in a certain way to not loose track of important aspects. Fortunately, I discovered Ocelot and I was able to implement a basic proof of concept (containing however all the main features we’ll need) in two days. So I thought I might want to share my thoughts about Ocelot, since I think this information might be useful for other developers that will see themselves facing a similar task.

What is Ocelot?

Ocleot is an open source product aimed at people using .NET running a micro services / service orientated architecture that need a unified point of entry into their system. Ocelot is fast, scalable and provides mostly all features you consider as mandatory when building an API gateway. It is designed to work with .NET Core only and is currently built to netcoreapp2.0. They also have a very detailed documentation.

Initial configuration

A getting started guide is available on Ocelot’s documentation page. However, the best setting up guide was written by Thiago Passos and I strongly recommend to read Thiago’s article since I won’t duplicate the information an provide a similar guide myself. What I want to do is to focus on different requirements I had and how I managed to implement them with Ocelot.

To make just a short description, Ocelot is easily configurable using a “configuration.json” file where you can configure all your needed features. Two concepts are in my opinion really important: DownstreamPathTemplate and UpStreamPathTemplate. The “downstream” path is the path to the API back end where you want to redirect a certain request, while the “upstream” path is the path that consumers need to hit to get a desired response. Consumers need only to be aware of the “upstream”, since that’s the endpoint they will work with. Of course you can have a lot of such paths configured. Regarding the initial configuration, once you get these concepts right, you’ll find it easy to work with Ocelot further.

Authentication

If you want to authenticate using JWT tokens from a provider like Auth0 you just need to add authentication to ConfigureServices with the desired authentication options. Ocelot will perform the token validation and if the token is invalid you’ll receive an “Unauthorized” response.


var authenticationProviderKey = "One";

services.AddAuthentication()
.AddJwtBearer(authenticationProviderKey, o =>;
{
o.Authority = "https://<domain>.auth0.com/";
o.Audience = "https://<domain>.auth0.com/api/v2/";

})

One very cool things is that you can configure several JWT bearer options and use different authentication/authorization providers for different Ocelot ReRouts.


var authenticationProviderKey = "One";
var authenticationProviderKey2 = "Two";
services.AddAuthentication()
.AddJwtBearer(authenticationProviderKey, o =>;
{
o.Authority = "https://<domain>.auth0.com/";
o.Audience = "https://<domain>.auth0.com/api/v2/";

})
.AddJwtBearer(authenticationProviderKey2, o =>;
{
o.Authority = "https://<different_domain>.auth0.com/";
o.Audience = "https://<different_domain>.auth0.com/api/v2/";
});

And in configuration.json:



{
"DownstreamPathTemplate": "/api/v1/something/somereports",
"DownstreamScheme": "http",
"UpstreamPathTemplate": "/somereports",
"UpstreamHttpMethod": [ "Get", "Post" ],
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 64548
}
],
"AuthenticationOptions": {
"AuthenticationProviderKey": "One",
"AllowedScopes": []
}

},
{
"DownstreamPathTemplate": "/",
"DownstreamScheme": "http",
"UpstreamPathTemplate": "/dan",
"UpstreamHttpMethod": [ "Get"],
"DownstreamHostAndPorts": [
{
"Host": "danpatrascu.com",
"Port": 80
}

],
"AuthenticationOptions": {
"AuthenticationProviderKey": "Two",
"AllowedScopes": []
}

}


So, for the first ReRoute, Ocelot will use the options under the provider key called “One” and for the second ReRoute Ocelot will use the options under the provider key “Two”. Which is cool!

Regarding authorization, Ocelot supports claim based authorization. This occurs, of course after the authentication process. And you can easily configure authorization also via configuration.json:


"RouteClaimsRequirement": {
"UserType": "registered"
}

Hence a request not having the specified claim on the access token won’t get authorized to access the resource.

Implement own logic on incoming requests

In my case I needed to find a way to perform some logic on each incoming request that would, in some cases, also modify the upstream path, to make sure that the request is re-routed to the appropriate downstream path. Since Ocelot doesn’t seem to be very open to such “on the fly” logic, I had to find a proper way to achieve my goal. And I remembered of course that I wrote two different articles on ASP.Net core middleware. Ocelot is basically a middleware (at least that’s what we see, because under the hod Ocelot consists of several middleware), so I could implement a custom middleware before the Ocelot one. Hence when a request will hit Ocelot, the needed logic is already applied.

As a first proof of concept I wanted to change the request path so that requests for “/dan” will be changed in requests for “/somereports”. And that was important, because requests for “/dan” are routed to one downstream, while requests for “/somereports” are routed to another downstream (each also with different authentication provider keys and so on). Doing this in Startup.cs was trivial:


app.Use(async (context, next) =>
{

string newPath = null;
var url = context.Request.Path;
if (url == "/dan")
{
newPath = "/somereports";
context.Request.Path = newPath;
context.Request.Method = "POST";
context.Request.ContentType = "application/json";

}

await next();

});

app.UseOcelot().Wait();

And it works as expected. Of course, one could use custom middleware to also manipulate the response before leaving the server and so on. So, custom logic might be added using custom middleware, to make sure that once a requests hits the Ocelot middleware, it has the desired content needed for Ocelot to perform the expected actions.

Other features

Other features provided by Ocelot are response aggregation, service discovery, rate limiting (throttling), caching, logging and many more. Everything is configurable via configuration.json in a matter of minutes. I’m not sure yet if it’s a good thing that everything is configurable via the configuration JSON file or not. On one hand, configuration is made very easy, but on the other hand it doesn’t allow the addition of custom logic when configuring the API gateway.

As a short conclusion, I was able to implement all requirements for the API gateway using Ocelot. All testing went well. I wouldn’t have any concerns to go ahead and build a beta version of an API gateway using Ocelot and test how it’s working in production. Many thanks to the entire Ocelot team that spend their free time developing this product! Since this is an open source effort, if anybody reading this article would like to contribute, here’s the GitHub repo. So you may get in touch with the team.

Sorry for the long post, but if you made it to this point, I really thank you for your patience and hope you’ve found some useful information here. And if you think it was useful, don’t be shy and share it with your network; they might find it useful too. If you also tried Ocelot out and want to share something, then hit me with your comment 🙂 Cheers!

Leave a Reply

Your email address will not be published. Required fields are marked *