Minimal API avec .NET 6

Les Minimal API, disponibles avec .NET 6, nous permettent de créer des API avec beaucoup moins de « cérémonie » qu’avec les controlleurs traditionnels. Dans cet article, on fait un retour sur la vidéo et on explore la différence par rapport aux controlleurs. Bonne lecture!

Tout d’abord en rappel, la vidéo qui a été publiée récemment sur les Minimal API.

Bien organiser ses APIs

Avant même de parler de comment écrire ces APIs, il faut parler de comment organiser notre code. Avec les nouveaux top-level statements, on peut maintenant ajouter des endpoints directement dans notre fichier Program.cs. On pourrait même tous les mettre dans ce fichier, mais c’est justement ce qu’on veut éviter! Donc pour garder notre programme propre, on ajoute une méthode d’extension qui sera utilisée dans la classe Program. Celle-ci va s’occuper de gérer l’ajout de nos endpoints, comme ceci:

public static class EndpointRouteBuilderExtensions
{
    public static void AddApplicationEndpoints(this IEndpointRouteBuilder endpointRouteBuilder)
    {
        endpointRouteBuilder.AddWeatherForecastEndpoints();
        endpointRouteBuilder.AddPersonEndpoints();
        endpointRouteBuilder.AddSecurityEndpoints();
    }
}

Vous remarquerez aussi que cette méthode contient elle-même des appels à d’autres méthodes d’extensions. Celles-ci, situées dans d’autres projets, encapsulent les endpoints en fonction du domaine d’affaire. De cette manière, l’organisation de nos endpoints est claire et bien structurée.

Comment écrire un endpoint

Voici un exemple que nous allons regarder en détails:

endpointRouteBuilder
    .MapGet(
        Routes.Person,
        (IPersonService personService) => 
            personService.GetPersons().Select(f => f.ToPerson()))
    .AllowAnonymous()
    .WithTags("Person");

Le point d’entrée pour créer notre endpoint est avec la méthode d’extension MapGet. Dans ce cas-ci pour créer une requête GET, et de la même manière les MapPost, MapPut, MapDelete sont disponibles pour faire des requêtes POST, PUT, DELETE respectivement. Le premier paramètre est la route de notre endpoint. J’ai pris l’habitude de les garder dans des constantes, qui peuvent être dans un projet partagé lorsqu’on fait une application Blazor par exemple. Le deuxième paramètre est la fonction qui sera appelée. Au niveau de ses paramètres, on peut utiliser des objets injectés, des paramètres dans la requête ou des objets provenant du body de notre requête, le tout sera inféré à l’utilisation.

De manière fluente, on peut ensuite enchaîner différentes méthodes pour bonifier notre endpoint. Dans cet exemple, on a utilisé AllowAnonymous pour un accès sans authentification, mais dans un contexte où on doit être authentifié, on utiliserait la méthode RequireAuthorization. Ensuite nous avons WithTags, qui permet de regrouper des endpoints sous une même catégorie. On pourrait aussi spécifier entre autre le type de résultat produits par le endpoint et utiliser la classe Results pour facilement produire ces résultats. Par exemple, on voit ici un retour de type 201 Created, sur un endpoint qui peut aussi produire un statut 401 s’il y a des problèmes de validation:

endpointRouteBuilder
    .MapPost(
        Routes.Person,
        (IPersonService personService, Person person) =>
        {
            personService.Add(person.ToPersonEntity());
            return Results.Created($"{Routes.Person}/{person.Id}", person);
        })
    .ProducesValidationProblem()
    .Produces<Person>(StatusCodes.Status201Created);

Différences avec les controlleurs

Comme mentionné dans l’épisode vidéo, il y a certaines différences entre les Minimal API et les controlleurs qu’il faut considérer pour faire le bon choix de technologie à utiliser pour nos APIs. Voici les principales différentes à considérer selon moi.

Les filtres

Tout d’abord, il n’est pas possible de faire de filtres sur les Minimal API. Les filtres sont des classes qui peuvent être exécutés à différentes étapes dans la chaîne d’exécution d’une requête. Ils font parti de ce qui est offert par MVC. Donc par exemple, il n’est pas possible d’ajouter un filtre d’authorisation qui viendrait intercepter la requête si on est pas authentifié. C’est donc le genre de situation où on devra se servir des mécanismes en place comme la méthode d’extension RequireAuthorization sur chacuns de nos endpoints.

Le « model binding« 

Le model binding permet par exemple avec une implémentation de IModelBinder de faire qu’une action de controlleur reçoit un ID et va agir pour retourner l’objet de ce ID dans la méthode de controlleur (au lieu d’envoyer le ID et qu’on fasse manuellement la requête dans la méthode). Alors que cela fonctionne avec les controlleurs, cette fonctionnalité n’est pas supportée par les Minimal API. On doit donc faire ce genre d’opération manuellement dans nos endpoints ou encore dans les services que ceux-ci utilisent.

La validation

Les Minimal API n’offrent pas le support pour validation qu’on retrouve avec MVC, notamment avec l’interface IModelValidator. On doit donc se tourner vers une autre alternative, par exemple l’excellente librairie FluentValidation. Avec cette librairie, on peut se créer des validateurs comme ceci:

public class PersonValidator : AbstractValidator<Person>
{
    public PersonValidator()
    {
        RuleFor(p => p.FirstName).NotEmpty();
        RuleFor(p => p.LastName).NotEmpty();
    }
}

Par la suite, moyennant l’initialisation nécessaire, il nous suffit d’injecter ce validateur dans le endpoint. En prime, si vos APIs sont utilisés pour une application Blazor et que vos validateurs sont dans le projet partagés, vous pourrez les utiliser au niveau des formulaires grâce à la librairie Blazored FluentValidation.

Conclusion

Comme vous avez pu voir, les Minimal API offrent une belle alternative aux controlleurs, qui est à mon avis plus simple à utiliser et mettre en place. Si les différences ne sont pas un élément bloquant pour vous, cela peut définitivement être à considérer pour vos prochains projets. Il y a quelques différences de plus que ce qui est listé plus haut, je vous suggère de lire l’article suivant pour bien évaluer: Différences entre les Minimal API et les controlleurs
La documentation sur les Minimal API disponible via ce lien: Minimal API
Pour voir la solution utilisée pour la vidéo et les exemples de code: AspNetMinimalApi sur GitHub

Si vous avez des questions ou commentaires sur les Minimal API, n’hésitez pas à me laisser savoir dans les commentaires!

Bruno

Auteur : Bruno

De jour, je suis développeur, chef d'équipe et le co-animateur du Bracket Show. De soir, je suis un mari et le père de deux magnifiques enfants. Le temps qu'il me reste après tout cela, je le passe à tenter de bouger, à jouer et écrire des critiques de jeux vidéos, ainsi qu'à préparer du matériel pour les enregistrements du Bracket Show et pour ce blog. Au travers tout cela, je suis aussi passionné de musique et de microbrasseries.