Développement de API

Les APIs constituent la limite HTTP d'un service Lino. Elles exposent des cas d'utilisation de la couche application sans mélanger les problÚmes de transport avec les rÚgles de domaine. Ce chapitre explique comment Lino génÚre des Minimal APIs, des contrats request/response, des typed clients, des métadonnées OpenAPI et l'enregistrement automatique des endpoints par les source generators.


Le endpoint gĂ©nĂ©rĂ© doit rester correct : reçoit les donnĂ©es HTTP, assemble une commande ou une requĂȘte, l'envoie Ă  la couche Application via ISender et convertit le rĂ©sultat en une rĂ©ponse tapĂ©e. La validation, les rĂšgles mĂ©tier, la persistance, les transactions et l'autorisation de domaine se poursuivent dans les commandes, les requĂȘtes, les gestionnaires, les validateurs, les rĂ©fĂ©rentiels et l'unitĂ© de travail.

API minimes sur Lino

Lino adopte le noyau ASP.NET API minimes comme standard pour générer des endpoints, car ils gardent le code direct, explicite et performant sans briser les limites de Clean Architecture.

Dans les projets Lino actuels, les endpoints gĂ©nĂ©rĂ©s sont des classes minimales API qui implĂ©mentent IEndpoint. Chaque endpoint expose une mĂ©thode statique MapEndpoint(IEndpointRouteBuilder app) et une mĂ©thode de gestionnaire qui utilise ISender pour distribuer la commande ou la requĂȘte gĂ©nĂ©rĂ©e Ă  la couche Application.

PiÚces principales générées

  • *Endpoint.cs: mĂ©thode de cartes HTTP, itinĂ©raire, mĂ©tadonnĂ©es, autorisation, exigences du tenant, limitation de dĂ©bit et version.
  • *Request.cs: reprĂ©sente une entrĂ©e provenant du corps, de la chaĂźne de requĂȘte, des paramĂštres de route ou des donnĂ©es de formulaire.
  • *Extensions.cs: convertit la requĂȘte en commande/requĂȘte et convertit le rĂ©sultat de l'application en rĂ©ponse HTTP.
  • *Response.cs: dĂ©finit le contrat fortement typĂ© renvoyĂ© par le endpoint et les typed clients.

Fonctionnalités intégrées

Lino utilise TypedResults et Results<...> afin que les réponses de réussite et d'erreur soient explicites dans le code et apparaissent correctement dans OpenAPI. Les défauts provenant de la couche Application sont convertis en ProblemDetails mettre result.MapToProblemDetails().

  • WithTags: regroupe les endpoints par module, entitĂ© ou fonctionnalitĂ© dans OpenAPI et Scalar.
  • WithName: dĂ©finit un identifiant d'opĂ©ration cohĂ©rent, le cas Ă©chĂ©ant.
  • WithSummary: dĂ©crit clairement l'objectif du endpoint.
  • Produces(statusCode, schema): spĂ©cifie les codes d'Ă©tat et les contrats de rĂ©ponse.
  • MapToApiVersion(1, 0): associe le endpoint Ă  la version API.
  • RequirePermission, RequireAuthorization, AllowAnonymous et RequireTenant: appliquer la sĂ©curitĂ© selon les options du projet.
  • Limitation du dĂ©bit au niveau du groupe ou du endpoint, Ă  l'aide de politiques authentifiĂ©es ou anonymes selon la configuration.

Demandes et réponses

Par défaut, Lino utilise enregistrements pour les demandes et les réponses. Ils sont concis, immuables par défaut et fonctionnent bien en tant que contrats explicites entre API, les typed clients et les consommateurs externes.

  • Les requĂȘtes reçues via le endpoint sont transformĂ©es en commandes ou requĂȘtes.
  • Les rĂ©sultats des commandes ou des requĂȘtes sont convertis en rĂ©ponses.
  • Les entitĂ©s de domaine ne doivent pas ĂȘtre directement exposĂ©es en tant que contrat HTTP.
public record CreatePersonRequest(string Name, int Age);
public record CreatePersonResponse(Guid Id, string Name, int Age);

Création de nouveaux API

CrĂ©ez des API par CLI lorsque le modĂšle de domaine et le cas d'utilisation sont dĂ©jĂ  suffisamment clairs pour ĂȘtre exposĂ©s par HTTP. Les API connectent les commandes et les requĂȘtes Ă  la couche de prĂ©sentation via des Minimal APIs, des contrats, des rĂ©ponses saisies et des mĂ©tadonnĂ©es de documentation.

lino api new
lino api new --name <ApiName> --service <ServiceName> --module <ModuleName> --entity <EntityName>
lino api list --service <ServiceName> --module <ModuleName> --entity <EntityName>

