<style> .reveal { font-size: 27px; } mark { background-color: rgba(50, 168, 82, .5) !important; } </style> # I-CH 151 Intégrer des bases de données dans des applications Web .NET 6.0 Web API © Selmir Hajruli & EPSIC 2021 --- ![](https://i.imgur.com/obgrLjs.png) --- # API > Une interface de programmation d'application (API) est une interface ou un protocole de communication entre différentes parties d'un programme informatique destiné à simplifier la mise en œuvre et la maintenance des logiciels. Une API peut être destinée à un système basé sur le web, un système d'exploitation, un système de base de données, du matériel informatique ou une bibliothèque de logiciels. --- # WebAPI > Lorsqu'elle est utilisée dans le contexte du développement web, une API est généralement définie comme un ensemble de spécifications, telles que les messages de requête HTTP (Hypertext Transfer Protocol), ainsi qu'une définition de la structure des messages de réponse, généralement dans un format XML (Extensible Markup Language) ou JSON (JavaScript Object Notation). --- # HTTP Verbs * **GET**: demande une représentation de la ressource spécifiée. Les requêtes GET doivent uniquement être utilisées afin de récupérer des données.<!-- .element: class="fragment highlight-green" data-fragment-index="9" --> * **HEAD**: demande une réponse identique à une requête GET pour laquelle on aura omis le corps de la réponse (on a uniquement l'en-tête). * **POST**: envoie une entité vers la ressource indiquée. Cela entraîne généralement un changement d'état.<!-- .element: class="fragment highlight-green" data-fragment-index="9" --> * **PUT**: remplace toutes les représentations actuelles de la ressource visée par le contenu de la requête.<!-- .element: class="fragment highlight-green" data-fragment-index="9" --> * **DELETE**: supprime la ressource indiquée.<!-- .element: class="fragment highlight-green" data-fragment-index="9" --> * **OPTIONS**: décrit les options de communications avec la ressource visée. * **TRACE**: réalise un message de test aller/retour en suivant le chemin de la ressource visée. * **PATCH**: applique des modifications partielles à une ressource. --- # REST API > REST signifie ==Re==presentational ==S==tate ==T==ransfer (ou transfert d’état de représentation, en français), et constitue un ensemble de normes, ou de lignes directrices architecturales qui structurent la façon de communiquer les données entre votre application et le reste du monde, ou entre différents composants de votre application. * #1 : Client-serveur separation * #2 : Stateless * #3 : Cacheable * #4 : Uniforme Interface (interface uniforme) * #5 : Layered system (système de couches) * #6 : Code on demand (code à la demande) Note: **Client-serveur separation** L’une des normes de REST est la séparation du client et du serveur. Nous avons un peu abordé la question des clients et des serveurs dans le chapitre précédent, il est temps d’approfondir un peu le sujet ! Un client est celui qui va utiliser l’API. Cela peut être une application, un navigateur ou un logiciel. Par exemple : en tant que développeur, vous utiliserez peut-être l’API de Twitter. Comme je l’ai dit précédemment, un client peut aussi être un logiciel ou un navigateur, qu’il s’agisse de Chrome, Safari ou Firefox. Quand un navigateur se rend sur twitter.com, il formule une requête à l’API de Twitter et utilise les données de l’API afin que vous puissiez accéder aux derniers tweets. Un serveur est un ordinateur distant capable de récupérer des données depuis la base de données, de les manipuler si besoin et de les renvoyer à l’API, comme ce gros ordinateur au milieu : Exemple d'une relation client-serveur Une relation client-serveur typique De façon générale, il existe une séparation entre le client et le serveur. Cette séparation permet au client de s’occuper uniquement de la récupération et de l’affichage de l’information et permet au serveur de se concentrer sur le stockage et la manipulation des données. Chacun son rôle ! Les API REST offrent un moyen de communication standardisé entre le client et les données. En gros, peu importe comment le serveur est construit ou comment le client est codé, du moment qu’ils structurent tous les deux leur communication selon les lignes directrices architecturales REST, en utilisant le protocole HTTP, ils pourront communiquer entre eux ! 👌 C’est particulièrement utile lorsque de grandes équipes de développeurs travaillent sur une même application. Vous pouvez avoir une équipe qui travaille indépendamment sur le backend tandis que l’autre travaille sur le frontend. Comme l’API REST communique entre les deux, cela permet aux développeurs de scaler plus facilement les applications et aux équipes de travailler de manière plus efficace. 🤝 **Stateless** L’un des autres aspects uniques des API REST est qu’elles sont stateless – sans état, en français – ce qui signifie que le serveur ne sauvegarde aucune des requêtes ou réponses précédentes. > Mais le rôle du serveur est de stocker et manipuler les données. Comment est-ce que cela pourrait fonctionner si on ne garde pas une trace des requêtes, alors ? 🤔 Pour revenir à notre métaphore de l’API en tant que serveuse, imaginons que vous demandiez des frites à votre serveuse. 🍟 Elle se rend à la cuisine, récupère vos frites, et revient avec votre commande. Parfait ! Houla... mais attendez ! Vous venez de vous souvenir que vous voulez également du ketchup avec vos frites. Vous demandez donc à votre serveuse : « Excusez-moi, je pourrais avoir du ketchup avec ? »  « Avec quoi ? » Une serveuse stateless n’aurait aucune idée de ce dont vous parlez… car elle ne se souviendrait pas que vous venez de commander des frites  ! Elle se charge seulement de transférer les commandes de la cuisine au client. > OK, mais alors concrètement, qu’est-ce que cela signifie pour les API REST ? 🤔 Étant donné que chaque message est isolé et indépendant du reste, il vous faudra vous assurer d’envoyer avec la requête que vous formulez toutes les données nécessaires pour être sûr d’avoir la réponse la plus précise possible. Cela nous donnerait quelque chose comme : « Est-ce que je pourrais avoir du ketchup sur les frites que j’ai commandées à ma table ? » Avec toutes ces informations, votre serveuse pourra identifier à quelles frites il faut ajouter du ketchup ! Le fait d’être stateless rend chaque requête et chaque réponse très déterminée et compréhensible. Donc, si vous êtes développeur et que vous voyez la requête API de quelqu’un d’autre dans un code déjà existant, vous serez capable de comprendre l’objet de la requête sans contexte ! 👌 **Cacheable (ou sauvegardable** La réponse doit contenir l’information sur la capacité ou non du client de mettre les données en cache, ou de les sauvegarder. Si les données peuvent être mises en cache, la réponse doit être accompagnée d’un numéro de version. Ainsi, si votre utilisateur formule deux fois la même requête (c’est-à-dire s’il veut revoir une page) et que les informations n’ont pas changé, alors votre serveur n’a pas besoin de rechercher les informations une deuxième fois. À la place, le client peut simplement mettre en cache les données la première fois, puis charger à nouveau les mêmes données la seconde fois. 💪 Une mise en cache efficace peut réduire le nombre de fois où un client et un serveur doivent interagir, ce qui peut aider à accélérer le temps de chargement pour l’utilisateur ! 👏 Vous avez peut-être entendu le terme cache en référence à, par exemple, « Rafraîchissez le cache de votre navigateur ». Un cache est un moyen de sauvegarder des données pour pouvoir répondre plus facilement aux prochaines requêtes qui seront identiques. Quand vous allez sur de nombreux sites web depuis votre navigateur, celui-ci peut sauvegarder ces requêtes pour pouvoir compléter lui-même le site que vous voulez atteindre ou charger la page plus rapidement la prochaine fois que vous vous y rendez. Pratique !. **Uniforme Interface (interface uniforme)** Lors de la création d’une API REST, les développeurs acceptent d’utiliser les mêmes normes. Ainsi, chaque API a une interface uniforme. L’interface constitue un contrat entre le client et le service, que partagent toutes les API REST. C’est utile, car lorsque les développeurs utilisent des API, cela leur permet d'être sûrs qu’ils se comprennent entre eux. Une API REST d’une application peut communiquer de la même façon avec une autre application entièrement différente. **Layered system (système de couches)** Chaque composant qui utilise REST n’a pas accès aux composants au-delà du composant précis avec lequel il interagit. > Que… quoi ? C’est-à-dire ? 🤔 Cela signifie qu’un client qui se connecte à un composant intermédiaire n’a aucune idée de ce avec quoi ce composant interagit ensuite. Par exemple, si vous faites une requête à l’API Facebook pour récupérer les derniers posts : vous n’avez aucune idée des composants avec lesquels l’API Facebook communique.. Cela encourage les développeurs à créer des composants indépendants, facilitant le remplacement ou la mise à jour de chacun d’entre eux. **Code on demand (code à la demande)** Le code à la demande signifie que le serveur peut étendre sa fonctionnalité en envoyant le code au client pour téléchargement. C’est facultatif, car tous les clients ne seront pas capables de télécharger et d’exécuter le même code – donc ce n’est pas utilisé habituellement, mais au moins, vous savez que ca existe ! --- # Créer une Web API ![](https://i.imgur.com/5YrQkgM.gif) --- ## Commandes - `dotnet -h` pour visualiser toutes les commandes disponibles - `dotnet new` pour voir tous les templates disponibles - `dotnet new webapi -o Epsic.Info3Dev.Rpg` pour créer une nouvelle Web API ASP.NET Core - `cd Epsic.Info3Dev.Rpg` pour aller dans le dossier du projet - `dotnet build` pour builder notre nouvelle API - `code .` pour ouvrir le projet dans VS Code - Cliquer sur `Yes` ![](https://i.imgur.com/BvKjmC9.png) --- ## Structure On doit maintenant, dans VS Code avoir la structure suivante : ![](https://i.imgur.com/x6TufG1.png) --- ### `Startup.cs` => `ConfigureServices` ```csharp public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "SwaggerTest", Version = "v1" }); }); } ``` :::success Ordre pas important ! ::: --- ### `Startup.cs` => `Configure` ```csharp // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "SwaggerTest v1")); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } ``` :::warning Ordre **TRÈS** important ! ✔ ::: Note: On trouve ici les `ConfigureServices` et la méthode `Configure`. `ConfigureServices` configurent les services de l'application, c'est-à-dire un composant réutilisable qui fournit les fonctionnalités de l'application. Nous enregistrerons les services à l'avenir dans cette méthode, afin qu'ils puissent être consommés dans notre service web via l'**injection de dépendances** par exemple. La méthode ``Configure`` crée le pipeline de traitement des requêtes de l'application, ce qui signifie que la méthode est utilisée pour spécifier comment l'application répond aux requêtes HTTP. Comme vous pouvez le voir, nous utilisons la `HttpRedirection`, le `Routing`, etc. Avec toutes ces méthodes d'extension ``Use...``, nous ajoutons des composants **middleware** au pipeline de traitement des requêtes. Par exemple, UseHttpRedirection ajoute un middleware pour rediriger les requêtes HTTP vers le HTTPS. --- ### `Program.cs` ```csharp public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); } ``` Note: `Program.cs` est le point d'entrée de toute application .NET Core, quand on lance/debug l'application la première ligne de code appelée est `public static void Main(string[] args)`. Dans la classe ``Program`` la méthode ``CreateHostBuilder()`` va configurer la suite des "opérations" en spécifiant la classe ``Startup`` en appelant la méthode `UseStartup<Startup>()`. --- ### `Epsic.Info3Dev.Rpg.csproj` ```xml <Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net5.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" /> </ItemGroup> </Project> ``` Dans le fichier ``.csproj``, nous voyons le SDK, le framework cible, dans notre cas **.NET 5.0**. Par la suite, nous trouverons dans ce fichier des packages supplémentaires comme Entity Framework Core. --- ### `appsettings.json` ```json { "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*" } ``` Contient, comme son nom l'indique 👻, les configuration relatives à l'application. --- ### Dossier `Controllers` - Très intéressant et qu'on utilisera tout au long du cours. Le premier contrôleur qu'on voit dedans est le contrôleur de démo ``WeatherForecast``. - Nous reviendrons plus tard sur les détails des contrôleurs. Pour l'instant, il est seulement important de savoir que nous pouvons déjà appeler la méthode ``Get()``. ```csharp [HttpGet] public IEnumerable<WeatherForecast> Get() { var rng = new Random(); return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = rng.Next(-20, 55), Summary = Summaries[rng.Next(Summaries.Length)] }) .ToArray(); } ``` --- ## Premier run ![](https://i.imgur.com/1GtfUnj.gif) --- ## Tester WebAPI... pas d'interface utilisateur, comment tester que ça tourne correctement ? ![](https://i.imgur.com/20xyrEM.jpg =360x) --- ### Tester : VS Code - Installer [REST Client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) - `CTRL`+`N` > https://localhost:5001/WeatherForecast - `CTRL`+`SHIFT`+`P` > REST Client: Send Request (`CTRL`+`ALT`+`R`) ![](https://i.imgur.com/zGpY4vb.png) --- ### Tester : VS Code => Résultat ```json HTTP/1.1 200 OK Connection: close Date: Mon, 05 Oct 2020 21:23:18 GMT Content-Type: application/json; charset=utf-8 Server: Kestrel Transfer-Encoding: chunked [ { "date": "2020-10-06T23:23:18.8132705+02:00", "temperatureC": -14, "temperatureF": 7, "summary": "Hot" }, { "date": "2020-10-07T23:23:18.8134163+02:00", "temperatureC": 54, "temperatureF": 129, "summary": "Scorching" }, { "date": "2020-10-08T23:23:18.8134198+02:00", "temperatureC": 35, "temperatureF": 94, "summary": "Warm" }, { "date": "2020-10-09T23:23:18.8134203+02:00", "temperatureC": 35, "temperatureF": 94, "summary": "Balmy" }, { "date": "2020-10-10T23:23:18.8134206+02:00", "temperatureC": 24, "temperatureF": 75, "summary": "Sweltering" } ] ``` --- ### Tester : Postman - Télécharger [Postman](https://www.postman.com/downloads/) et l'installer. - Créer une nouvelle requête ![](https://i.imgur.com/EjUnamI.png) - Cliquer sur ==Send== --- ### Tester : Postman => résultat ![](https://i.imgur.com/aLCYrXe.png) --- ### Tester : Swagger ![](https://i.imgur.com/CSdWFYf.png) --- ### Tester : Swagger => résultat https://localhost:5001/swagger/index.html ![](https://i.imgur.com/RpZ4pIZ.gif =700x) --- ## MVC MVC : Modèle-Vue-Contrôleur ou MVC court est un modèle de conception de logiciel qui divise la logique de programme correspondante en trois éléments interconnectés. Model : Représente la structure logique sous-jacente des données dans une application logicielle, ainsi que la classe supérieure qui y est associée. Ce modèle d'objet ne contient aucune information sur l'interface utilisateur. View : Une vue, autrement dit un ensemble de classes représentant les éléments de l'interface utilisateur (tous ceux que l'utilisateur voit à l'écran et avec lesquels il peut interagir : boutons, boîtes de dialogue, etc.). Controller : Représente les classes qui se connectent au modèle et à la vue, et servant à la communication entre les classes dans le modèle et la vue. --- ![](https://i.imgur.com/61Wzxng.png) --- ## Nouveau modèle - Pour implémenter une nouvelle fonctionnalité à notre API, nous aurons besoin de nouveaux modèles. - Pour commencer, créer un dossier `Models` dans le projet dans VS Code ![](https://i.imgur.com/VlKWkaG.png =300x) - Dans le dossier `Models` créer la classe `Character` ```csharp namespace Epsic.Info3Dev.Rpg.Models { public class Character { public int Id { get; set; } public string Name { get; set; } = "Frodo"; public int HitPoints { get; set; } = 100; public int Strength { get; set; } = 10; public int Defense { get; set; } = 10; public int Intelligence { get; set; } = 10; } } ``` --- ## Nouvel enum - Dans notre classe `Character` il nous manque une propriété qui définit le type de personnage. - Pour commencer, créer un dossier `Enums` dans le projet dans VS Code ![](https://i.imgur.com/ZmWqQfY.png =200x) - Dans le dossier `Models` créer l'enum `RpgClass` ```csharp namespace Epsic.Info3Dev.Rpg.Enums { public enum RpgClass { Knight = 1, Mage = 2, Cleric = 3 } } ``` --- - Pour finir, revenir dans le modèle `Character` et ajouter une propriété `Class` de type `RpgClass`. ```csharp namespace Epsic.Info3Dev.Rpg.Models { public class Character { public int Id { get; set; } public string Name { get; set; } = "Frodo"; public int HitPoints { get; set; } = 100; public int Strength { get; set; } = 10; public int Defense { get; set; } = 10; public int Intelligence { get; set; } = 10; public RpgClass Class { get; set; } = RpgClass.Knight; } } ``` - Le `{ get; set; } = RpgClass.Knight;` indique que la classe par défaut sera `Knight`. --- ## Nouveau contrôleur - Dans le dossier `Controllers` créer un contrôleur `CharacterController.cs` - Pour être utilisé comme un contrôleur notre classe doit - Hériter de `ControllerBase` : ```csharp public class CharacterController : ControllerBase ``` - Contenir l'attribut `[ApiController]` ```csharp [ApiController] public class CharacterController : ControllerBase ``` - Pour finir, il faut déterminer la route (URL) via laquelle le contrôleur sera accessible : ```csharp [ApiController] [Route("[controller]")] public class CharacterController : ControllerBase ``` --- - Ajouter le `using` suivant : ```csharp using Microsoft.AspNetCore.Mvc; ``` - Pour finir, ajouter une méthode `Get()` qui va retourner une instance de la classe `Character` : ```chsarp [HttpGet] public IActionResult Get() { var perso = new Character(); return Ok(perso); } ``` - On renvoie un ``IActionResult`` car cela permet de renvoyer au client des codes HTTP spécifiques ainsi que les données réelles qui ont été demandées. Dans cette méthode, avec `Ok(perso)`, nous renvoyons le code de statut 200 OK et notre personnage. --- ## Résultat ```json HTTP/1.1 200 OK Connection: close Date: Tue, 06 Oct 2020 21:56:11 GMT Content-Type: application/json; charset=utf-8 Server: Kestrel Transfer-Encoding: chunked { "id": 0, "name": "Frodo", "hitPoints": 100, "strength": 10, "defense": 10, "intelligence": 10, "class": 1 } ``` - On voit, sur la première ligne que l'on a bien `HTTP/1.1 200 OK` et dans le corps entre `{}` notre résultat : ```json { "id": 0, "name": "Frodo", "hitPoints": 100, "strength": 10, "defense": 10, "intelligence": 10, "class": 1 } ```
{"metaMigratedAt":"2023-06-15T13:42:22.694Z","metaMigratedFrom":"YAML","title":"I-CH 151 - .NET 6.0 Web API","breaks":true,"slideOptions":"{\"theme\":\"moonl\",\"spotlight\":{\"enabled\":false}}","contributors":"[{\"id\":\"2ff8bf3a-d09c-4308-a7a7-64e5fb1c4783\",\"add\":28630,\"del\":7835}]"}
    329 views