Apprendre à créer un service WebAPI et à l'utiliser via plusieurs technologies

Partie 1 : préambule sur .NET Core

Matthieu Anceret

Pour réagir au contenu de ce tutoriel, un espace de dialogue vous est proposé sur le forum.

Commentez

- Titre : Apprendre à créer un service WebAPI et à l'utiliser via plusieurs technologies

- Auteur : Matthieu Anceret

- Parution : 15 juillet 2017

- Mise à jour : 5 février 2018

- Licence : Copyright ® 2017 Matthieu Anceret. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- Préambule

- I. Introduction

- II. .NET Core

- III. Le nouveau fichier .csproj

- IV. Bye-bye IIS !

- IV-A. Le standard OWIN

- IV-B. Kestrel

- V. Les middlewares

- VI. Entity Framework Core

- VII. Autres nouveautés et évolutions

- VIII. Conclusion

- IX. Note de la rédaction de developpez.com

Préambule

Je vais me lancer dans une série d'articles à propos de la création d'un service web WebAPI et de son utilisation via plusieurs technologies. L'idée va donc être de créer un ensemble de services web et de les consommer via différents moyens : site web, application mobile native/hybride… Chaque article sera donc dédié à une technologie, et contiendra une présentation et un cas pratique (décliné à partir de l'API).

Pour vous donner une idée de où je veux vous emmener, voici le sommaire prévisionnel de cet ensemble d'articles. Il pourra évidemment être amené à évoluer, notamment en fonction de mon temps et de mon envie de privilégier telle ou telle technologie.

Sommaire :

- WebAPI ;

- ASP.Net MVC et AngularJS ;

- Xamarin ;

- Apache Cordova - Framework Ionic ;

- Android ;

- iOS ;

- Docker : comment l'utiliser pour industrialiser notre API ?

Contrairement aux prochains, ce premier article ne contient pas de cas pratique et se contente de poser les bases pour bien aborder la suite.

I - Introduction

L'environnement .NET existe depuis déjà une quinzaine d'années. La version 1.0 est sortie en février 2002 et la plus récente, la 4.7, en mai 2017. Il est utilisé dans de nombreux projets informatiques, en particulier dans les entreprises. Pour donner quelques chiffres, sur les 100k sites les plus visités, le framework ASP.NET (MVC compris) représente 21% (données de juin 2017). Côté IIS (le serveur web de Microsoft), on arrive à 18% de parts de marché. Alors oui, c'est vrai, .NET n'est pas le numéro 1, mais il reste tout de même très bien représenté et conserve une dynamique d'évolution constante. Il faut aussi garder à l'esprit la distinction entre « Internet » et « Intranet » : le monde Microsoft est extrêmement présent dans les entreprises (bien aidé par Office et SharePoint) et les parts de marché y sont bien plus importantes (et celles-ci ne sont pas comptées dans les statistiques ci-dessus !).

À l'heure actuelle, .NET est suffisamment mature, performant et complet pour être la référence du développement sur les environnements Microsoft. Mais depuis juin 2016 (date de la 1re release de .NET Core), une révolution est en marche chez Microsoft : le passage de cette plateforme en open source (licence MIT et Apache 2) et cross-plateform ! Chose incroyable il y a quelques années, on va pouvoir faire fonctionner des applications .NET sur d'autres systèmes d'exploitation que Windows, et le code source du framework est même disponible sur GitHub !

II - .NET Core

Malgré ce que le nom pourrait laisser penser, .NET Core n'est pas une évolution du framework .NET : c'est une complète réécriture ! Pour que ça soit bien clair pour tout le monde, Microsoft a fait un reset du numéro de version : 1.0.0.

J'ai déjà abordé dans l'introduction les aspects open source. Il est important de noter que pour l'occasion, Microsoft a créé la .NET Foundation, qui est un organisme indépendant réunissant des acteurs liés à l'écosystème .NET (la société Xamarin en particulier). Cet organisme a pour but d'accélérer les développements et les projets ainsi que de faciliter les échanges entre les entreprises gravitant autour de .NET. La fondation gère un certain nombre de repositories GitHub dont les plus connus sont : corefx, Roslyn, coreClr, EF Core… L'ensemble des projets est disponible ici.

