Créer un site web .Net Core MVC razor hébergé sur Raspbian

1. Objectif

Créer un site web avec sa base de données hébergé sur le raspberry pi sous Raspbian. Authentification sécurisé et présence d’un ORM permettant d’interfacer facilement les données sur un site MVC.

Ce tutoriel nécessite la connaissance de nombreux concept que nous tenterons d’aborder, ou tout au moins de vous donner les ressources nécessaire à leur compréhension.

Je dois avouer que ce ne fut pas une mince affaire car il reste difficile de trouver des informations/tutoriaux pertinent sur le sujet (Technologie récente). Disons qu’ils y a ceux émis par Microsoft ou Oracle et tout ce que l’on trouve reprend presque texto ces informations de base. On ne trouve rien de complet et encore moins en Français.

Alors me direz vous pourquoi faire cela ? Sous linux, on a déjà tous des sites en php hébergé sur Apache !

Alors d’une part, je suis historiquement développeur en C#. C’est un langage que j’affectionne particulièrement, en perpétuelle évolution.

D’autre part, parce que nous allons utiliser .Net Core qui possède bien des avantages : Il est multiplateforme, multi langage, open source mais c’est surtout qu’il possède une particularité majeure par rapport à ses prédécesseurs frameworks .Net : Un vrai changement de paradigme. Il est de base très léger et fonctionne par injection de dépendance. A tel point que pour faire tourner un site web, il n’y a même pas besoin d’un serveur web type IIS ou Apache. Votre site peut être un exécutable capable de rendre les demandes http. Cela lui confère de belles performances. Il sera toujours possible de le publier derrière un serveur web afin d’offrir d’autres fonctionnalités.

Enfin, nous verrons que l’environnement Visual Studio ainsi que ces technologies sont pensées pour réaliser des applications rapidement. C’est bluffant de voir comment l’on peut générer rapidement un modèle de données et les pages pour l’administrer. De nos jours, nous réalisons des applications de plus en plus complexe et la rapidité a donc son importance.

 

2. Pré requis

Et oui j’ai pris l’habitude de donner la liste des pré requis donc on ne va pas y couper. L’idée étant d’installer notre site sur le raspberry mais de garder notre confort de travail sur notre environnement de développement (ici un PC sous windows 10). Néanmoins le raspberry doit être autonome quand à l’hébergement du site et des données de celui ci.

  a. Matériel

  • Un raspberry Pi (ici un 3 mais un 2 peut faire l’affaire). On y aura installé raspbian ainsi que le framework .Net Core (voir ces tutos installation et conf du Raspberry puis installation .Net Core et Hello World).
  • Un PC (ou Mac) sous Windows (ou Linux ou Mac Os).
  • Une Box ou un routeur afin de pouvoir configurer nos machines en réseaux (On définira une IP fixe pour le raspberry).

  b. Logiciel

  • Visual Studio 2017 pour développer (Plus pratique pour .Net Core et surtout plus léger, il adopte la même philosophie que le Framework). Vous le trouverez ici.
  • Installez également .Net Core 2.X et le SDK .Net Core 2.X (vous trouverez le SDK ici)
  • Workbench pour administrer et requêter la base de données. Vous le trouverez ici.
  • VNC et/ou putty pour accéder à notre raspberry.

Bon avant de passer au concret, parlons un peu de ce que nous allons utiliser. En effet, les choix ne sont pas anodins et il me faut présenter un peu plus les technologies.

3. Ce qu’il y a savoir

Je souhaite réaliser à terme une application complète dont l’interface utilisateur se présentera sous la forme d’un site web. Ici, nous voulons utiliser le raspberry Pi pour héberger le site ainsi que la base de données.

le raspberry est une incroyable machine compte tenu de son rapport prix/performance/consommation. De plus, nous pouvons facilement l’interfacer avec tout un ensemble de périphérique. Malgré tout cela reste une machine ayant des capacités limités, il faut donc être prudent lorsque l’on souhaite y héberger un site avec sa base de données.

Je souhaite également travailler autour d’un environnement de travail performant et dit RAD (Rapid Application Development). L’idée étant de produire un site autour de données qui pourront facilement être administré dans un environnement sécurisé.

Pour toutes ces raisons nous allons utiliser une application ASP.NET Core Razor accompagné d’EntityFramework Core.

  A. .NET Core et ASP.NET Core Razor

