Last Updated: 2020-03-06 Nicolas Bonin

Références

Full documentation

Github

Learn Razor Pages

Dot Net Tutorials

TutorialsTeacher

Pourquoi ASP.NET Core?

ASP.NET Core n'est pas la continuité de ASP.NET 4.x (voir Choose between ASP.NET Core and ASP.NET 4.x), c'est une refonte et une ré-implémentation "from Scratch"

Des modifications architecturales permettent une infrastructure plus légère et modulaire.

ASP.NET Core peut s'exécuter en utilisant le runtime du .NET Framework Core mais aussi le runtime du le .NET Framework (grâce à l'implémentation commune de .NET Standard 2.0).

Par exemple, ASP.NET Core 2.x peut s'exécuter sur les frameworks .NET Framework > 4.6 (sous Windows seulement) ou .NET Core (multi-platforms).

L'implémentation est basée sur .NET Standard 2.0 (Spécifications communes pour les .NET frameworks tel que Mono, Xamarin, .NET Framework, .NET Core...)

ASP.NET Core permet aussi de créer des applications Web Self-Hosted (indépendantes de IIS), ou hébergées sur n'importe quel serveur web (IIS, NGinx, Apache, Docker...)

Avantages de ASP.NET Core

Tableau comparatif

Ce qui change par rapport à ASP.NET 4.x

Ce que l'on va apprendre

Dans ce CodeLab on va créer une application utilisant le framework ASP.NET Core.

Nous verrons:

.

Ce dont on a besoin

Vérification de la version du framework .NET Core

Lancer un cmd Shell (Taper la touche Win > cmd+Enter)

>dotnet --version

Help sur les commandes ( option "-h" ou "--help")

>dotnet -h
>dotnet new -h
>dotnet publish -h

Listes des templates (option "-l" ou "--list")

Lorsque l'on installe le framework .NET Core, un ensemble de templates est installé par défaut, pour voir la liste:

>dotnet new -l

Par exemple:

>dotnet new console -o cons1 #crée une application cons1 dans le dossier cons1
>cd cons1 
>dotnet run #execute le projet

Dans ce CodeLab, nous allons passez en revue plusieurs templates afin de comprendre différentes notions de ASP.NET Core.

Le framework ASP.NET Core offre deux systèmes de générations de pages dynamiques:

Le système MVC (Model/View/Controller) est plus connu car largement utilisé dans le précédent framework ASP.NET 4.x. Les principes restent les mêmes bien que des améliorations y aient été apportées.

Les Views dans MVC sont aussi des fichiers Razor (.cshtml), ce qui peut porter à confusion avec les "Razors Pages".

Les Razors pages sont la continuité des "Web Pages" de ASP.NET 4.x avec plusieurs améliorations, notamment l'intégration du Model (PageModel) comme dans le pattern MVC.

Les 2 systèmes utilisent le Razor Engine permettant de créer des vues partagées, vues partielles et de composants (Layout, PartialView) et de ré-utiliser des templates communs dans l'application.

Endpoint

Un Endpoint est un point de terminaison.

Sur un réseau, la notion de Endpoint correspondant à une adresse IP et un Port. L'adresse IP peut être associée à un nom de domaine.

Pour une application Web, le point de terminaison correspond au chemin entier de l'URL ainsi que le Verb HTTP de la requête.

Routage

Le routage est une notion importante en ASP.NET, elle définit comment les requêtes HTTP sont aiguillées vers les Endpoints.

Il y a plusieurs façons de configurer le routage:

Vous trouverez ici la documentation détaillée.

Par exemple:

Le framework ASP.NET Core définit un ensemble de "conventions" ou de "valeurs par défaut" qu'il faut connaître pour comprendre son fonctionnement par défaut.

Par exemple (par défaut):

#move to C:
   >cd / 
#Create a working folger
   >mkdir AspNetCore 

Création d'une application web "vide"

Afin de comprendre quelques concepts clés, nous allons créer une application web "vide" à partir du template "web" dans le répertoire web1

>dotnet new web -o web1
//-o means "output directory"

Lancer l'application:

>cd web1
>dotnet run

Création d'une solution et ajout du projet à la solution

Lancer un "Command Shell" et placez-vous dans le répertoire travail C:\AspNetCore

#show help
   >dotnet new sln -h 
