Fermer

décembre 12, 2024

Premiers pas avec les passerelles API dans ASP.NET Core

Premiers pas avec les passerelles API dans ASP.NET Core


Une passerelle API fournit un point central pour gérer le routage, la sécurité et l’agrégation des données dans les applications Web. Découvrez les principaux avantages de cette approche, tels que la sécurisation des API en créant une couche externe, la limitation du débit d’accès et l’optimisation des performances à l’aide d’Ocelot.

Les API Web sont connues pour intégrer et communiquer entre différentes applications via des requêtes.

Pour qu’une API continue de fonctionner, il est généralement nécessaire de la surveiller afin qu’elle ne manque pas de ressources lorsqu’elle reçoit un nombre important de requêtes. De plus, d’autres exigences sont nécessaires, telles que l’authentification, l’autorisation, l’optimisation des performances, le traitement des données et autres.

Supposons que vous disposiez de plusieurs API. Imaginez combien de travail il faudrait pour mettre en œuvre ces mécanismes dans chacun !

Les passerelles API encapsulent tous ces processus, créant des mécanismes unifiés pouvant être partagés par les applications. Dans cet article, nous en apprendrons davantage sur les passerelles API et mettrons en pratique un exemple en utilisant Ocelot.

Qu’est-ce qu’une passerelle API et pourquoi est-elle importante ?

Une passerelle API est un composant intermédiaire qui fait office de pont entre les clients et les services.

Sa fonction principale est de recevoir les demandes des clients, de les acheminer vers les services internes configurés, puis de renvoyer ces réponses. En plus de gérer le routage, une passerelle API peut unifier les réponses de plusieurs services, appliquer l’authentification et l’autorisation, effectuer des transformations de données, mettre en œuvre un contrôle du trafic et fournir une surveillance centralisée.

En ce sens, une API Gateway offre plusieurs avantages, notamment dans les architectures basées sur des microservices. Parmi ces avantages, on peut souligner les suivants :

  • Routage et abstraction des services : Une passerelle API centralise les requêtes, reçoit les appels des clients et les transmet aux microservices correspondants. Cela permet d’abstraire la complexité interne des services, offrant ainsi une expérience plus simple aux consommateurs, qui n’ont pas à s’inquiéter excessivement lorsqu’ils appellent une API donnée.
  • Sécurité centralisée : Au lieu d’implémenter l’authentification et l’autorisation dans chaque microservice individuellement, il est possible de configurer ces politiques de manière unifiée directement dans la passerelle. Cela réduit la duplication de code et fournit un point de contrôle centralisé, facilitant l’application de politiques telles que l’utilisation de jetons JWT ou l’authentification basée sur OAuth2.
  • Optimisation des performances : La passerelle peut aider à optimiser considérablement les performances en regroupant plusieurs appels vers des microservices en une seule requête. Cela réduit le nombre d’appels aller-retour que le client doit effectuer, économisant ainsi du temps et de la bande passante.
  • Équilibrage de charge et basculement : Une passerelle API peut répartir les requêtes entre les instances de microservices afin que la charge soit efficacement équilibrée. Il peut également surveiller la disponibilité des services et rediriger les demandes vers des services sains en cas de panne.
  • Traitement des données : La passerelle peut servir de middleware, convertissant les formats de requête et de réponse selon les besoins. Par exemple, transformer une requête REST en appel GraphQL ou encore normaliser les données pour différentes versions d’API.
  • Contrôle de la circulation : Il est facile de limiter le nombre de requêtes par client ou d’appliquer des politiques de délai d’attente au niveau de la passerelle, empêchant ainsi les microservices individuels d’être surchargés d’appels excessifs.
  • Surveillance centralisée : Les passerelles API constituent un point central pour la collecte des métriques et des journaux, ce qui facilite la surveillance des performances, le suivi des pannes et l’activité d’audit.

Organigramme de la passerelle API

Implémentation d’une passerelle API avec Ocelot

Dans le contexte d’ASP.NET Core, il existe d’excellents outils permettant d’implémenter une passerelle API. L’un des plus utilisés est Ocelot.

Ocelot est un package NuGet qui agit comme un ensemble de middleware regroupant plusieurs fonctions présentes dans les passerelles API, telles que le routage, l’authentification et l’autorisation, la limitation de débit, l’agrégation de requêtes et autres.