L'assistant interactif vous demandera :

  • Service: service dans lequel API sera créé.
  • Module: module de service, le cas Ă©chĂ©ant.
  • EntitĂ© ou Ă©numĂ©ration: Ă©lĂ©ment de domaine associĂ© au endpoint.
  • Nom de API: normalement alignĂ© avec le verbe de l'opĂ©ration, tel que CreatePerson.
  • Type d'opĂ©ration: GET, POST, PUT, PATCH, DELETE, options de chargement, de tĂ©lĂ©chargement ou de endpoint.
  • ItinĂ©raire:modĂšle d'itinĂ©raire, comme /people/{id:guid}.
  • PropriĂ©tĂ©s: champs de requĂȘte et de rĂ©ponse qui doivent effectivement traverser la frontiĂšre HTTP.

Ce que l'assistant peut configurer

  • OBTENIR: rĂ©sultat unique, liste, liste paginĂ©e et options/sĂ©lectionner des scĂ©narios.
  • POSTE: crĂ©ation et actions commerciales, y compris le type de commande et les propriĂ©tĂ©s sĂ©lectionnĂ©es.
  • METTRE et CORRECTIF: mise Ă  jour complĂšte ou partielle.
  • SUPPRIMER: suppression mappĂ©e pour supprimer les commandes.
  • TĂ©lĂ©charger: endpoints avec IFormFile et DisableAntiforgery() lorsque cela est nĂ©cessaire.
  • TĂ©lĂ©charger:retour des endpoints FileStreamHttpResult; Les projets authentifiĂ©s peuvent gĂ©nĂ©rer un endpoint de jeton pour un accĂšs sĂ©curisĂ© aux fichiers.
  • ÉnumĂ©rations: endpoints qui exposent des options valides par requĂȘte et rĂ©ponse gĂ©nĂ©rĂ©es.

Débit recommandé

  1. CrĂ©ez ou examinez l'entitĂ©, les commandes et les requĂȘtes qui reprĂ©sentent le cas d'utilisation.
  2. Courir lino api new et sélectionnez le service, le module et l'entité corrects.
  3. Choisissez le type d'opération par cas d'utilisation, pas seulement par le verbe HTTP souhaité.
  4. Définir un itinéraire stable, en utilisant des contraintes telles que {id:int} ou {id:guid} le cas échéant.
  5. Sélectionnez uniquement les propriétés qui doivent traverser la limite HTTP.
  6. DĂ©finissez l’autorisation, la permission, les exigences du tenant, la limitation du dĂ©bit et les codes d’état attendus.
  7. Exécutez build et inspectez Scalar/OpenAPI pour confirmer l'itinéraire, le résumé, la version, la sécurité et les réponses produites.

Exemple : Créer une personne

Lors de la création d'un API POST appel CreatePerson, associé à l'entité Person, CLI génÚre un endpoint, des contrats de demande/réponse, des extensions de mappage, des métadonnées OpenAPI et une intégration avec la commande correspondante.

<ProjectName>/
└── src/
    └── Services/
        └── <ServiceName>/
            └── Api/
                └── Endpoints/
                    └── People/
                        └── CreatePerson/
                            ├── CreatePersonEndpoint.cs
                            ├── CreatePersonExtensions.cs
                            ├── CreatePersonRequest.cs
                            └── CreatePersonResponse.cs

Contrats et typed clients

Lorsque le projet dispose d'une Web App Blazor, Lino génÚre également des artefacts pour la consommation typée des API: contrats partagés, interface client et implémentation de HTTP. Cela permet à Blazor de consommer les endpoints d'une maniÚre simple, cohérente et fortement typée.

<ProjectName>/
└── src/
    └── Services/
        └── <ServiceName>/
            ├── Api.Contracts/
            │   └── Features/
            │       └── People/
            │           ├── CreatePerson/
            │           │   ├── CreatePersonRequest.cs
            │           │   └── CreatePersonResponse.cs
            │           └── IPersonApiClient.cs
            └── Api.Client/
                └── Features/
                    └── PersonApiClient.cs

L'interface client est enregistrée pour l'injection de dépendances et l'implémentation utilise HttpClientProvider avec les assistants HTTP de Tolitech pour appeler le API généré d'une maniÚre fortement typée.

Liste de contrĂŽle avant de publier

  • Utiliser GET pour la lecture, POST pour la crĂ©ation/actions, PUT/PATCH pour le changement et DELETE pour le retrait lorsque cela a du sens.
  • N'exposez pas les entitĂ©s de domaine directement en tant que contrat externe.
  • Erreurs de validation de document, de conflit, d'introuvable et d'accĂšs refusĂ©.
  • Utiliser api list pour Ă©viter les routes en double ou les endpoints concurrents pour le mĂȘme cas d'utilisation.