J’en ai déjà parlé, .Net Core est un framework assez récent performant, open source et multiplateforme. Nous serons sur Linux et il n’y aura aucun problème pour faire fonctionner notre application, tout autant que sur notre PC pour le débugger; voilà un avantage non négligeable.

ASP.NET est la partie du framework dédié à la création d’application web. Depuis des années déjà, l’ASP.NET est utilisé depuis le framework .Net 1 jusqu’au versions 4. L’introduction de MVC (Modèle Vue Controleur) a simplifié la gestion des interfaces en adoptant une architecture à la fois souple, pratique et adapté à la réalisation de site web ou plusieurs cœurs de métiers peuvent intervenir (développeur, monteur).

C’est dans cette droite ligné que l’ASP.Net Core est apparu.  (Présentation de .Net Core et ASP.Net Core)

Razor quand à lui est une surcouche de l’ASP.NET permettant de simplifier l’écriture des pages web. Il permet d’introduire dans le code HTML du code dynamique en toute simplicité. Vous trouverez facilement de nombreuse ressource concernant ce moteur. (Présentation de Razor)

 

  C. EntityFramework Core

EntityFramework Core est un ORM (voir ici) qui permet de créer dans votre code objet des class dite Entités représentant généralement des tables de votre modèle de données relationnel. Il est alors possible via Linq de requêter votre base et de l’administrer en manipulant des collections d’entités.

Nous utiliserons l’approche code first qui consiste à créer dans le code des entités et à générer automatiquement le modèle dépendant en base ainsi que les pages pour l’administrer (Vous verrez, c’est bluffant de simplicité et de rapidité).

 

  D. MariaDb

MariaDb est un moteur de base de données relationnel. Il s’agit d’un fork de MySql (voir présentation). Vous pourrez trouver de nombreuses informations concernant notamment les performances du moteur. MariaDb est donc une bonne alternative à Mysql notamment pour un raspberry. Nous pourrons utiliser les outils classique pour y accéder comme par exemple ici Workbench.

 

4. Réalisation

 

La plupart des manipulations se réaliserons à l’aide de Visual Studio, néanmoins, il nous faut préalablement installer MariaDb sur le raspberry pi. Les site web .NET Core n’ont pas forcément besoin d’un serveur web en tant que tel (Par exemple Apache). En effet, le framework .net Core permet d’avoir un site web auto hébergé via le Kestrel (Infos détaillé sur Kestrel)

 

A. Installer MariaDb

Sur votre connexion ssh ou VNC, lancez la commande

sudo apt-get install mariadb-server

On se connecte pour vérifier que tout fonctionne mais également pour modifier le mot de passe root.

mysql -u root use mysql; Select user, password, plugin from user; 

Vous pouvez qu’il y a une ligne pour le compte root que nous allons modifier pour plusieurs raison. La sécurité d’une part, il est toujours bon de changer les mots de passe par défaut mais aussi pour préparer l’accès à la base depuis l’extérieur (et non seulement localhost).

Lancez la commande suivante pour modifier le mot de passe (vous remplacerez VotreMotDePasse pour le mot de passe désiré). Puis on réalise un redémarrage du service.

UPDATE user SET password=PASSWORD('VotreMotDePasse') WHERE User='root' AND Host = 'localhost'; FLUSH PRIVILEGES;

service mysql restart

On se connecte maintenant avec le compte root, on va en profiter pour préparer l’accès à l’extérieur. Vous aurez préalablement noté l’IP fixe de votre raspberry (ici IpDuRasberry)

mysql -u root -p

tapez le mot de passe et lancez la commande en remplaçant IP et mot de passe.

GRANT ALL PRIVILEGES ON *.* TO 'root'@'IpDuRasberry' IDENTIFIED BY 'VotreMotDePasse' WITH GRANT OPTION;
GRANT ALL PRIVILEGES ON *.* TO 'root'@'IpDuPc' IDENTIFIED BY 'VotreMotDePasse' WITH GRANT OPTION;

Quittez mysql

exit;

maintenant nous allons éditer le fichier de configuration my.cmd pour configurer l’accès depuis l’extérieur (vous trouverez des infos ici)

 sudo nano /etc/mysql/my.cmd

Rajoutez les lignes suivantes à la fin du fichier ou vous remplacerez le mot de passe.

[client]
password=VotreMotDePasse
port=3306
socket=/tmp/mysql.sock