.NET Core VS .NET Framework 4.5

Pour moi, l'évolution la plus importante est la constitution modulaire du framework. Il est désormais possible d'intégrer uniquement les briques fonctionnelles nécessaires à son projet, et non plus la totalité du framework, ce qui permet d'alléger les applications. Ce point est tout de même à nuancer, car jusqu'à maintenant, le framework .NET était préinstallé avec Windows. Il était donc partagé avec l'ensemble des applications qui en avaient besoin. Par contre, gros avantage, il est maintenant possible d'utiliser plus facilement des versions différentes entre applications (les DLLs sont désormais embarquées directement dans l'application) et de les mettre à jour de manière autonome et distincte.

Voilà les quatre principales briques du framework .NET Core :

- CoreFX : contient les classes de base (anciennement BCL, Base Class Library) c'est-à-dire System, System.IO, System.Collections, System.XML… Microsoft a essayé autant que possible de limiter les dépendances entre ces classes pour permettre une meilleure modularisation lors de l'ajout de références.

- Les mécanismes de gestion mémoire (Garbage Collector),

- Les implémentations bas niveau (pour le multiplateforme notamment),

- Quelques classes : Exceptions, Thread, Object…,

- Roslyn, le compilateur .NET : il a pour rôle de convertir le code C# en langage MSIL (bytecode). Nouveauté, celui-ci est désormais capable de compiler les fichiers à la volée (en mémoire), c'est-à-dire sans avoir besoin d'enregistrer les fichiers sur le disque. Roslyn est disponible sous la forme d'une API qui est utilisée par Visual Studio, mais aussi accessible à des outils tiers. Il est disponible aussi bien pour .NET Core que pour .NET « standard »,

- RyuJIT, le compilateur JIT (« Just In Time ») : lors de l'exécution, il a pour rôle de convertir le code MSIL en langage machine. Il passe donc après Roslyn dans le cycle de compilation d'une application. Les performances ont été grandement optimisées et RyuJIT est plus efficace pour les applications 64 bits. Comme Roslyn, RyuJIT supporte aussi bien .NET Core que .NET « standard ».

- CoreCLR (Common Language Runtime) : contient le runtime de .NET Core, c'est-à-dire :

- CoreRT : contient le runtime pour la compilation AOT (Ahead Of Time) à destination des applications UWP (Universal Windows Platform),

- CLI (Command-Line Interface) : contient les outils permettant le développement d'applications .NET Core (utilisés, entre autres, par les IDE).

Architecture .NET Core

Seconde évolution majeure : le multiplateforme. Jusqu'à maintenant, il était inenvisageable d'exécuter une application .NET sur un autre système que Windows (on va plutôt dire compliqué, n'oublions tout de même pas Mono qui est une implémentation « non officielle », mais open source, de la plateforme .NET). Désormais, il est possible de cibler nativement les environnements Linux et Mac en plus de Windows et cela est extrêmement intéressant pour plusieurs raisons :

- pouvoir exécuter des applications .NET sans changer de système ;

- pouvoir étendre le scope des développeurs C#/.NET et inversement récupérer des développeurs d'autres plateformes ;

- l'exécution sur un environnement Linux permet d'économiser sur le prix des licences Microsoft et aussi de profiter des avantages de ce type d'OS (stabilité, performance et sécurité, notamment dans le cadre d'un usage serveur).

En termes de compatibilité, il est nécessaire d'utiliser Visual Studio 2015 Update 3 ou Visual Studio Code (version allégée de VS basée sur Atom qui est dispo sur Mac, Linux et Windows).

Je vais maintenant aborder plus en détail quelques points importants.

III - Le nouveau fichier .csproj

Microsoft a enfin décidé de revoir l'organisation des projets en refondant l'antique fichier .csproj ! Toujours au format XML, celui-ci est désormais plus simple, facilement lisible par un être humain et du coup modifiable aisément « à la main ». Il est découpé en trois sections principales :

- la première définit le type de projet et le framework de destination ;

- la deuxième permet l'inclusion des fichiers de code source. Il est possible d'utiliser le token « ** » pour inclure l'ensemble des fichiers (sous-dossiers compris). Cela permet d'éviter la déclaration un par un des fichiers… Du coup, plus besoin d'y faire des modifications lors de l'ajout/suppression d'un fichier et de plus, beaucoup moins de soucis avec les gestionnaires de code source (GIT/TFS). La popup d'« Auto Reload » n'est donc plus qu'un mauvais souvenir, et tout se fait désormais de manière automatique !

- la troisième liste les dépendances NuGet (plus de fichier packages.config) et les références (qui ne sont désormais plus des assemblies, mais des packages).

Je parlerai de la structure d'un projet .NET Core dans le cas pratique de mon prochain article.

IV - Bye-bye IIS !

Derrière ce sous-titre racoleur se trouve la promesse de Microsoft de ne plus imposer le serveur IIS pour faire tourner un site ASP.NET Core (bon, de toute façon, ils étaient un peu obligés s'ils voulaient cibler Linux et Mac…) ! En effet, jusqu'à maintenant, les applications ASP.NET avaient énormément d'adhérence avec IIS. Il est désormais possible d'utiliser d'autres serveurs web comme Apache ou NGinx.

Le découplage vis-à-vis de IIS implique des changements au niveau de la configuration et en particulier la disparition du fichier web.config. En réalité, celui-ci existe toujours, mais il est uniquement réservé à IIS et n'apporte rien au projet web (qui n'en a d'ailleurs même pas connaissance). La lecture des paramètres, « à l';ancienne », via ConfigurationManager.AppSettings[« myKey »] ne fonctionne donc plus. Ce fichier est remplacé par un autre : appsettings.json. Comme son nom l'indique, il est au format JSON, ce qui permet de profiter de toute la souplesse de ce langage : plus de limitation à des paires clé/valeur, tableaux, imbrication, sous-niveaux… Comble du confort, il est possible de créer des objets C#, correspondant à l'ensemble ou à une partie de notre fichier JSON, sur lesquelles .NET viendra binder les données au démarrage de l'application. La lecture et l'utilisation des paramètres sont donc bien plus aisées et modernes.

IV-A - Le standard OWIN

Open Web Interface for .NET est un standard (une norme) qui définit une couche d'abstraction entre le code applicatif et le serveur applicatif. Le gain est de permettre une portabilité du code applicatif sur n'importe quel serveur respectant cette norme (et donc de permettre de se passer de IIS). Attention, il faut bien garder à l'esprit que OWIN n'est qu'une spécification. L'implémentation telle que proposée par Microsoft, c'est Katana.

IV-B - Kestrel

Pour permettre le développement web cross-plateform avec .NET Core, Microsoft se devait donc de proposer une alternative à IIS fonctionnant sous d'autres systèmes que Windows : c'est Kestrel ! Kestrel est basé sur libuv, la même librairie que l'on trouve derrière Node.JS. Il est très performant, mais n'atteint pas l'efficacité et les fonctionnalités de IIS dans un contexte professionnel ou de production. En effet, c'est « juste » un serveur web, c'est-à-dire qu'il ne fait que lire et écrire des messages HTTP et ne propose pas de fonctionnalités avancées comme les logs, l'authentification ou encore la gestion du cache. Il peut par contre être aisément secondé (et c'est bien là tout l'intérêt) par un autre serveur web, comme IIS ou NGinx, en utilisant ces derniers comme des reverse proxies. Cela permet de lui ajouter les fonctionnalités manquantes et/ou supplémentaires désirées.

L'objectif est donc bien de découpler l'application du serveur. Cela permet de s'adapter plus finement au contexte (application lourde, cloud, embarquée…) et au système (pas de IIS sur Linux par exemple).

V - Les middlewares

Dans une application ASP.Net, les requêtes HTTP entrantes passent dans un pipeline (= tuyau) de traitement avant de revenir vers l'appelant en tant que requêtes HTTP sortantes. Ce pipeline est composé d'une succession de composants logiciels qui vont appliquer un traitement à la requête. Ces composants logiciels sont appelés « middlewares ». Ils peuvent servir à plusieurs choses : fournir des fichiers statiques, gérer l'authentification et les autorisations, collecter les logs, gérer les exceptions, gérer le cache et la compression, ainsi que les sessions…

Pipeline HTTP - Microsoft (docs.asp.net)

Il existe des middlewares prédéfinis, mais on peut bien évidemment créer ses propres middlewares. Pour cela, il suffit d'implémenter l'interface IApplicationBuilder, qui met à notre disposition trois méthodes :

- Use : prend en paramètre le contexte HTTP et un callback vers le middleware suivant.

- Run : prend en paramètre uniquement le contexte HTTP. Cette méthode ne permet pas de faire un chaînage vers un autre middleware, elle sera donc réservée à la dernière étape du pipeline.

- Map : permet de programmer l'exécution d'un ou plusieurs middlewares sur une route spécifique.

Pour définir le chaînage de notre pipeline, il faut écrire le code correspondant dans la méthode Configure(…) de la classe Startup.cs (au passage, j'en profite pour signaler que le fichier Global.asax n'existe plus !).

1. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)

2. {

3. loggerFactory.AddConsole(Configuration.GetSection("Logging"));

4. loggerFactory.AddDebug();

6. if(env.IsDevelopment())

7. {

8. app.UseBrowserLink();

9. app.UseDeveloperExceptionPage();

10. }

11. else

12. {

13. app.UseExceptionHandler("/Shared/Error");

14. }

16. app.UseStaticFiles();

17. app.UseAuthorizationMiddleware();

19. app.UseMvc(routes =>

20. {

21. routes.MapRoute(

22. name: "default",

23. template: "{controller=Home}/{action=Index}/{id?}");

24. });

25. }

Attention, l'ordre de définition des middlewares est très important, aussi bien au niveau fonctionnel (en général, la gestion de l'authentification doit-être faite au plus tôt) qu'au niveau des performances (la fourniture des fichiers statiques peut être faite au tout début du pipeline).