Enregistrement des endpoints avec les source generators

Lino évite de mapper manuellement des fichiers volumineux à l'aide de source generators. Au lieu de lister les endpoints un par un dans Program.cs, l'enregistrement est produit lors de la compilation.

Implémentation des classes de endpoints générées IEndpoint de Tolitech.MinimalApis.Generators.Abstractions. Le groupe de endpoints appelle MapEndpointsGenerated(), produit par Tolitech.MinimalApis.Generators, et les nouveaux endpoints sont enregistrés par le code généré.

Pourquoi est-ce important

  • Moins de cĂąblage manuel: Les dĂ©veloppeurs n'ont pas besoin de penser Ă  mapper chaque endpoint manuellement.
  • CohĂ©rence au moment de la compilation: les endpoints suivent la mĂȘme structure et sont dĂ©couverts par le code gĂ©nĂ©rĂ©.
  • DĂ©marrage plus propre: Program.cs dĂ©lĂšgue l'installation pour la configuration des services, des middlewares et des mĂ©thodes d'extension.
  • CompatibilitĂ© AOT: rĂ©duit la dĂ©pendance Ă  la rĂ©flexion au moment de l'exĂ©cution.
  • OpenAPI cohĂ©rent: les balises, les rĂ©sumĂ©s, les codes d'Ă©tat et la version sont gĂ©nĂ©rĂ©s au niveau du point final.
  • Alignement architectural: HTTP est dans API, l'orchestration passe par MediatR, et les contrats peuvent ĂȘtre partagĂ©s avec les clients.

Flux au moment de l'exécution

  1. Program.cs construit l'application et appelle l'extension Ă  partir des endpoints du service ou du module.
  2. L'extension crée l'ensemble de versions API et le groupe de endpoints, applique l'autorisation/limitation de débit et appelle MapEndpointsGenerated().
  3. Le mappeur généré appelle la méthode MapEndpoint de chaque point final.
  4. Chaque endpoint mappe l'itinéraire, les métadonnées OpenAPI, l'autorisation, les exigences du tenant et le gestionnaire.
  5. Le gestionnaire reçoit l'entrĂ©e HTTP, la convertit en commande/requĂȘte, l'envoie Ă  MediatR et renvoie un rĂ©sultat typĂ©.

Définitions des erreurs

Les définitions d'erreurs normalisent les erreurs connues du domaine et de l'application. Ils aident les API, les gestionnaires, les journaux et les interfaces à gérer des problÚmes prévisibles sans compter sur des messages parasites, des chaßnes en double ou des décisions ad hoc à chaque endpoint.

lino error-definition new --name <ErrorDefinitionName> --service <ServiceName> --module <ModuleName> --entity <EntityName>
lino error-definition list --service <ServiceName> --module <ModuleName> --entity <EntityName>

Dans les API gĂ©nĂ©rĂ©s, les Ă©checs attendus renvoyĂ©s par les commandes et les requĂȘtes doivent ĂȘtre convertis en ProblemDetails avec code d'Ă©tat, code d'erreur et message sĂ©curisĂ©. Le serveur peut enregistrer des journaux techniques dĂ©taillĂ©s, mais la rĂ©ponse HTTP doit rester cohĂ©rente, sĂ©curisĂ©e et localisable.

StatutUtilisation typiqueExemple
400 requĂȘtes incorrectesErreur de saisie ou de validation invalide.Champ obligatoire manquant, format non valide, rĂšgle de demande non respectĂ©e.
401 Non autoriséUtilisateur non authentifié.Jeton manquant, expiré ou invalide.
403 InterditUtilisateur authentifié sans autorisation.Autorisation requise par RequirePermission pas accordé.
404 introuvableRessource inexistante ou hors du pĂ©rimĂštre autorisĂ©.ProductNotFound, entitĂ© d’un autre tenant ou identifiant inconnu.
409 ConflitConflit d’état ou rĂšgle d’unicitĂ©.Enregistrement en double, transition invalide, conflit de version.

Bonnes pratiques

  • CrĂ©ez des dĂ©finitions d'erreur pour les Ă©checs attendus et rĂ©utilisables.
  • Évitez de renvoyer des exceptions techniques ou des dĂ©tails d’infrastructure au client.
  • Maintenez des codes d'erreur stables afin que le frontend, les clients saisis et les tests puissent rĂ©agir en toute sĂ©curitĂ©.
  • Documentez dans OpenAPI les codes d'Ă©tat que le endpoint peut produire.
  • Utilisez les journaux cĂŽtĂ© serveur pour les dĂ©tails de diagnostic et les messages sĂ©curisĂ©s dans la rĂ©ponse publique.
Une erreur non gérée est survenue. Rafraîchir 🗙