# The MySQL server
[mysqld]
port=3306
socket=/tmp/mysql.sock
bind-address=0.0.0.0
temp-pool

enregistrez, quittez et redémarrer le service mysql.

service mysql restart

Vous pouvez maintenant installer sur votre pc le client Workbench. Si vous faite une tentative de connexion, elle risque de n’être possible qu’à travers la connexion tunnel SSH. Néanmoins, on souhaite réaliser une connexion TCP/IP standard. Il faut donc autoriser le port 3306.

sudo iptables --list

vous allez voir cela normalement

Chain INPUT (policy ACCEPT)
 target prot opt source destination

Chain FORWARD (policy ACCEPT)
 target prot opt source destination

Chain OUTPUT (policy ACCEPT)
 target prot opt source destination

Rajoutons le port

sudo iptables -A INPUT -i eth0 -p tcp --destination-port 3306 -j ACCEPT

Vous pouvez maintenant tester, la connexion TCP/IP doit fonctionner. Nous avons maintenant un SGBDR accessible depuis l’extérieur.

 

B. Création du site web MVC Razor.

Notre objectif est de créer une application web ou l’on pourra administrer et sécuriser des News (A titre d’exemple).

Nous allons commencer par créer la base de données dont les tables seront créé par directement par EntityFramework. Lancez workbench.

Faite un clic droit dans la partie schéma puis Create Schema

 

Nommons la base MaBase et cliquez sur Apply (validez ensuite les étapes).

Lancez Visual Studio 2017, puis fichier > nouveau > Projet.

Dans la partie .Net Core, choisissez une application ASP .NET Core que nous nommerons MonAppliDeNews, cliquez sur OK.

Ensuite, nous devons choisir le type de site à créer. Vous choisissez Application Web (Vous voyez dans la description que l’on parle d’exemples Razor).

Cliquez sur Modifier l’authentification. Cliquez sur Compte d’utilisateur individuels et stocker les comptes d’utilisateurs dans l’application. Il est à noté qu’il sera facile de réaliser des authentifications facebook, twitter, … Pour ceci référez vous à la documentation microsoft (ici)

 

 

 

Vous voilà maintenant en présence de votre solution. Je pourrais passer queques heures à vous expliquer l’ensemble de ce que voyez mais citons simplement l’essentiel: Dans la partie dépendances, vous retrouverez l’ensemble des dépendances du projet, comme je vous l’ai dit .Net Core fonctionne par injection de dépendances. On y retrouve le framework lui même, le CSharp, l’ASP.Net Core etc… Vous verrez plus tard qu’à travers Nuget nous allons rajouter certaines dépendances (EntityFramework ainsi que le provider pour MariaDb : Pomelo).

Vous retrouvez ensuite wwwroot qui comprend les éléments front d’un site web, css, js, bootstrap ou encore JQuery.

Puis un ensemble de répertoires Controllers, Data, Extensions, Pages et Services qui représente l’architecture de notre site MVC.

Nous nous attarderons par la suite sur quelques fichiers clés.

De base votre application est créé pour utiliser une base de données SQL Server et fonctionner sur votre PC. N’oubliez pas que nous souhaitons utiliser une base MariaDb ainsi qu’héberger notre site sur Raspberry (il faut donc compiler pour processeur ARM).

    1. Installation des composants EntityFramework et Pomelo

Nuget est l’outil Visual Studio permettant d’enrichir nos solutions de fonctionnalités. Il peut simplement rajouter une dépendance comme installer et configurer un ensemble d’éléments dans nos projets. A ce titre, il nous servira à enrichir notre solution de l’ORM Entity Framework et du provider pour accéder à la base de données MariaDb. Il nous servira également à générer les vues permettant d’administrer nos données (ici des news).

Allez dans Outils>Gestionnaire de Package Nuget>Gérer les packages pour la solution.

Cliquez sur Parcourir et dans le champs de recherche tapez EntityFrameworkCore. Vous noterez en scrollant  qu’il en existe une multitude. C’est parfois le piège des packages ‘Que choisir’. Des recherches préalables sont souvent nécessaire. Ici par exemple avant de prendre les packages Pomelo j’ai essayé les packages MySql puisque MariaDb est censé être full compatible. Malheureusement, cela n’a pas bien fonctionné.

