YARP (Yet Another Reverse Proxy) è una potente libreria .NET che permette di creare server reverse proxy ad alte prestazioni, pronti per la produzione e altamente personalizzabili.

Per gli sviluppatori impazienti di buttarsi nel codice, il progetto completo è disponibile su GitHub: APIGateway.NET

Perché usare YARP ?

YARP (Yet Another Reverse Proxy) emerge come un’alternativa chiara e potente a Ocelot per l’implementazione di API Gateway in .NET. Offre una soluzione robusta e flessibile, particolarmente adatta per progetti moderni basati su architetture a microservizi.

  • Semplicità e chiarezza: A differenza di Ocelot, YARP offre un’API più intuitiva e una documentazione più completa. La sua struttura e il suo utilizzo sono progettati per essere più facilmente comprensibili, riducendo la curva di apprendimento per gli sviluppatori.
  • Prestazioni superiori: YARP è ottimizzato per offrire prestazioni elevate, superando Ocelot in scenari di carico intensivo. Supporta nativamente HTTP/2 e gRPC, rendendolo ideale per applicazioni moderne che richiedono alte prestazioni e bassa latenza.
  • Maggiore flessibilità: YARP permette una personalizzazione più granulare rispetto a Ocelot. Gli sviluppatori possono facilmente estendere e modificare il comportamento del proxy direttamente attraverso il codice .NET, offrendo un controllo più preciso sulla logica di routing e trasformazione delle richieste.

Implementazione

Creazione soluzione

Usando Visual Studio o il tuo IDE preferito, iniziamo con la creazione della soluzione. A tal fine, è sufficiente creare due progetti ASP.NET Core Web API, come illustrato nell’immagine.

api gateway dotnet

Configurazione progetto APIGateway

Per iniziare la configurazione del progetto che fungerà da punto d’ingresso, devi installare la libreria YARP.ReverseProxy. Puoi farlo usando il NuGet Package Manager oppure eseguendo questo comando:

dotnet add package Yarp.ReverseProxy

Registrazione servizio

Dopo aver installato la libreria, registrala nei servizi:

using System.Threading.RateLimiting;
using Microsoft.AspNetCore.RateLimiting;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddControllers();
builder.Services.AddHealthChecks();

// YARP - Register Service
builder.Services.AddReverseProxy().LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

builder.Services.AddSwaggerGen();


// YARP - Configure RateLimiter Service to prevent DoS attacks
builder.Services.AddRateLimiter(options =>
{
    options.AddFixedWindowLimiter("customPolicy", opt =>
    {
        opt.PermitLimit = 1;
        opt.Window = TimeSpan.FromSeconds(4);
        opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
        opt.QueueLimit = 1;
    });
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.MapControllers();

app.MapHealthChecks("healthy");

// YARP - Configure RateLimiter Application Part to prevent DoS attacks
app.UseRateLimiter();

// YARP - Add Application Part
app.MapReverseProxy();

await app.RunAsync();

La componente RateLimiter non è indispensabile, ma integrarla aumenterà la sicurezza del gateway. Con la configurazione attuale, il gateway introdurrà un ritardo di 4 secondi nel caso di chiamate simultanee.

Configurazione rotte

Affinché il gateway operi correttamente, è indispensabile configurare le rotte delle diverse applicazioni che saranno ospitate. Modifica il file appsettings.json aggiungendo la seguente configurazione:

"ReverseProxy": {
  "Routes": {
    "get-customers": {
      "ClusterId": "customers",
      "RateLimiterPolicy": "customPolicy",
      "Match": {
        "Path": "/customers/all",
        "Methods": [
          "GET"
        ]
      }
    },
    "create-customer": {
      "ClusterId": "customers",
      "Match": {
        "Path": "/customers/create",
        "Methods": [
          "POST"
        ]
      },
      "Transforms": [
        {
          "RequestHeader": "X-Added-Website",
          "Set": "https://CosminIrimescu.COM"
        }
      ]
    }
  },
  "Clusters": {
    "customers": {
      "Destinations": {
        "customers/destination1": {
          "Address": "http://localhost:5010/"
        }
      }
    }
  }
}

Configurazione progetto APIGateway.Customers

È il momento di configurare il progetto che esporrà le API attraverso l’APIGateway.

using Microsoft.AspNetCore.Mvc;

namespace APIGateway.Customers.Controllers;

[ApiController]
[Route("[controller]/[action]")]
public class CustomersController : ControllerBase
{
    private static readonly List<string> _customers = ["Cst1", "Cst2", "Cst3"];

    [HttpGet]
    public Task<IActionResult> All()
    {
        return Task.FromResult<IActionResult>(Ok(_customers));
    }

    [HttpPost]
    public Task<IActionResult> Create([FromQuery] string customer)
    {
        const string HeaderKeyName = "X-Added-Website";
        Request.Headers.TryGetValue(HeaderKeyName, out var headerValue);
        _customers.Add(customer);
        return Task.FromResult<IActionResult>(Ok(new
        {
            Customers = _customers,
            CustomHeader = headerValue
        }));
    }
}

Nella funzione Create viene recuperato un header personalizzato, definito nella configurazione delle rotte all’interno del file appsettings.json del progetto APIGateway, e restituito come payload. Sebbene questo header non sia necessario, serve a comprendere le potenzialità di YARP.

Registrazione Controllers

Con l’introduzione di .NET 8, il template ASP.NET Core Web API non registra più automaticamente i controller. Pertanto, è necessario modificare il file Program.cs per aggiungere manualmente la registrazione e la mappatura dei controller, come mostrato nel codice seguente.

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddControllers();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.MapControllers();

await app.RunAsync();

Test Gateway

Ora che l’implementazione è completata, i due progetti possono essere avviati per testare il gateway. Il progetto APIGateway è configurato per l’esecuzione sulla porta 5000, mentre APIGateway.Customers è impostato sulla porta 5010. Con entrambe le applicazioni in esecuzione, è possibile aprire un terminale e testare il gateway utilizzando i seguenti comandi curl:

Creazione nuovo customer

curl --location --request POST 'http://localhost:5000/customers/create?customer=nuovo%20cst'

Elenco dei customers

curl --location 'http://localhost:5000/customers/all'

Conclusione

I gateway API rappresentano un elemento fondamentale nelle architetture moderne, soprattutto quando si tratta di microservizi. Offrono un punto centrale per gestire le richieste, semplificare la comunicazione tra client e servizi, e applicare logiche trasversali come autenticazione, autorizzazione e caching. YARP, in particolare, si distingue per la sua flessibilità e integrazione nativa con ASP.NET Core, rendendolo una scelta eccellente per chi lavora già in questo ecosistema. È una soluzione che consente di affrontare con semplicità le sfide della gestione delle API, adattandosi facilmente a diversi scenari e necessità.