Playing around with headers in 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. If you want to read a getting started article on Ocelot, here is one! Right now I would like to concentrate on a single very specific yet interesting topic: HTTP headers.

What’s so important about HTTP headers?

The whole idea of HTTP headers is to offer client and server to send some additional information with requests and responses. In a typical client/server scenario things seem straightforward since the client and the service that responds communicate directly. in a microservices / service oriented architecture the client doesn’t actually communicate directly with the service, since all communication is brokered by the API gateway.

Some people may see this as a problem. I tend to see it as an opportunity instead. Why? Because this means that the API gateway can already do a lot of evaluations regarding the request and provide each service with additional information in form of custom HTTP headers. This would result in what I call a “fully qualified request” that arrives to each service. Meaning that the request contains just enough information that the service can process it as quickly as possible, without spending time to fulfill tasks not directly related to the response generation.

Use cases

One use case is typical for authentication / authorization. Systems should be protected and in an APIs world we usually to authentication and authorization based on the OpenID Connect and OAuth2.0 protocols. Long story short, this means that we’re working a lot with JWT tokens. Commonly, JWT tokens contain a lot of information in form of claims. And most commonly that information is not put there on tokens just because we can do that. It’s there because it’s needed.

When we have an API gateway like Ocelot one common thing that we’d like to do is take care of the JWT validation and authorization at the gateway level. This means that requests won’t even arrive to the different services something goes wrong during the token validation and authorization processes. Services won’t need to perform these tasks. But how do we pass the information from the access tokens to the services in this case?

Here’s where the API gateway should excel. An API gateway should be able to take the needed claims and add them as headers. This way, services will be able to easily consume the information without the whole hustle that comes with token deserialization.

There might be other use cases as well. For instance, if you want to do some analytics on all requests. This is also a scenario where the API gateway would be the best place to do it. And if information needs to be passed to services, you guessed it, we could add it to a header.

Headers in Ocelot

Fortunately, Ocelot comes with a lot of features when it comes to HTTP headers. We can choose to add custom headers to all requests, we can specify only certain re-routes that should have headers, we can take information from claims and add it as headers and much more. As with all other Ocelot features, this is something that is done in the configuration file, without the need to write a single line of code.

If you want to add a header to requests, it’ as easy as adding this configuration object to the Ocelot Re-Route object:

1
 

“UpstreamHeaderTransform”: {

“Uncle”: “Bob”

}

If you want to add a custom header to the responses from your services you just add this one:

1
2
3
"DownstreamHeaderTransform": {
"Uncle": "Bob"
},

You can also search for headers and modify them if needed. It’s all in the Ocelot documentation.

Set headers based on claims

The coolest use case from my point of view is the ability to take information from a JWT token’s claims and add it as headers to the request.  On the microservices project I’m working on this proves to be very efficient and helpful beyond expectations. And we can achieve this by simply adding some JSON objects to our configuration file:

1
2
3
4
5
6
7
8
"AuthenticationOptions": {
"AuthenticationProviderKey": "TestKey",
"AllowedScopes": []
},
"AddHeadersToRequest": {
"Username": "Claims[Username] > value",
"JobTitle": "Claims[JobTitle] > value"
}

How does this work

First of all to do that you need, of course, authentication (I’ll blog on this topic separately). Once this is done you can simply choose to add headers to requests by specifying each header’s key and the value should be looked up in the Claims array that results when deserializing a JWT token.

In my case I simply add a header called “Username” and the information from the header is extracted from the claim called “Username”. The same goes for “JobTitle”. If no such claims exist then Ocelot will still add the headers, but their value will be empty.

By doing this we can guarantee that when services will receive the request they’ll have that information in the headers and can perform whatever logic they need to perform based on that information.

Demo solution

If you want to better understand how Ocelot works and have an overview of the entire context, I have set up a GitHub repo. The entire demo app contains three different services (prodcuts, suppliers, users), an authentication service (auth), the Ocelot gateway (gateway) and a .Net Standard library with some helpers to extract header information.

All services are authenticated, meaning that to call each service you need a valid token. To get a token you can call the /tokens endpoint to get one. This endpoint is, of course, the only one that doesn’t require authorization. Add the received token as Authorization header to all subsequent requests. When running all the apps and making a request via Postman (or whatever other tool you might like) you’ll notice that the headers are always logged in the console for demo purposes.

Only note that most of the services are build on top .Net Core 3.0 so you’ll need the .Net Core 3.0 SDK to be able to run them.

Don’t hesitate to get in touch if you need any assistance.

 

How useful was this post?

Click on a star to rate it!

Average rating / 5. Vote count:

Dan Patrascu-Baba

Dan Patrascu-Baba

Developer, consultant, trainer at Codewrinkles
,Net Developer.Focusing on both the .Net world and Microsoft Azure. Experienced speaker and trainer.
Dan Patrascu-Baba
Spread the word!

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.