Tapez dans le champs de recherche Pomelo EntityFrameworkCore. Sélectionnez Pomelo.EntityFrameworkCore.MySql et dans la partie de droite cochez la case MonAppliDeNews.  Vous noterez que tout en bas à droite, on peut voir la liste des dépendances du package. Cela permet de vérifier qu’il est compatible avec notre projet.

Validez les boites de dialogues, vous devriez voir les actions de Nuget dans la fenêtre de sortie. Faite de même avec Pomelo.EntityFrameworkCore.MySql.Design. C’est ce package qui va nous permettre de générer automatiquement le modèle de données. Faite ensuite de même pour Microsoft.VisualStudio.Web.CodeGeneration.Design s’il n’est pas déjà installé. C’est ce package qui va nous permettre de générer automatiquement les vues dépendantes de nos objets Entités.

Nous avons maintenant les packages nécessaire au fonctionnement de notre projet. Passons maintenant au code.

    2. Paramétrer le projet pour utiliser la base MariaDb ‘MaBase’

Par défaut le projet utilise un contexte de base de données puisque nous implémentons la gestion des utilisateurs. Ce context est défini pour une base SQL Server. Nous devons modifier ces éléments pour utiliser notre base MariaDb.

Allez dans le fichier appsettings.json. Il s’agit des paramètres de notre application. Nous allons modifier la chaine de connexion à la base de données ConnectionStrings. Pour la valeur DefauttConnection indiquez la valeure ci dessous en remplaçant l’IP (chez moi le raspberry est 192.168.0.101) et le MoDePasse que vous avez indiqué pour le root de MariaDb.

Chaine de connexion à la base
"ConnectionStrings": {
   "DefaultConnection": "Server=192.168.0.101;Database=MaBase;Uid=root;Pwd=MotDePasse;"
  }

 

Allez maintenant dans le fichier Startup.cs

Dans la méthode ConfigureServices vous devez y voir l’ajout d’un DbContext que nous allons modifier en remplaçant UseSqlServer par UseMySql

services.AddDbContext<ApplicationDbContext>(options =>
                options.UseMySql(Configuration.GetConnectionString("DefaultConnection")));

Maintenant que nous avons tout ce qu’il faut. Nous allons demander à Nuget de générer les tables dans la base de données.

Allez Dans Outils>Gestionnaire de Package Nuget> Console du gestionnaire de Package

Tapez la commande

Add-Migration Initial 

Cela va générer dans votre projet dans la partie Data un répertoire migration et une class …initial.cs. Cette class contiendra les élements nécessaire à fournir pour un potentiel déploiement sur une autre base. Nous avons nommé cette migration Initial par convention car il s’agit de la 1er. Nous utiliserons ensuite d’autres noms (par exemple des numéros de version). A chaque fois une class sera généré et contiendra les éléments de base de données que nous aurons pu ajouter.

Tapez ensuite la commande suivante:

Update-Database

Vous pouvez maintenant allez voir votre base de données. Nuget a créé le modèle de données nécessaire à la gestion des utilisateurs ainsi qu’à la gestion des delivery EntityFramework.

Magnifique, nous avons maintenant une base de données MariaDb opérationnelle pour notre projet. Vous pouvez d’ore et déjà lancer le projet sous Visual Studio en appuyant sur le bouton play> IIS Express

Nous avons maintenant une application web fonctionnelle avec différentes parties, header, content, footer et la partie pour la gestion des utilisateurs sur la droite. Vous pouvez d’ores et déjà créer un utilisateur.

Sous MariaDb, vous pouvez taper la requête suivante

USE MaBase;
SELECT * FROM AspNetUsers;

Vous noterez la présence de l’utilisateur que vous venez de créer mais également et surtout que le champs password est crypté;  La sécurité avant tout !

Vous pouvez maintenant arrêter votre application en cliquant sur le bouton stop dans Visual Studio.

 

    3. Création de notre gestion des news

A titre d’exemple, nous allons créer des news qui seront caractérisé par les informations suivantes : Un identifiant, un titre, une date, un contenu ainsi qu’une catégorie. La catégorie sera caractérisé quand à elle par un identifiant et un titre..

        a. Création des Entités et des contextes

Dans la partie Data du projet, faite un clic droit >Ajouter > Nouveau Dossier > ‘News’

Puis dans ce dossier clic droit > Ajouter >  Class (bien prendre Class dans la partie Code) et ajouter les class News et CategoryNews.

