WebAPI et ses utilisations (1) - Préambule sur .NET Core
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.
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 !).
A 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 1ère 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 !
.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 repository GitHub dont les plus connus sont : corefx, Roslyn, coreClr, EF Core… L’ensemble des projets est disponible ici.
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 4 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.
- CoreCLR (Common Language Runtime) : contient le runtime de .NET Core, c’est-à-dire :
- 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”.
- 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 autre, par les IDEs)
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 terme 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étails quelques points importants.
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 3 sections principales :
- La 1ère définit le type de projet et le framework de destination
- La 2nde 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 3è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.
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’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 est donc bien plus aisée et moderne.
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.
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. C’est en effet “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 reverses proxy. 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).
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 “Middleware”. 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…
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 3 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 chainage vers un autre middleware, elle sera donc réservé à 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 chainage 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 !).
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if(env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Shared/Error");
}
app.UseStaticFiles();
app.UseAuthorizationMiddleware();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
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 fait au tout début du pipeline).
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. A 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 !
A 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é ;)
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 3 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.
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 micro-services (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 petites tailles 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.