Pour explorer les capacités d’Ocelot, nous allons créer une API Web pour récupérer les enregistrements d’un système de gestion de contenu (CMS), puis nous créerons un autre projet pour ajouter Ocelot et créer la passerelle API.

Dans cet exemple, nous ne nous concentrerons pas sur la structure de l’API, car elle n’est pas pertinente pour le contexte de la publication. Nous créerons donc uniquement ce qui est essentiel pour implémenter Ocelot. Vous pouvez personnaliser l’exemple d’API si vous le jugez nécessaire ; l’exemple fourni dans le message est uniquement à des fins de démonstration.

Un autre point important est qu’Ocelot peut ne pas fonctionner pour les anciennes versions de .NET, telles que .NET Framework, il est donc important que, lorsque vous reproduisez l’exemple dans l’article, vous disposiez d’une version récente de .NET. La publication utilise .NET 8.

Vous pouvez accéder au code source du projet dans ce référentiel GitHub : Publier le code source du CMS.

Pour créer l’exemple d’API, vous pouvez utiliser les commandes suivantes.

Création de la solution :

dotnet new sln -n PublishCMS

Créer le projet API et l’ajouter à la solution :

dotnet new webapi -n Content.Api
dotnet sln add Content.Api/Content.Api.csproj

Ensuite, ouvrez le projet et créez les classes suivantes ci-dessous :

namespace Content.Api.Models;
public class ContentItem
{
    public Guid Id { get; set; }
    public string Title { get; set; }
    public string Body { get; set; }
    public string Author { get; set; }
    public List<string> Categories { get; set; }
    public DateTime PublishedDate { get; set; }
}
using Content.Api.Models;

namespace Content.Api.Services
{
    public interface IContentService
    {
        IEnumerable<ContentItem> GetContentItems();
    }
}
using Content.Api.Models;

namespace Content.Api.Services;
public class ContentService : IContentService
{
    public IEnumerable<ContentItem> GetContentItems()
    {
        return new List<ContentItem>
        {
                new ContentItem
                {
                    Id =  new Guid("b198e834-b670-40c9-99bd-4a00c02df3b8"),
                    Title = "Understanding Microservices Architecture",
                    Body = "Microservices architecture allows building systems as a suite of independently deployable services. It helps in scaling and development efficiency...",
                    Author = "John Doe",
                    Categories = new List<string> { "Software Development", "Architecture", "Microservices" },
                    PublishedDate = new DateTime(2023, 5, 9)
                },
                new ContentItem
                {
                    Id = new Guid("59d01fb0-a679-40f3-adac-407af3cea5ad"),
                    Title = "The Rise of Artificial Intelligence in Healthcare",
                    Body = "AI is revolutionizing the healthcare industry by providing innovative solutions for diagnosis, patient care, and operational efficiency...",
                    Author = "Joe Smith",
                    Categories = new List<string> { "Healthcare", "AI", "Innovation" },
                    PublishedDate = new DateTime(2023, 6, 7)
                },
                new ContentItem
                {
                    Id = new Guid("80118e83-facf-49ee-b92e-67c1aafa33f6"),
                    Title = "Top 10 Investment Strategies for 2024",
                    Body = "With the global economy facing uncertainty, it's crucial to have a diversified investment portfolio. Here are the top 10 strategies to consider in 2024...",
                    Author = "Michael Cart",
                    Categories = new List<string> { "Finance", "Investment", "Economy" },
                    PublishedDate = new DateTime(2024, 1, 3)
                },
                new ContentItem
                {
                    Id = new Guid("3c4ae9f5-b3ec-47d7-ad93-aff609ac920e"),
                    Title = "How to Cook the Perfect Steak: A Guide for Beginners",
                    Body = "Cooking a perfect steak requires the right balance of heat, timing, and technique. Follow these steps to impress at your next dinner party...",
                    Author = "Sarah Lever",
                    Categories = new List<string> { "Cooking", "Lifestyle", "Food" },
                    PublishedDate = new DateTime(2023, 8, 9)
                }
        };
    }
}
using Content.Api.Services;
using Microsoft.AspNetCore.Mvc;

namespace Content.Api.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class ContentController : ControllerBase
    {
        private readonly IContentService _contentService;

        public ContentController(IContentService contentService)
        {
            _contentService = contentService;
        }

        [HttpGet]
        public IActionResult GetAll() => Ok(_contentService.GetContentItems());
     }
}
using Content.Api.Services;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IContentService, ContentService>();

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddControllers();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.MapControllers();
app.UseHttpsRedirection();
app.Run();