Y mettre respectivement le code suivant. Attention à garder le namespace ‘MonAppliDeNews.Data’

public class NewsCategory
{
	public int Id { get; set; }
	[Required, Display(Name ="Nom")]
	public string Title { get; set; }
}
	
public class News
{
	public int Id { get; set; }
	[ForeignKey("NewsCategoryId"), Required(ErrorMessage = "La catégorie est obligatoire."), Display(Name = "Catégorie")]
	public int CategoryId { get; set; }
	[NotMapped]
	public NewsCategory Category { get; set; }
	[Required(ErrorMessage = "Le Titre de la news est obligatoire."), Display(Name ="Titre")]
	[MaxLength(50, ErrorMessage = "La catégorie doit comporter moins de 21 caractères."), MinLength(10)]
	public string Title { get; set; }
	[Display(Name = "Date de création")]
	public DateTime Created { get; set; }
	[Required, Display(Name ="Contenu")]
	public string Content { get; set; }
}

On appel ce type de class des entités. Elles n’ont pour vocation qu’à porter des données provenant de la base de données. C’est EntityFramework qui va peupler des collections de ces objets ou qui va écrire les requêtes SQL pour les créer, les modifier ou les supprimer. Vous noterez la présence d’annotations permettant de spécifier qu’un champs est requis, la taille min, max, … permettant de définir les éléments en base mais aussi les alertes d’application lorsqu’un champs n’est pas renseigné.

Maintenant que nous avons ces données, nous devons ‘expliquer’ à notre context de base de données que nous souhaitons maintenant avoir ce type de données.

Ouvrez maintenant le fichier ApplicationDbContext.cs présent dans ce même répertoire Data. Vous pouvez voir que celui ci hérite de IdentityDbContext. Renommez cette class ApplicationSecurityDbContext.

Dans la même idée, nous allons déclarer notre propre context de base de données qui contiendra l’ensemble de nos données.

Dans ce même répertoire Data créez la class ApplicationDataContext qui hérite de DbContext

ApplicationDataContext
public class ApplicationDataContext  : DbContext
    {
        public ApplicationDataContext(DbContextOptions<ApplicationDataContext> options)
                : base(options)
        {
        }

        public DbSet<News> NewsList { get; set; }

        public DbSet<NewsCategory> NewsCategories { get; set; }
    }

Nous venons de créer un DbSet pour les catégories et les news. Nous devons maintenant rajouter ce context à notre site. Charger le fichier Startup.cs

Sous le context déjà présent, rajoutez le second comme ceci :

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

            services.AddDbContext<ApplicationDataContext>(options =>
                options.UseMySql(Configuration.GetConnectionString("DefaultConnection")));

Nous avons maintenant tous les éléments, nous devons mettre à jour la base de données.

 

        b. Création du modèle de données dépendant

Dans la console Nuget tapez

Add-Migration -Context:ApplicationDataContext v1.1

Update-Database -Context ApplicationDataContext

Nous devons maintenant préciser quel context nous utilisons puisque nous en avons plusieurs. On rajoute un point de migration, et nous réalisons une mise à jour de la base de données.

Vous pouvez maintenant vérifier le résultat sous Workbench.

Vous noterez la présence de nos deux tables et notamment de la clé étrangère CategoryId dans les news.

Il nous faut maintenant créer les interfaces permettant d’administrer ces données.

 

        c. Interface utilisateur d’administration des données.

Cela pourrait paraître compliqué mais en fait rien de plus simple. Avec une ligne de commande le tour est joué. Pour ces lignes de commande, nous allons aller dans Powershell (Cela peut ne pas fonctionner dans la console Nuget ! Je ne sais pas vraiment pourquoi).

Lancez un Powershell et allez dans le répertoire du projet ou se trouve le startup.cs.

Lancez la commande suivante :

dotnet aspnet-codegenerator razorpage -m NewsCategory -dc ApplicationDataContext -udl -outDir Pages\News\Category --referenceScriptLibraries

Toutes les pages nécessaire à l’administration de nos catégories sont ainsi créés. Vous noterez que l’on a placé les pages d’admin des catégories en sous répertoire des celles des news.

Nous pouvez maintenant retourner sous visual studio et admirer la présence des pages. Vous noterez que sous les pages cshtml, il y a les .cs (le code behind : exécuté côté serveur).

 

        d. Annotations