#create an empty solution named AspNetCore.sln
   >dotnet new sln -n AspNetCore 
#add project to the solution
   >dotnet sln add AspNetCore.sln web1 
#open the solution in VS 2019
   >AspNetCore.sln

Architecture de base

Ouvrir le projet dans Visual Studio, discuter et comprendre l'architecture du projet.

Ouvrir les fichiers Program.cs et Startup.cs ainsi que Properties/launchSettings.json

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace web2
{
        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>();
                    });
        }
}

Startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

namespace web1
{
        public class Startup
        {
            // This method gets called by the runtime. Use this method to add services to the container.
            // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
            public void ConfigureServices(IServiceCollection services)
            {
            }

            // 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.UseRouting();

                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapGet("/", async context =>
                    {
                        await context.Response.WriteAsync("Hello World!");
                    });
                });
            }
        }
}
Exercice:
Ajouter un Endpoint qui "Map" le path "/Time" et qui retourne l'heure du server.

Cette notion est assez simple, mais très importante à comprendre!

Elle définit "Comment les requêtes HTTP sont traitées".

On peut voir les middlewares comme une chaîne d'intercepteurs permettant de générer la réponse HTTP.

C'est l'équivalent des HTTPHandlers et HTTPModules de ASP.NET.

Lorsqu'une requête HTTP arrive, elle passe séquentiellement par les middlewares configurés jusqu'à un middleware finalisant la requête, celle-ci remonte alors les middlewares en sens inverse qui peuvent encore agir sur la réponse (code après l'appel à "next();").

La méthode Configure() de Startup permet de configurer les "Middlewares" grâce à l'objet Application Builder app.

Pour chaque middleware, la requête HTTP et le Context HTTP sont disponibles.

La plupart des Middlewares sont implémentés par des méthodes d'extension de IApplicationBuilder nommées UseXxxxx() (ex:app.USeStaticFiles(), app.UseRouting(), app.UseAuthorization() ...).

Chaque Middleware s'exécute, puis passe la main au middleware suivant jusqu'à ce que tous les middlewares se soient exécutés ou qu'un middleware termine la requête (eg:Not Authorized).

Exemple de Middleware communément utilisés:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseCookiePolicy();
    app.UseAuthentication();
    app.UseSession();
    app.UseMvc();
}

On peut implémenter ses propres Middlewares, notamment avec les méthodes Use() et Run() de IApplicationBuilder, mais attention, il y a des règles à respecter et on ne peut pas faire n'importe quoi!

Exemple d'implémentation "In-line" d'un middleware

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
                        
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
             //Execute an Inline Middleware
             app.Use(async (context, next) => {
                await context.Response.WriteAsync("Middleware 1<br>");
                await next.Invoke();
                await context.Response.WriteAsync("Middleware 1 After Invoke<br>");
                        });
             //Run is a Terminal middleware in the Application Pipeline
                app.Run(async (context) =>
            {                                
                await context.Response.WriteAsync("Middleware 2<br>");                                
            });
        }

On peut aussi utiliser la méthode Map() pour exécuter un Middleware en fonction de l'URL.

public class Startup
{
    //Declare a final middleware function
    private static void HandleMultiSeg(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map multiple segments.");
        });
    }

    public void Configure(IApplicationBuilder app)
    {
       //Map the middleware function to the /map1/seg1 path 
       app.Map("/map1/seg1", HandleMultiSeg);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate.");
        });
    }
}

La méthodes ConfiguresServices() permet de créer les Instances des Services qui seront utilisés dans l'application (Log, dbContext, UserManagement...).

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.AddMvc();
}

Il existe tout une liste de services prédéfinis (méthodes d'extension de IServiceCollection). Et l'on peut aussi ajouter ses propres services.

Durée de vie des Services

Lorsque l'on crée et enregistre des services il est important de bien choisir sa configuration de durée de vie. Il y en a 3 sortes:

Injection de dépendance des Services

Grâce à l'injection de dépendance, les Services configurés dans ConfigureServices() seront mis à disposition automatiquement par le Service Provider lorsqu'un controller, un Page ou une Action le nécessite.

Le mécanisme d'injection permet d'injecter les services nécessaires dans tout Controller Page ou Action les nécessitant.

exemple:

Ci-dessous on ajoute le service d'accès aux données Sql Server de type MovieContext à la collection des Services de l'application.

Celui-ci sera Injecté par le mécanisme d'injection de dépendance (ServiceProvider) à tout controller dont le constructeur demande un service de type MovieContext.

Startup.cs

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        //Add MVC pattern 
        services.AddMvc();

        //Add a DbContext of type MovieContext to the service Collection
        services.AddDbContext<MovieContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("MovieDb")));
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseMvc();
    }
}