Changer le Properties/launchSettings.json pour définir le port API sur 5001, car il sera utilisé par la passerelle API.

Définition du port API

Nous avons ici une API simple, qui renvoie simplement quelques exemples de données de contenu pour un blog. Créons maintenant le projet Ocelot. Pour ce faire, exécutez les commandes ci-dessous :

Créer le projet de passerelle API et l’ajouter à la solution :

dotnet new webapi -n Content.ApiGateway
dotnet sln add Content.ApiGateway/Content.ApiGateway.csproj

Ajout du package Ocelot NuGet :

cd Content.ApiGateway
dotnet add package Ocelot

Ouvrez le projet et remplacez le code existant par le code ci-dessous :

using Ocelot.DependencyInjection;
using Ocelot.Middleware;

var builder = WebApplication.CreateBuilder(args);

builder.Configuration.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);
builder.Services.AddOcelot();

var app = builder.Build();

await app.UseOcelot();

app.Run();

Notez que nous déclarons que le ocelot.json Le fichier servira de base aux configurations d’Ocelot. Nous déclarons également le AddOcelot() méthode afin qu’une instance d’Ocelot soit créée au démarrage de l’application. Enfin, nous déclarons le UseOcelot(); méthode pour démarrer le serveur Ocelot.

Ensuite, créons le fichier avec les configurations de la passerelle API et déclarons-y les règles à suivre par la passerelle API. Ainsi, à la racine du projet, créez un nouveau fichier appelé ocelot.json et ajoutez-y le code ci-dessous :

{
  "Routes": [
    {
      "DownstreamPathTemplate": "/api/content",
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "localhost",
          "Port": 5001
        }
      ],
      "UpstreamPathTemplate": "/cms/content",
      "UpstreamHttpMethod": [ "GET" ]
    }
  ],
  "GlobalConfiguration": {
    "BaseUrl": "http://localhost:5000"
  }
}

Dans ce fichier, nous définissons les règles des routes API pour rediriger les requêtes entrantes (en amont) vers les services internes (en aval), où :

  • "DownstreamPathTemplate": "/api/content": Définit le chemin interne de l’API vers lequel la requête sera redirigée. Dans ce cas, lorsque la requête est faite à la passerelle API, elle sera acheminée vers /api/content.
  • "DownstreamScheme": "http": Définit le protocole utilisé pour la communication en aval, qui, dans ce cas, est HTTP.
  • "DownstreamHostAndPorts": Définit l’hôte et le port du service interne. Ici, il est configuré pour localhost sur le port 5001, c’est-à-dire l’API créée précédemment, s’exécutant localement sur ce port.
  • "UpstreamPathTemplate": "/cms/content": Spécifie le chemin que le client utilisera pour accéder au service via la passerelle API. Lorsque quelqu’un accède à /cms/content, Ocelot redirigera la requête vers le chemin en aval configuré (/api/content).
  • "UpstreamHttpMethod": [ "GET" ]: Spécifie que seules les requêtes HTTP GET seront acheminées via cette règle.
  • GlobalConfiguration:"BaseUrl": "http://localhost:5000": Définit l’URL de base sur laquelle Ocelot est exécuté. Dans ce cas, la passerelle écoutera les requêtes à l’URL http://localhost:5000.

Test de la passerelle API

Voici les configurations de base pour le routage des API via Ocelot. Dans l’exemple de l’article, nous n’avons qu’une seule API, mais vous pouvez ajouter autant d’API et de routes que nécessaire.

Testons maintenant et vérifions si la passerelle API fonctionne correctement. Pour ce faire, vous devez configurer les deux projets pour qu’ils s’exécutent simultanément. Dans Visual Studio, procédez simplement comme suit :

Configurer les deux projets pour qu'ils s'exécutent

Ensuite, lancez l’application et demandez l’itinéraire http://localhost:5000/cms/content:

Résultats de la passerelle API

Notez que la passerelle API a créé un pont entre le client (votre demande) et l’API (/api/content). De cette façon, nous avons pu accéder aux ressources de l’API sans y accéder directement ; au lieu de cela, nous avons demandé la passerelle API.

Limiter le nombre de demandes