Il est possible de rajouter des annotations devant les propriétés de nos objets. Cela permet de préciser si les champs sont obligatoire, s’ils possèdent une taille particulière ou encore de forcer le nom d’une clé étrangère. Nous les avions positionné dès le départ mais il est possible de réaliser des modifications en utilisation les points de migration et l’update de la base de données.

Vous noterez la présence des 2 using DataAnnotations.

Vous remarquerez également qu’il est possible de personnaliser les messages d’erreurs si les conditions ne sont pas respectées ainsi que les noms de champs sur le rendu visuel. Générons maintenant les pages news (dans Powershell) :

dotnet aspnet-codegenerator razorpage -m News -dc ApplicationDataContext -udl -outDir Pages\News --referenceScriptLibraries

Si vous compilez la solution vous avez une série d’erreur concernant ces .cs. C’est parce que nous avons créé les pages sous un répertoire du même nom et donc le namespace de la page interfère avec des déclarations. Il faut alors aider le compilateur pour lui expliquer que l’on parle bien des News présentent dans MonAppliDeNews.Data.

Remplacez pour chacune des erreurs

public News News { get; set; }  par public MonAppliDeNews.Data.News News { get; set; }

Vous pouvez maintenant compiler et lancer l’application.

Dans l’url rajoutez /news/category. Vous pouvez maintenant rajouter des catégories à votre guise et vérifier leur présence en base de données.

Dans l’url retirez /category et lancez la création d’une news. Appuyez directement sur Create. Vous remarquez que les catégories n’apparaissent pas sous la forme d’un choix dans une liste déroulante. On va donc avoir un peu de travail !

Sachez qu’il existe une multitude de générateur de code à partir d’EntityFramework et du modèle de données (A tester).

Stoppez maintenant l’application.

       e. Ajout de la catégorie

Vous avez peut être remarqué que dans l’entité News, nous avions mis deux propriétés concernant la catégorie : La catégorie elle même de type NewsCategory ainsi qu’un int représentant l’identifiant de la catégorie (CategoryId) dont le foreign key est précisé selon la convention de nommage.

En effet, nous souhaitons pouvoir renseigner l’identifiant de la catégorie pour la base de données et d’un autre côté pouvoir l’utiliser directement dans le code, d’où la présence des deux propriétés.

Nous allons donc devoir charger depuis la base les catégories pour la création et la modification. En ce qui concerne les autres affichages, nous allons devoir remplacer l’identifiant par le titre lui même.

Chargez la page Create.cshtml.cs des News qui correspond au code behind de la page. nous allons rajouter une propriété de liste d’éléments qui sert à construire le select et remplacer la méthode OnGet par sa version asynchrone :

public List<SelectListItem> Categories { get; set; }

        public async Task<IActionResult> OnGetAsync()
        {
            //On va charger la liste des catégories
            var tempCats = await _context.NewsCategory.ToListAsync();                
            if(tempCats != null)
                Categories = tempCats.Select(u => new SelectListItem
                {
                    Text = u.Title,
                    Value = u.Id.ToString()
                }).ToList();
            return Page();
        }

Chargez maintenant la page elle même Create.cshtml

<form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="News.Title" class="control-label"></label>
                <input asp-for="News.Title" class="form-control" />
                <span asp-validation-for="News.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="News.CategoryId" class="control-label"></label>
                @Html.DropDownListFor(x => x.News.CategoryId, new SelectList(Model.Categories, "Value", "Text"), htmlAttributes: new { @class = "form-control", id = "CategoryId" })
                <span asp-validation-for="News.CategoryId" class="text-danger"></span><br />
            </div>
            <div class="form-group">
                <label asp-for="News.Created" class="control-label"></label>
                <input asp-for="News.Created" class="form-control" />
                <span asp-validation-for="News.Created" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="News.Content" class="control-label"></label>
                <input asp-for="News.Content" class="form-control" />
                <span asp-validation-for="News.Content" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </form>

Vous noterez que le dropdown est présent pour renseigner News.CategoryId mais que la construction est basé sur Model.Categories.

Au tour du Details.cshtml.cs

public async Task<IActionResult> OnGetAsync(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            News = await _context.News.SingleOrDefaultAsync(m => m.Id == id);

            if (News == null)
            {
                return NotFound();
            }

            News.Category = await _context.NewsCategory.SingleOrDefaultAsync(m=> m.Id == News.CategoryId);

            return Page();
        }