VI - Entity Framework Core

Entity Framework est un ORM (Object-Relational Mapping), c'est-à-dire qu'il permet la création d'une abstraction entre les données (une base SQL Server par exemple) et le code C# (DAL, Data Access Layer, « couche d'accès aux données » en français). Jusqu'à maintenant, EF était souvent vu comme une « boite noire », voire une usine à gaz… Il était donc temps pour Microsoft de revoir sa copie et de nous proposer un outil moderne et performant !

EF Core est donc une refonte complète de la version 6 d'Entity Framework (au même titre que .NET Core est une refonte de .NET finalement :)). Qui dit refonte dit réécriture, et malheureusement, la couverture fonctionnelle n'est pas encore au niveau de la précédente version. À mes yeux, les principaux manques sont :

- pas d'EDMX, et donc pas de modèle visuel de la BDD ;

- pas de relation N-N (many-to-many) sans table de jointure (un très bon article sur la façon de pallier ce manque est disponible ici) ;

- pas de lazy loading ;

- pas de mapping des procédures stockées…

- pas de reverse engineering à partir d'une base existante (pour créer un modèle) et donc pas de mise à jour de modèle à partir d'une base.

Vous trouverez la liste complète des manques et des différences sur ce lien. Heureusement, Microsoft est bien conscient des « trous dans la raquette » et la roadmap a été construite en conséquence pour venir combler les manques (https://github.com/aspnet/EntityFramework/wiki/Roadmap).

Sur le même principe que .NET Core, EF Core a été conçu pour être léger et modulaire. En dehors du « core », l'ensemble des fonctionnalités annexes est sous la forme de module optionnel (fonctionne sous la forme d'injection de dépendance). Effet positif de ce chantier, les performances (autrefois assez médiocre en fonction des cas…) sont désormais bien meilleures. L'équipe d'EF parle d'un gain moyen de 80 % ! Il y a aussi eu un gros travail effectué sur la qualité du code SQL généré, qui est désormais lisible beaucoup plus facilement par un être humain !