Le Service MovieContext sera injecté par le Service Provider dans tout Controller dont le constructeur réclame un service de type MovieContext

MyController.cs

public class MyController: Controller
{
    private readonly MovieContext _context;

    public MyController(MovieContext context)
    {
        _context = context;
    }
    // ...
    public async Task OnGetAsync()
    {
        var movies = from m in _context.Movies
                        select m;
        Movies = await movies.ToListAsync();
    }
}

Ou bien injection dans une action par l'attribut [FromService]

MoviesController.cs

public IActionResult About([FromServices] MovieContext context)
{
    ViewData["Movies"] = await context.Movies.ToListAsync();
    return View();
}

Ou bien injection directe dans une Razor pages par la directive @Inject

Movies.cshtml

@Page
@Inject MovieContext context
@foreach(var m in context.Movies)
{
   <div>@m.Title</div>
}

Nous allons étudier le template "razor" (synonyme "webapp") qui configure le nécessaire pour l'utilisation des pages Razor.

>dotnet new razor -o razor1
>cd razor1
>dotnet run

Tip:

Dans la version ASP:NET Core 3.0 dans ConfigureServices, l'appel à services.AddMvc() a été revu et remplacé par services.AddRazorPages() (voir point 2 de cette documentation ou dans celle-ci).

Startup.cs (pour ASP.NET Core 2.x)

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Includes support for Razor Pages and controllers.
        services.AddMvc();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseMvc();
    }
}

Startup.cs (pour ASP.NET Core 3.x)

 public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }

            public IConfiguration Configuration { get; }

            // This method gets called by the runtime.
               //Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddRazorPages();
            }

            // 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();
                }
                else
                {
                    app.UseExceptionHandler("/Error");
                    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                    app.UseHsts();
                }

                app.UseHttpsRedirection();
                app.UseStaticFiles();

                app.UseRouting();

                app.UseAuthorization();

                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapRazorPages();
                });
            }
        }
  1. Ajouter le projet "razor1" à la solution et l'ouvrir Visual Studio, puis parcourir et commenter l'architecture du projet.
  2. Passer en revue les fichiers Program.cs et Startup.cs et essayer d'en comprendre le fonctionnement.
  3. En quoi ce template est-il différent du template "web" vu précédemment?

Dossier /wwwroot

Le dossier wwwroot est le répertoire par défaut pour stocker les ressources web "statiques" (images, javascript, css...).

Pour que les fichiers soient accessibles, il faut que le middleware UseStaticFiles() soit ajouté au Request pipeline (nous verrons cela en détail dans le chapitre suivant).

Dossier /Pages

Explorer le répertoire /Pages et discuter des différents fichiers.

  1. /Pages/_ViewStart.cshtml
  1. Que signifie le "_" devant le nom du fichier?
  2. A quoi sert le le fichier _ViewStart.cshtml?
  1. /Pages/Shared/_Layout.cshtml
  1. Quel est sont les CSS et les librairies javascript utilisées?
  2. Quelles sont les instructions Razor dans la page et à quoi servent-elles?
  3. Identifier où sont générer les liens hypertext, qu'ont-ils de particulier?
  1. /Pages/Shared/_ValidationScriptPartial.cshtml
  2. /Pages/Index.cshtml
  1. Que signifie la directive @Page?
  2. Et la directive @model?
  3. Où le Title (ViewData["Tilte"]) de la page sera utilisé?
  1. Quelle est la classe de base d'une page Razor?

Quelles sont les conventions ici?

Est-il possible de modifier le répertoire par défaut "/Page" pour contenir les pages Razor?

Comment?

Exercice

En comparant le projet razor1 et web1, que faut-il modifier pour que le projet web1 supporte:

Faites-le fonctionner!

ASP.NET MVC Core fonctionne sur le même principe que dans la version ASP.NET MVC 4.x avec l'ajout des améliorations de ASP.NET Core (Tags Helpers, Injection de dépendances, anti-forgery automatique)