Dans la page Details.cshtml, il suffit de spécifier le titre de la catégorie comme suit :

<dt>
            @Html.DisplayNameFor(model => model.News.CategoryId)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.News.Category.Title)
        </dd>

Pour la partie Edit.cshtml.cs

public List<SelectListItem> Categories { get; set; }

        public async Task<IActionResult> OnGetAsync(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            News = await _context.News.SingleOrDefaultAsync(m => m.Id == id);

            if (News == null)
            {
                return NotFound();
            }

            //On va charger la liste des catégories
            var tempCats = await _context.NewsCategory.ToListAsync();
            if (tempCats != null)
                Categories = tempCats.Select(u => new SelectListItem
                {
                    Text = u.Title,
                    Value = u.Id.ToString(), 
                    Selected = u.Id == News.CategoryId
                }).ToList();

            return Page();
        }
Edit.cshtml
<div class="form-group">
                <label asp-for="News.CategoryId" class="control-label"></label>
                @Html.DropDownListFor(x => x.News.CategoryId, new SelectList(Model.Categories, "Value", "Text"), htmlAttributes: new { @class = "form-control", id = "CategoryId" })
                <span asp-validation-for="News.CategoryId" class="text-danger"></span><br />
            </div>

Enfin la page d’index; index.cshtml.cs

public async Task OnGetAsync()
        {
            News = await _context.News.ToListAsync();

            var tempCats = await _context.NewsCategory.ToListAsync();

            foreach(MonAppliDeNews.Data.News el in News)
                el.Category = tempCats.FirstOrDefault(c => c.Id == el.CategoryId);
        }

Ici on parcours toutes les news pour charger à chaque fois la catégorie.

Pour la page, il faut simplement remplacer le CategoryId.

index.cshtml
<td>
                        @Html.DisplayFor(modelItem => item.Category.Title)
                    </td>

On a fait le tour des pages mais maintenant on voudrait bien faire en sorte que les actions de modification sur base ne puisse avoir lieu que si l’on est connecté.

Il faut donc sécuriser tout cela.

     f. Sécurisation des pages

Vous pouvez déjà vous référer à ce lien pour avoir des informations détaillés. Nous allons d’une part sécuriser les pages en elle même puis customizer l’affichage pour ne pas afficher ce qui ne doit pas l’être (liens Créer, Modifier, …).

        1. Sécurisation des pages

Dans la page startup.cs vous pouvez ce code :

services.AddMvc()
                .AddRazorPagesOptions(options =>
                {
                    options.Conventions.AuthorizeFolder("/Account/Manage");
                    options.Conventions.AuthorizePage("/Account/Logout");
                });

que vous pouvez remplacer par ceci :

services.AddMvc()
                .AddRazorPagesOptions(options =>
                {
                    options.Conventions.AuthorizeFolder("/Account/Manage");
                    options.Conventions.AuthorizePage("/Account/Logout");
                    options.Conventions.AuthorizeFolder("/News/Category").AllowAnonymousToPage("/News/Category/Index")
                                                                         .AllowAnonymousToPage("/News/Category/Details");
                    options.Conventions.AuthorizeFolder("/News").AllowAnonymousToPage("/News/Index")
                                                                .AllowAnonymousToPage("/News/Details");
                });

on restreint l’autorisation de tout le répertoire puis on donne l’accès sur des pages.

        2. Modification affichage.

Il faut ajouter sur les pages l’injection de dépendance de gestion des droits et faire un test sur l’authentification.

Voici un exemple, vous ferez de même pour les autres pages.

index.cshtml
@page
@model MonAppliDeNews.Pages.News.IndexModel
@inject SignInManager<ApplicationUser> SignInManager

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>
@if (SignInManager.IsSignedIn(User))
{
    <p>

        <a asp-page="Create">Create New</a>
    </p>
}
    <table class="table">
        <thead>
            <tr>
                <th>
                    @Html.DisplayNameFor(model => model.News[0].CategoryId)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.News[0].Title)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.News[0].Created)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.News[0].Content)
                </th>
                <th></th>
            </tr>
        </thead>
        <tbody>
            @foreach (var item in Model.News)
            {
                <tr>
                    <td>
                        @Html.DisplayFor(modelItem => item.Category.Title)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.Title)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.Created)
                    </td>
                    <td>
                        @Html.DisplayFor(modelItem => item.Content)
                    </td>
                    <td>
                        @if (SignInManager.IsSignedIn(User))
                        {
                        <a asp-page="./Edit" asp-route-id="@item.Id">Edit</a>@Html.Raw("|")
                        }
                        <a asp-page="./Details" asp-route-id="@item.Id">Details</a> 
                        @if (SignInManager.IsSignedIn(User))
                        {
                               @Html.Raw("|")<a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
                        }
                    </td>
                 </tr>
            }
        </tbody>
    </table>

