Using Ocelot as an API Gateway in a .NET Core Microservices Architecture

.Net Core Jun 5, 2024

Microservice introduce agility, scalability and autonomy for development teams but they also bring complexity in handling cross-cutting concerns like routing, authentication and load balancing across multiple services. An API Gateway centralizes these concerns, providing a single entry point for clients. In the .NET ecosystem, Ocelot is a lightweight, easy-to-configure API Gateway library for ASP.NET Core.

In this article, we will:

  1. Explain why you need an API Gateway in a microservice setup.
  2. Introduce Ocelot and its key features.
  3. Walk through setting up two sample .NET Core microservices.
  4. Configure an Ocelot Gateway project with routing, load balancing and authentication.
  5. Test the end-to-end setup.

Why Use an API Gateway?

  • Single Entry Point: Clients communicate with one service url (endpoint) instead of multiple service urls (e.g payment.mydomain.com, identity.mydomain.com, messaging.mydomain.com e.t.c)
  • Routing & Aggregation: Routes requests to appropriate downstream services and can combine responses.
  • Cross-cutting Features: Implement authentication, rate limiting, caching, logging and circuit breakers in one place.

Without a gateway, each client must track and call multiple endpoints directly, increasing coupling and exposure of internal service urls.

Introducing Ocelot

Ocelot is an open-source API Gateway library for .NET Core. Its core features include:

  • Routing: Map incoming (Upstream) paths to downstream services
  • Load Balancing: Round-robing, least connection e.t.c
  • Authentication & Authorization: Integrate with JWT, IdentityServer4 or custom schemes
  • Rate Limiting & Throttling: Protect downstream (internal) services from traffic spikes.
  • Caching & QoS: Reduce latency and implement fault tolerance.

Prerequisites

  • .NET 8 SDK
  • IDE: Visual Studio 2022/Rider IDE/VS Code
  • Basic knowledge of ASP.NET Core Web API

Create Sample Microservices

Service A (Product API)

//Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.MapGet("/api/products", () => new[] { new { Id = 1, Name = "Widget" } });
await app.RunAsync();

Service B (Order API)

//Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.MapGet("/api/orders", () => new[] { new {OrderId = 100, ProductId = 1, Quantity = 2 }});
await app.RunAsync();

Run each service on a different port (e.g 5001 for Products, 5002 for Orders).

Create the Ocelot Gateway Project

    1. dotnet new web -n ApiGateway
    2. cd ApiGateway
    3. dotnet add package Ocelot
    4. Create a ocelot.json
{
  "Routes": [
    {
      "DownstreamPathTemplate": "/api/products",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [{ "Host": "localhost", "Port": 5001 }],
      "UpstreamPathTemplate": "/products",
      "UpstreamHttpMethod": [ "GET" ]
    },
    {
      "DownstreamPathTemplate": "/api/orders",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [{ "Host": "localhost", "Port": 5002 }],
      "UpstreamPathTemplate": "/orders",
      "UpstreamHttpMethod": [ "GET" ]
    }
  ],
  "GlobalConfiguration": {
    "BaseUrl": "http://localhost:7000"
  }
}

Program.cs for Gateway

var builder = WebApplication.CreateBuilder(args);

//Load ocelot config
builder.Configuration.AddJsonFile("ocelot.json");

//Register Ocelot
builder.Services.AddOcelot(builder.Configuration);

var app = builder.Build();

app.MapGet("/", () => "Api Gateway is Active");

await app.UseOcelot();

Run the gateway on port 7000 using dotnet run --urls "http://localhost:7000".

Test the setup

With all three projects running, open a terminal or postman:

# Products via gateway
curl http://localhost:7000/products

#Orders via gateway
curl http://localhost:7000/orders

Responses from downstream services should flow through the gateway seamlessly.

Advanced Features

Load Balancing

Add multiple ports for a downstream service and set:

{
  "DownstreamHostAndPorts": [
    {
      "Host": "localhost",
      "Port": 5001
    },
    {
      "Host": "localhost",
      "Port": 5003
    }
  ],
  "LoadBalancerOptions": {
    "Type": "RoundRobin"
  }
}

JWT Authentication

  1. Protect downstream APIs with JWT
  2. In ocelot.json, add an AuthenticationOptions section:
{
  "AuthenticationOptions": {
    "AuthenticationProviderKey": "JwtAuth",
    "AllowedScopes": []
  }
}
  1. In Program.cs, configure JWT bearer
builder.Services.AddAuthentication()
  .AddJwtBearer("JwtAuth", options => { /* configure */ });

app.UseAuthentication();
app.UseAuthorization();

Rate Limiting & Caching

Configure RateLimitOptions and FileCacheOptions in ocelot.json to protect services and reduce latency.

Conclusion

Using Ocelot as an API Gateway in your .NET Core microservices setup simplifies routing, security and cross-cutting concerns. This article walked through:

  • Creating minimal microservices
  • Configuring an Ocelot Gateway
  • Testing routes
  • Extending with load balancing and authentication

Explore additional Ocelot middleware such as circuit breakers, aggregation, dynamic configuration to further harden your gateway.

Tags

Views: Loading...