Rappel du Fonction de ASP.NET MVC

Le principe du pattern MVC (Model - View - Controller) et de séparer "les tâches" (separation of concerns).

Dans le pattern MVC, le controller se charge de récupérer les données grâce au model, s'occupe de la logique métier, puis transmet les données à la View qui s'occupe de la mise en page.

Ce pattern s'appuie sur un ensemble de règles:

Création d'une application ASP.NET MVC Core

Etudions maintenant le template "mvc" afin de comparer les différences avec les templates vus précédemment.

>dotnet new mvc-o mvc1
>cd mvc1
>dotnet run

Ajouter le projet à la solution dans Visual Studio parcourir et commenter l'architecture du projet.

Passer en revue les fichiers Program.cs et Startup.cs et essayer de comprendre le fonctionnement.

Quelles sont les différences avec les templates vus précédemment?

Startup.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace mvc1
{
        public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }

            public IConfiguration Configuration { get; }

            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddControllersWithViews();
            }

            // 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();
                }
                else
                {
                    app.UseExceptionHandler("/Home/Error");
                    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                    app.UseHsts();
                }
                app.UseHttpsRedirection();
                app.UseStaticFiles();

                app.UseRouting();

                app.UseAuthorization();

                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllerRoute(
                        name: "default",
                        pattern: "{controller=Home}/{action=Index}/{id?}");
                });
            }
        }
}

Que fait la fonction MapControllerRoute() ? Et quelles en sont les conventions?

Au besoin, avant l'exercice, si vous avez besoin de vous rafraîchir la mémoire sur le fonctionnement des controllers dans le pattern MVC.

Exercice:

En vous inspirant du code généré par le template "mvc", discutez de ce qu'il faut modifier dans le projet "web1" pour qu'il gère les pages MVC. Puis faites fonctionner une Action MVC.

Pour cela:

Razor n'est pas un langage de programmation mais une "markup syntax" qui permet de mixer du code HTML et C#. Le code C# est alors interprété par le serveur pour générer (ou non) de l'HTML.

Les expressions Razor commence toujours par "@"