Histoire de finir, rajoutons nos deux rubriques dans le Header. Éditez le fichier _Layout.cshtml et complétez le menu ainsi

<div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li><a asp-page="/Index">Home</a></li>
                    <li><a asp-page="/About">About</a></li>
                    <li><a asp-page="/Contact">Contact</a></li>
                    <li><a asp-page="/News/Category/Index">Catégorie</a></li>
                    <li><a asp-page="/News/Index">News</a></li>
                </ul>
                @await Html.PartialAsync("_LoginPartial")
            </div>

Vous pouvez maintenant tester en étant connecté ou non pour voir les différences. L’application est maintenant fonctionnelle. Adaptons maintenant notre solution pour la déployer sur le raspberry.

4. Ajout des éléments de publication pour pouvoir déployer notre site sur Raspberry

Nous allons modifier le projet pour lui indiquer qu’il faut compiler pour plusieurs sorties. Pour cela, il faut décharger le projet.

Allez dans l’explorateur de solution et faite un clic droit sur le projet et choisissez décharger.

 

Puis refaite clic droit et modifier le fichier. Vous allez voir un fichier xml dans lequel apparait une balise <PropertyGroup>

Rajouter la balise RuntimeIdentifiers :

<PropertyGroup>
    <TargetFramework>netcoreapp2.0</TargetFramework>
    <UserSecretsId>aspnet-MonAppliDeNews-B5A23284-EA04-4A12-A6B9-822F4DCD7A47</UserSecretsId>
    <RuntimeIdentifiers>win-x64;win10-x64;linux-arm</RuntimeIdentifiers>
  </PropertyGroup>

Enregistrez, puis faite clic droit sur le projet et recharger.

Nous allons maintenant rajouter l’appel au Kestrel que nous avions évoqué au début du tutoriel.

Dans program.cs rajouter UseKestrel()

WebHost.CreateDefaultBuilder(args)
.UseKestrel()
.UseStartup<Startup>()
.Build();

Nous somme maintenant prêt. Reste à définir la publication sur le raspberry Pi.

    a. Définir la publication sur le raspberry Pi

Allez dans affichage>Barrres d’outils> et cliquez sur Publication Web en 1 clic. Vous verrez apparaître dans la barre de tâche en haut une partie publication que nous utiliserons plus tard.

Cliquez ensuite sur Générer> Publier MonAppliDeNews.

Nous allons publier sur un dossier du Raspberry Pi (dans le partage).

Cliquez sur Dossier et choisissez un dossier sur la raspberry.

Cliquez sur l’icone de paramètres à côté du bouton publier. Puis choisissez Paramètres.

Nous allons choisir ici la cible linux-arm, définir la suppression complète des fichiers en cas de publication. Nous n’avons rien à changer pour la base de données ou les scripts de Entity Framework puisque la base est commune mais vous noterez que d’ici on peut paramétrer directement ce que l’on publiera (si on se trouve vers une base de production par exemple). Cliquez sur Enregistrer

Vous n’avez plus qu’à cliquer sur le bouton de publication pour déployer l’application sur le Raspberry Pi.

Vous pouvez maintenant aller sur VNC viewer, vous rendre dans le répertoire ou vous avez publié le site et lancer la commande

dotnet MonAppliDeNews.dll

Le 1er chargement sera un peu long mais ensuite ce sera rapide.

Et voilà nous avons maintenant un site fonctionnel sur raspberry.

 

5. Conclusion

Ce fut long mais le résultat est là. Nous avons vu beaucoup de concept différents sur ce tutoriel qui méritent d’être approfondis par ailleurs. Nous pouvons faire un site web complet, hébergé sur notre Raspberry et en C#. J’espère que ce tutoriel vous aura plu (Ce fut assez long à réaliser !)

Nous verrons dans un prochain tuto comment héberger de façon permanente notre site.

One thought on “Créer un site web .Net Core MVC razor hébergé sur Raspbian

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.