Jusqu’à présent, nous avons utilisé la fonction de routage de la passerelle API, qui transmet une requête à un point de terminaison préalablement configuré, mais nous n’avons encore ajouté aucune restriction à la passerelle. Pour ajouter des politiques de restriction d’accès à la passerelle API, nous pouvons utiliser le Rate Limiting d’Ocelot.

Dans le fichier ocelot.json, ajoutez l’extrait suivant juste en dessous de l’endroit où le "UpstreamHttpMethod" la configuration est créée :

,"RateLimitOptions": {
   "ClientWhitelist": [],
   "EnableRateLimiting": true,
   "Period": "2m",
   "PeriodTimespan": 2,
   "Limit": 2
 }

Puis, à l’intérieur du "GlobalConfiguration" bloquer, ajoutez ce qui suit :

,
    "RateLimitOptions": {
      "HttpStatusCode": 429,
      "ClientIdHeader": "ClientId",
      "QuotaExceededMessage": "Too many requests. Please try again later."
    }

Dans la configuration ci-dessus, nous utilisons les fonctionnalités de limite de débit d’Ocelot :

  • EnableRateLimiting: Active la limite de débit.
  • Period: Définit la période pour calculer la limite. Exemple : « 2m » pendant 2 minutes.
  • PeriodTimespan: Durée de la période (nombre de minutes, d’heures, etc.).
  • Limit: Nombre maximum de demandes autorisées dans la période (dans ce cas 2).
  • ClientWhitelist: Liste des identifiants clients exemptés de la limite de tarif (peut être laissée vide ou remplie avec des identifiants spécifiques).
  • HttpStatusCode: code de réponse HTTP envoyé au client lorsque la limite de débit est dépassée (généralement 429 - Too Many Requests).
  • ClientIdHeader: En-tête qui identifie le client pour appliquer la limite de débit.
  • QuotaExceededMessage: Message personnalisé qui sera renvoyé lorsque la limite sera atteinte.

Maintenant, exécutez à nouveau les applications et effectuez trois requêtes consécutives au point final. http://localhost:5000/cms/content. La troisième requête renverra un code HTTP 429 - Too Many Requestscomme le montrent les images ci-dessous :

Erreur de limite de débit 1

Erreur de limite de débit 2

Ajout de cache en mémoire

Ocelot dispose de mécanismes pour gérer la mise en cache en mémoire et distribuée via CacheManager. Pour télécharger le package CacheManager NuGet sur votre projet API Gateway, ouvrez un terminal à la racine de votre projet API Gateway et exécutez la commande suivante :

dotnet add package Ocelot.Cache.CacheManager

Ensuite, dans le fichier ocelot.json, ajoutez le code suivant, juste en dessous du UpstreamHttpMethodconfiguration:

,
"FileCacheOptions": {
"TtlSeconds": 300
}

Ici, nous configurons le délai d’expiration pour que les données soient stockées dans le cache en mémoire, qui dans ce cas est de 300 secondes, soit 5 minutes.

Enfin, dans le fichier Program.cs de la passerelle API, dans le AddOcelot() méthode, ajoutez ce qui suit :

.AddCacheManager(x =>
{
x.WithDictionaryHandle();
});

Maintenant, si vous exécutez à nouveau le projet et effectuez deux requêtes, vous remarquerez peut-être que la seconde sera plus rapide puisque les données ont été renvoyées depuis le cache :

Mise en cache des Ocelots

Conclusion

Les passerelles API sont une ressource essentielle pour les applications de moyenne et grande taille en raison de leur large gamme de fonctionnalités et principalement parce qu’elles permettent d’unifier les fonctions et les règles applicables aux API web.

La nécessité d’utiliser une passerelle API doit être prise en compte dans les scénarios dans lesquels plusieurs API partagent les mêmes règles, telles que l’authentification et l’autorisation, la transformation des données, la limitation du débit et la journalisation. De plus, avec une passerelle API, il est possible de réaliser des configurations avancées telles que l’équilibrage de charge, la surveillance, la découverte de services et autres.

Dans cet article, nous avons eu une introduction aux passerelles API utilisant Ocelot, qui est un outil open source pour ASP.NET Core au format de package NuGet. Nous avons également exploré certaines fonctions d’Ocelot en pratique, telles que le routage des points de terminaison, la limite de débit et la mise en cache en mémoire.

Alors, si cela vous intéresse, essayez de reproduire l’exemple dans le post et explorez les différentes options proposées par Ocelot.




Source link