Pour écrire le caractère @, il faut le doubler (@@trademark), sauf pour les mails (qui sont reconnus par Razor.

Exemple:

<p>@DateTime.Now</p>
<p>@DateTime.IsLeapYear(2016)</p>

Il serait un peu de passer en revue toute la syntaxe Razor, les documentations suivantes sont très bien détaillées:

Il existe plusieurs façons de découper et de réutiliser du code qui sont apparues historiquement.

Nous ne rentrerons pas dans les détails (trop) techniques, mais nous les verrons par des exemples dans nos TPs.

Les vues Partielles permettent d'écrire des morceaux de code Razor qui peuvent être ré-utilisés dans plusieurs Vues MVC ou/et dans les pages Razors. Ce sont des fichiers d'extension ".cshtml" que l'on nomme avec un préfixe "_". On peut utiliser le ViewData de la page appelante ou lui passer un model.

NB: le _Startup.cshtml n'est pas exécuté pour les vues partielles.

Les View Components, sont l'équivalent des Vues Partielles, mais ne dépendent pas d'un model mais de paramètres qui lui sont passés.

Lescomposants Razor, est un nouveau concept de ASP.NET Core 3.x qui est l'équivalent de vues partielles ou de View Components mais qui pourront en plus être utilisés avec le Framework Blazor. Ce sont des fichiers d'extension ".razor" qui peuvent être associés à un fichier "cshtml.cs" ou contenir un bloc @Code in-line.

Les Tags Helpers est une notion nouvelle par rapport à ASP.NET NVC 4.x.

On les utilise par exemple pour:

Voir la liste des principaux Tag Helpers.

On peut créer ses propres Tag Helpers.

Les Tags Helpers sont importés depuis la vue /Views/_ViewImports.cshtml par la directive @addTagHelper

_ViewImports.cshtml

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Ce qui signifie: Importe tous les Tags Helpers de Microsoft.AspNetCore.Mvc.TagHelpers.

Par exemple, le Tag helper asp-for

<label asp-for="Movie.Title"></label>

générera le html

<label for="Movie_Title">Title</label>

Par exemple, avant, pour un formulaire en ASP.NET NVC 4.x on avait:

Et avec ASP.NET Core, cela devient

Ce code est plus facile à lire et à maintenir et il y a moins de code C# pour générer le formulaire.

Il existe beaucoup de solutions (Indépendantes de ASP.NET Core) pour stocker des données et la plupart de ces solutions sont accessible dans le framework ASP.NET Core avec le package approprié.

EX:

Dans notre cas nous utiliserons les fichiers LocalDB, équivalents à SQL Server Express pour Visual Studio.

Modèle de données

Il nous faudra donc initialiser un service de type DbContext associé au modèle de donnée et à la source de données.

Lorsque l'on crée une application et que les données existent déjà (ou que l'on préfère créer le modèle de données avant de développer l'UI), on parle d'approche "Database First", sinon l'approche "Model First" dans laquelle on crée le modèle, puis on met à jour la base de données à partir du modèle au travers de migrations.

Entity Framework Core

Si vous connaissez l'Entity Framework, Entity Framework Core en est la version pour .NET Core.

l'Entity Framework Core est un ORM (Object Relational Mapping) qui permet de faire le lien entre nos données et le modèle de données.

Développer le fonctionnement de l'Entity Framework dépasse le cadre de ce cours, mais nous en utiliserons quelques rudiments.

Scaffolding signifie approximativement "échafauder" ou "générer" permet de à partir de templates prédéfinis (mais modifiables) de générer du code.

Visual Studio inclut un puissant outil de génération de code ce qui est parfois bien pratique et qui ici va nous permettre de mieux comprendre le fonctionnement du framework.

Nous allons utiliser la génération automatique de code de Visual Studio pour gérer un inventaire d'utilisateurs, et nous analyserons le code générer.

Création du Modèle de données:

Scénario:

Notre chef IT veut que l'on crée un Extranet pour nos utilisateurs externes à la société.

Il nous demande dans un premier temps de faire une interface permettant de gérer la liste des utilisateurs (Users) et de faire un POC (Proof of Concept) sur les différents template web, razor et mvc afin de choisir la solution la plus adaptée.

Afin de pouvoir ré-utiliser notre "Business Model" dans nos différents sites, nous allons créer une "Class Library" indépendante pour nos Entités.

  1. Créer un nouveau projet de type "Class Library (.Net Core)"
>dotnet new classlib -o MyCompany.Extranet.Models
  1. Ajouter un classe User.cs avec quelques propriétés (prop+Tab)

User.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MyCompany.Extranet.Models
{
    public class User
    {
            public int Id { get; set; }
            public String FirstName { get; set; }
            public int LastName { get; set; }
            public int Email { get; set; }
            public DateTime Birthdate { get; set; }
            public string  AvatarUrl { get; set; }
    }
}
  1. Faire un build
  2. Dans le projet "web1" référencer la librairie "MyCompany.Extranet.Models"
  3. Dans le répertoire /Pages ajouté un répertoire /Pages/Users
  4. Click-droit>Add>New Scaffolded Item>Razor Page Using Entity Framework CRUD
  5. Dans le dialogue, discuter des options proposées
  6. Sélectionner votre class Users et créer un nouveau dbContext (+)
  7. Visual Studio produit un message d'erreur, analysez-le et essayer de le résoudre
  1. Une fois le code généré, regarder ce qui a été modifié et l'expliquer (ainsi que Startup.cs, appsettings.json et les packages ajoutés en référence)
  2. Lancer l'application et aller sur http://localhost/Users
  3. Pourquoi ce message d'erreur?
  1. Lancer la console "Package Manager Console" et sélectionner votre projet dans la liste
  2. Taper la commande
>add-migration "Initial Migration"
  1. puis
>update-database
  1. A quoi servent ces commandes?
  2. Relancer le projet "web1" et faire fonctionner la page /Users (Insert/Update/Delete)
  3. Pourquoi, l'interface est-elle si moche?
  4. Essayez de reproduire les étapes ci-dessus dans le projet "razor1"
  5. Est-ce plus joli? Pourquoi?
  6. Que faut-il faire pour intégrer BootStrap dans le projet "web1"
  7. Essayer d'intégrer notre modèle dans le projet "mvc1" et de "Scaffolder" la gestion des Users
  8. Analyser et expliquer le code généré
  9. Comparez les pages mvc1/Views/Users/Index.cshml et razor1/Pages/Users/Index.html

Le rôle d'une application Web standard est de répondre à des requêtes HTTP en retournant des fichiers ou des pages HTML.

De nos jours, avec l'apparition du WEB 2.0 ou X.x, les développeurs s'oriente de plus en plus vers des framework Client-Side (Angular, React, View, Ionic....) leur permettant de faire des SPA (Single Page Application) dont la réactivité est fortement améliorée.

Les applications côté client font alors un grande utilisation de données au format JSON et le rôle du serveur est de le leur fournir et les mettre à jour. Cela se fait au travers d'une Web d'API.

Les Web APIs sont aussi très utilisées pour faire interagir des systèmes hétérogènes. Par exemple:

Au niveau du framework ASP.NET Core, les Web API, ne sont autres que des Controllers au sens MVC auxquels la gestion de la vue n'est pas utilisé.

En pratique, tous les controllers dérivent de la classe "ControllerBase".

En MVC, la classe "Controller" ajoute les fonctionnalités gérant la vue associée.

Créer une Web API à partir du template "webapi"

>dotnet new webapi -o webapi
>cd webapi1
>dotnet run

Startup.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace webapi1
{
        public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }

            public IConfiguration Configuration { get; }

            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddControllers();
            }

            // 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.UseHttpsRedirection();

                app.UseRouting();

                app.UseAuthorization();

                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllers();
                });
            }
        }
}

