33 API Gateway with ASP.NET Core + YARP or Azure APIM
C# is an excellent choice for implementing an API Gateway on Azure. Let me show you the approaches and a concrete example.
33.1 Two Main Approaches
1. Managed Service: Azure API Management (APIM)
This is Azure’s fully managed API Gateway service that handles all the features you listed out-of-the-box.
2. Custom Gateway: ASP.NET Core with YARP
Build your own using ASP.NET Core and reverse proxy libraries.
┌─────────────────────────────────────────────────────────────┐
│ Approach Comparison │
├─────────────────────────────────────────────────────────────┤
│ │
│ Azure APIM Custom ASP.NET Core + YARP │
│ ├─ Fully Managed ├─ Full Control │
│ ├─ Built-in Auth/RBAC ├─ Custom Logic │
│ ├─ Built-in Rate Limiting ├─ Fine-grained Config │
│ ├─ WAF Integration ├─ Lighter Weight │
│ ├─ Portal & Analytics ├─ Lower Cost (compute only) │
│ └─ Higher Cost └─ More Dev Work │
│ │
└─────────────────────────────────────────────────────────────┘
33.2 Custom API Gateway Example (ASP.NET Core + YARP)
Here’s a simplified implementation that handles routing, auth, and rate limiting:
Project Structure:
APIGateway/
├── Program.cs
├── appsettings.json
├── Middleware/
│ ├── RateLimitingMiddleware.cs
│ └── CorsMiddleware.cs
└── APIGateway.csproj
appsettings.json - Configuration for routes and backend services:
{
"ReverseProxy": {
"Routes": {
"ai-service-1-route": {
"ClusterId": "ai-service-1",
"Match": {
"Path": "/api/inference/v1/{**catch-all}"
},
"Transforms": [
{ "PathPattern": "/v1/{**catch-all}" }
]
},
"ai-service-2-route": {
"ClusterId": "ai-service-2",
"Match": {
"Path": "/api/inference/v2/{**catch-all}"
},
"Transforms": [
{ "PathPattern": "/v2/{**catch-all}" }
]
}
},
"Clusters": {
"ai-service-1": {
"Destinations": {
"destination1": {
"Address": "https://ai-service-1.azurewebsites.net"
}
}
},
"ai-service-2": {
"Destinations": {
"destination1": {
"Address": "https://ai-service-2.azurewebsites.net"
}
}
}
}
},
"Authentication": {
"Authority": "https://login.microsoftonline.com/{tenant-id}",
"Audience": "api://your-api-gateway"
}
}Program.cs - Main gateway setup:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
// Add YARP Reverse Proxy
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
// Add Azure AD Authentication (OAuth 2.0 / OIDC)
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("Authentication"));
// Add Authorization
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireAuthenticatedUser", policy =>
policy.RequireAuthenticatedUser());
options.AddPolicy("AdminOnly", policy =>
policy.RequireRole("Admin"));
});
// Add CORS
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowWebClients", policy =>
{
policy.WithOrigins("https://your-web-client.com")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
// Add Rate Limiting (ASP.NET Core 7+)
builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter("fixed", limiterOptions =>
{
limiterOptions.PermitLimit = 100;
limiterOptions.Window = TimeSpan.FromMinutes(1);
limiterOptions.QueueProcessingOrder = System.Threading.RateLimiting.QueueProcessingOrder.OldestFirst;
limiterOptions.QueueLimit = 10;
});
});
var app = builder.Build();
// Configure Middleware Pipeline
app.UseCors("AllowWebClients");
app.UseAuthentication();
app.UseAuthorization();
app.UseRateLimiter();
// Map reverse proxy with auth policy
app.MapReverseProxy(proxyPipeline =>
{
proxyPipeline.UseSessionAffinity();
proxyPipeline.UseLoadBalancing();
});
app.Run();33.3 Request Flow Diagram
Client Request
│
▼
┌─────────────────────────────┐
│ CORS Middleware │ Check origin
└─────────────────────────────┘
│
▼
┌─────────────────────────────┐
│ Authentication │ Validate JWT token
│ (Azure AD / OAuth 2.0) │
└─────────────────────────────┘
│
▼
┌─────────────────────────────┐
│ Authorization │ Check roles/claims
│ (RBAC) │
└─────────────────────────────┘
│
▼
┌─────────────────────────────┐
│ Rate Limiting │ Check request quota
└─────────────────────────────┘
│
▼
┌─────────────────────────────┐
│ YARP Reverse Proxy │ Route to backend
│ - Route Matching │
│ - Load Balancing │
│ - Path Transformation │
└─────────────────────────────┘
│
▼
Backend Service (AI Inference)
33.4 Key Features Implemented
Authentication (OAuth 2.0 / OIDC): - Uses Azure AD / Microsoft Identity Platform - Validates JWT tokens automatically - Extracts user claims for authorization
RBAC (Role-Based Access Control): - Define policies based on roles in JWT claims - Apply at route level or controller level
Rate Limiting: - Built-in ASP.NET Core 7+ rate limiter - Per-user or per-IP limiting possible
CORS: - Configure allowed origins - Handle preflight requests
Routing: - YARP handles path transformation - Load balancing across multiple instances - Session affinity (sticky sessions) support
33.5 NuGet Packages Needed
<ItemGroup>
<PackageReference Include="Yarp.ReverseProxy" Version="2.1.0" />
<PackageReference Include="Microsoft.Identity.Web" Version="2.15.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" />
</ItemGroup>33.6 When to Choose Each Approach
Use Azure APIM when: - You need enterprise features (analytics, developer portal, monetization) - Multiple teams consume your APIs - You want zero-code API management - Budget allows for managed service costs
Use Custom ASP.NET Core Gateway when: - You need fine-grained control - Budget is tight (only pay for compute) - You have specific custom requirements - Your team is comfortable with C# development