À noter, pour finir sur ce sujet, l'existence d'un provider « in-memory » permettant de travailler directement en mémoire et donc de s'affranchir d'une connexion à une base de données « standard » (gain de performance et simplification des tests). Dans la roadmap du produit, Microsoft a aussi prévu de fournir un provider NoSQL (mais ce n'est pas prioritaire pour le moment).

Enfin, le mécanisme de log a été revu pour s'aligner avec celui d'ASP.NET. Il suffit d'implémenter un logger (interface ILogger), de l'injecter dans la configuration du DbContext et le tour est joué.

VII - Autres nouveautés et évolutions

Pour finir, voici une liste « en vrac » des autres nouveautés et évolutions intéressantes :

- Les contrôleurs MVC et WebAPI ont été fusionnés et on peut désormais exposer des vues (HTML) et des services web (endpoint) au sein d'un même contrôleur (fusion des assemblies System.Web.Mvc et System.Web.Http).

- Performances :

- Amélioration significative des performances web grâce à la diminution de la taille des requêtes HTTP,

- La taille des applications a été considérablement réduite (~37%),

- Empreinte mémoire réduite (facteur de 10),

- Plus d'informations ici et ici.

- Bundling et minification : jusqu'à maintenant, la configuration était réalisée dans le fichier BundleConfig.cs. Désormais, Microsoft a choisi de faire confiance à des outils tiers libres pour gérer ce processus, j'ai nommé Gulp ! Un fichier gulpfile.js permet de configurer les traitements à réaliser (gulp-concat, gulp-cssmin, gulp-uglify…). Il est ensuite possible de lier l'exécution de ce script à un évènement dans Visual Studio, comme post-build.

- Injection de dépendances : ces opérations se réalisent dans la méthode ConfigureServices(…) de la classe Startup.cs. Il y a trois modes de durée de vie possibles : Transient (créé à chaque fois que nécessaire, c'est-à-dire à chaque résolution), Scoped (créé une seule fois par requête) et Singleton (créé une seule fois pour l'ensemble des requêtes).

- Tag Helpers : jusqu'à maintenant, dans les vues Razor, on mélangeait du code HTML et du code C# :

@Html.LabelFor(m => m.MyProperty, new { @class = "col-md-6 control-label" })

Pour simplifier la compréhension et l'écriture des vues, ASP.NETCore apporte les Tags Helpers. Ce sont des extensions à la syntaxe HTML qui sont interprétées par le moteur Razor (côté serveur donc) et qui permettent d'utiliser uniquement une syntaxe HTML côté vue, tout en profitant du binding et de la validation :

<input type="text" for="MyProperty" class="form-control" />

Le code gagne donc en clarté !

- .NET Core n'intègre plus les modèles d'applications WPF et WinForms, car ceux-ci reposent sur DirectX qui est une bibliothèque graphique trop dépendante de Windows.

VIII - Conclusion

Pour conclure, il faut clairement saluer le travail que Microsoft a réalisé avec .NET Core. Ils ont réussi à proposer un produit moderne, modulaire, léger et performant à partir des cendres du framework .NET et à fédérer une grande partie de l'écosystème des développeurs (et pas uniquement des développeurs C#). Ce framework est parfaitement adapté aux applications ayant des besoins cross-plateforme, aux architectures sous forme de microservices (Docker !), aux usages cloud (Azure !) et enfin, aux contraintes de performances et de scalabilité.

Pour ma part, .NET Core me parait encore un peu jeune pour être utilisé pleinement dans un environnement professionnel (entreprise) sur un sujet critique, mais je pense tout de même qu'il est grandement temps d'y consacrer du temps et du budget pour des sujets moins critiques ou des projets de petite taille afin d'appréhender la technologie et ses enjeux. En effet, une fois la machine lancée et les premières releases éprouvées, il est fort probable que .NET Core soit un framework incontournable pour les développements web et desktop.

IX - Note de la rédaction de developpez.com

Nous tenons à remercier Matthieu Anceret qui nous a aimablement autorisés à publier son tutoriel :WebAPI et ses utilisations (1) - Préambule sur .NET Core. Nos remerciements également à ced pour la relecture orthographique.