Quelles sont les différences avec le Startup.cs du project"mvc1"?

Et au niveau du Controller par défaut?

Quel est le format des données retourné lors de l'appel à l'url /weatherForcast?

Exercice:

  1. Lancer le projet "mvc1" et créer quelques Users
  2. Modifier le projet "webapi1" pour qu'il retourne les Users ainsi créés
  3. Quelle url permet de ne voir qu'un seul user à la fois
  4. Est-il possible d'effacer un User avec l'API? Comment?
  5. Est-il possible de créer un User avec l'API? Comment

La sécurisation d'un site web doit être analysée de manière approfondie avant toute mise en production.

Les principaux paramètres sont les suivants:

  1. Est-ce un intranet, extranet, un site public ou une API?
  2. Quelle stratégie de gestion des accès souhaite-on (Gestion des authentifications et authorisations)?
  3. Est-ce un système externe qui gère les User (Active Directory, LDAP, Identity Server..)? Si oui, comment les interfacer avec notre site?
  4. Doit-on ce protégé contre des Hackers? Quelles sont les vulnérabilités de notre site?

Gestion des Accès

Pour définir le type d'authentification acceptée par notre application, il faut définir un Service d'authentification dans notre classe Startup.cs.

Cela se fait par l'appel à :

services.AddAuthentication();

Par exemple, pour une authentification par Token JWT:

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => Configuration.Bind("JwtSettings", options))
        .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => Configuration.Bind("CookieSettings", options));

Puis il faudra fournir le middleware implémentant l'authentification dans la méthode configure()

app.UseAuthentication();

ASP.NET Core Identity

Le framework ASP.NET Core propose aussi le package ASP.NET Core Identity basé sur OWIN (Open Web Interface for .NET), permettant de gérer des Utilisateurs, des Rôles, des "Claims", des external provider (Twitter, google, facebook, Github...).

Ce package contient une implémentation pré-configurée de la gestion de l'authentification et des autorisations, avec les UI nécessaires pour ajouter/modifier/enregistrer des utilisateurs. Il permet aussi dans une certaine mesure d'être customisé.

Visual Studio propose aussi un Scaffolding pour ajouter le module Identity avec le choix de pouvoir modifier les pages par défauts (Projet > Add new scaffold Item > Identity).

Identity Server

Identity Server est une autre solution, open source, de la gestion centralisée des accès au sein d'une organisation (Authentification as a Service) utilisable par toute les applications ASP.NET.

Identity server permet entre autre de gérer le SSO (Sigle Sign On) au sein d'une organisation.

Lire cet article pour les détails

Lorsque l'on développe une application Web, bien le que Service Host gère les requêtes HTTP clientes de manière asynchrone, il est recommandé d'utiliser la programmation asynchrone pour les actions que l'on programme.

Le C# permet maintenant de facilement utilisé la programmation asynchrone grâce aux Task et à Async/Await.

Pour transformer une action synchrone en action asynchrone, il suffit de renommer cette action avec le suffix "Async" (par convention) et de modifier le "type retourné" par une Task<"Type retourné">