Création d'un helper pouvant contenir du HTML

Depuis le tout début, ASP.NET MVC fournit des helpers permettant de créer des liens vers des actions ou des routes particulières. Et depuis le tout début, ces helpers n’autorisent rien d’autre que du texte. Si on veut mettre du html, il ne faut pas utiliser ces helpers (ou faire d’immondes bidouilles)

La solution la plus simple est de créer un simple lien html, en utilisant le Helper Url

<a href="@Url.Action("Index")"><img src="image.png"/></a>

Mais malheureusement, ceci n’est pas possible lorsqu’on veut créer des liens Ajax.

Vu que MVC est open-source, j’ai décidé d’aller voir un peu comment sont fait ces helpers, et d’en créer un nouveau, sur le modèle du BeginForm, permettant d’insérer du HTML dans un lien.
Le package MvcHtmlLink - permettant de créer des liens avec du html - est disponibles sur NuGet ; le code source et une mini doc sont sur GitHub

Le fonctionnement est simple : il faut créer un object IDisposable. Dans son constructeur on génère le tag d’ouverture, et dans le Dispose le tag de fermeture. Lorsqu’on l’utilise dans une vue Razor MVC avec un using, le html écrit entre les accolades sera donc au milieu de notre tag html.

@using (Html.BeginActionLink("Index", new { Id = 1 }, new { @class = "some-css-class" }))
{
	<span>Html link</span>
}

va générer :

<a class="some-css-class" href="/Home/Index/1">Mvc RouteLink with params</a>

Pour faire ça, j’ai créé une classe HtmlLink :

public class HtmlLink : IDisposable
{
    private readonly ViewContext _viewContext;

    public HtmlLink(ViewContext viewContext, string targetUrl)
    {
        _viewContext = viewContext;			
        _viewContext.Writer.Write("<a href=\"" + targetUrl + "\">");
		// Ici le lien doit être créé correctement, les attributs html et les options Ajax
    }

    public void Dispose()
    {
        _viewContext.Writer.Write("</a>");
    }
}

Cette classe doit recevoir le ViewContext d’ASP.NET MVC, c’est ce qui va permettre d’écrire dans la page.
La façon dont est générée le lien est reprise du code d’ASP.NET, et est visible sur GitHub.

Pour pouvoir ensuite utiliser cette classe, on va créer les helpers. Pour qu’ils soient plus facile d’accès, on va les créer sous forme de méthodes d’extension de la classe HtmlHelper, et dans le même namespace, afin qu’elles apparraissent sur l’objet @Html et @Ajax des vues MVC.

namespace System.Web.Mvc.Html
{
    public static class HtmlLinkHelper
    {
        public static HtmlLink BeginActionLink(this HtmlHelper htmlHelper, string actionName, string controllerName)
        {
            string targerUrl = UrlHelper.GenerateUrl(null, actionName, controllerName, null, htmlHelper.RouteCollection, htmlHelper.ViewContext.RequestContext, true);
            return new HtmlLink(htmlHelper, targetUrl);
        }
        
        public static HtmlLink BeginRouteLink(this HtmlHelper htmlHelper, string routeName)
        {
            string targerUrl = UrlHelper.GenerateUrl(null, routeName, null, null, htmlHelper.RouteCollection, htmlHelper.ViewContext.RequestContext, true);
            return new HtmlLink(htmlHelper, targetUrl);
        }
    }
}

Je n’ai mis que deux exemples pour illustrer le principe, mais il faut créer toutes les différentes surcharges.

Cette fois c’est bon, notre helper peut être utilisé dans nos vues.

Si vous souhaitez créer des helpers de ce type, vous voyez donc que ce n’est pas compliqué, si vous souhaitez juste utiliser ces méthodes BeginActionLink ou BeginRouteLink, vous avez juste à référencer le package NuGet

Activer HTTPS sur IIS avec Let's Encrypt (Mode Facile)

Il y a déjà un an, j’avais fait un article expliquant comment activer HTTPS sur IIS. Il est temps aujourd’hui d’en faire une nouvelle version, car depuis quelques jours est arrivé Let’s Encrypt.

Let’s Encrypt est une nouvelle autorité de certification qui fournit gratuitement, et de manière totalement automatisée, des certificats pour sécuriser les sites internet. Chaque certificat est valide pour un seul sous-domaine, et pour une durée de 90 jours. Mais la création et le renouvellement étant automatisés, ce n’est pas vraiment un problème. Si par contre vous avez besoin de certificats plus avancés (wildcards, vérification de l’identité), il faudra aller voir une autre autorité de certification.

Fonctionnement : la théorie

Avant de délivrer un certificat, l’autorité de certification doit vérifier que le demandeur est bien propriétaire du nom de domaine. Cela peut se faire par l’envoi d’un e-mail, ou l’ajout d’une entrée dns. Let’s Encrypt fait cette vérification automatiquement en proposant un outil à installer sur le serveur, qui va créer un fichier dans le site web, et en voyant s’il est bien accessible : si c’est bon, on sait que le domaine est valide.

Une fois le domaine validé, ce même outil va faire tout le nécessaire : créer une Cert Request, l’envoyer à Let’s Encrypt, récupérer le certificat, et configurer le serveur web. Puis s’enregistrer sur le serveur pour refaire tout ça avant que le certificat n’expire 3 mois plus tard.

Cet outil a été créé par Let’s Encrypt, mais il n’existe pas de version officielle pour IIS. Heureusement, il en existe des versions non-officielles, qui marchent plutot bien.

Le client Let’s Encrypt pour IIS

Il existe plusieurs clients pour Windows, j’en ai choisi un simple à utiliser : letsencrypt-win-simple.

Téléchargez la dernière release, et dézippez la sur votre serveur. Attention à l’endroit où vous le mettez, il faudra laisser l’exécutable en place, car il sera appelé régulièrement pour gérer les renouvellements des certificats. Lancez letsencrypt.exe avec les droits administrateurs ; il va se connecter à votre IIS pour voir les sites actifs.

Scanning IIS 7 Site Bindings for Hosts
 1: IIS blog.adhess.net (C:\inetpub\wwwroot\redirblog)
 2: IIS blog.lacasa.fr (C:\inetpub\wwwroot\blog)
 3: IIS www.lacasa.fr (C:\inetpub\wwwroot\www)
 
 M: Generate a certificate manually.
 A: Get certificates for all hosts
 Q: Quit
Which host do you want to get a certificate for:

Choisissez le numéro correspondant au domaine pour lequel vous voulez créer le certificat, ou ‘A’ pour tous les faire. Vous pouvez aussi faire ‘M’ pour en créer un manuellement, mais IIS ne se mettra pas à jour.

Attention, dans la version actuelle, la validation peut ne pas fonctionner correctement à cause de la gestion des fichiers statiques dans IIS. Le message d’erreur suivant apparait :

This could be caused by IIS not being setup to handle extensionless static
files. Here's how to fix that:
1. In IIS manager goto Site/Server->Handler Mappings->View Ordered List
2. Move the StaticFile mapping above the ExtensionlessUrlHandler mappings.
(like this http://i.stack.imgur.com/nkvrL.png)"

Si vous avez ce message, vous pouvez suivre les indications pour corriger le problème, mais ça risque d’empêcher le fonctionnement des applications ASP.NET MVC. Le bug est connu et sera probablement corrigé dans les prochains jours - en attendant la version corrigée peut être téléchargée ici : [https://github.com/glacasa/letsencrypt-win-simple/releases/tag/v1.7.1]

Une fois que c’est fait, une tâche planifiée s’est enregistrée, qui va vérifier chaque jour si il est temps de renouveler un certificat. Vous n’avez plus rien à faire.

Désactivation de SSL, et activation de TLS

J’en parlais dans mon article de l’an dernier, et c’est toujours d’actualité : si vous ne l’avez pas encore fait sur votre serveur, il vous faut activer TLS et désactiver SSL, pour des raisons de sécurité. Ces protocoles ne sont pas liés à Let’s Encrypt, et sont gérés par Windows. Il faut modifier certaines clés de registre, il vous suffit de taper les lignes suivantes dans dans une invite de commande avec les droits administrateurs :

REG ADD "HKLM\System\CurrentControlSet\Control\SecurityProviders\SChannel\Protocols\SSL 2.0\Server" /v Enabled /t REG_DWORD /d 0 /f
REG ADD "HKLM\System\CurrentControlSet\Control\SecurityProviders\SChannel\Protocols\SSL 2.0\Client" /v Enabled /t REG_DWORD /d 0 /f

REG ADD "HKLM\System\CurrentControlSet\Control\SecurityProviders\SChannel\Protocols\SSL 3.0\Server" /v Enabled /t REG_DWORD /d 0 /f
REG ADD "HKLM\System\CurrentControlSet\Control\SecurityProviders\SChannel\Protocols\SSL 3.0\Client" /v Enabled /t REG_DWORD /d 0 /f

REG ADD "HKLM\System\CurrentControlSet\Control\SecurityProviders\SChannel\Protocols\TLS 1.0\Server" /v Enabled /t REG_DWORD /d 1 /f
REG ADD "HKLM\System\CurrentControlSet\Control\SecurityProviders\SChannel\Protocols\TLS 1.0\Server" /v DisabledByDefault /t REG_DWORD /d 0 /f
REG ADD "HKLM\System\CurrentControlSet\Control\SecurityProviders\SChannel\Protocols\TLS 1.0\Client" /v Enabled /t REG_DWORD /d 1 /f
REG ADD "HKLM\System\CurrentControlSet\Control\SecurityProviders\SChannel\Protocols\TLS 1.0\Client" /v DisabledByDefault /t REG_DWORD /d 0 /f

REG ADD "HKLM\System\CurrentControlSet\Control\SecurityProviders\SChannel\Protocols\TLS 1.1\Server" /v Enabled /t REG_DWORD /d 1 /f
REG ADD "HKLM\System\CurrentControlSet\Control\SecurityProviders\SChannel\Protocols\TLS 1.1\Server" /v DisabledByDefault /t REG_DWORD /d 0 /f
REG ADD "HKLM\System\CurrentControlSet\Control\SecurityProviders\SChannel\Protocols\TLS 1.1\Client" /v Enabled /t REG_DWORD /d 1 /f
REG ADD "HKLM\System\CurrentControlSet\Control\SecurityProviders\SChannel\Protocols\TLS 1.1\Client" /v DisabledByDefault /t REG_DWORD /d 0 /f

REG ADD "HKLM\System\CurrentControlSet\Control\SecurityProviders\SChannel\Protocols\TLS 1.2\Server" /v Enabled /t REG_DWORD /d 1 /f
REG ADD "HKLM\System\CurrentControlSet\Control\SecurityProviders\SChannel\Protocols\TLS 1.2\Server" /v DisabledByDefault /t REG_DWORD /d 0 /f
REG ADD "HKLM\System\CurrentControlSet\Control\SecurityProviders\SChannel\Protocols\TLS 1.2\Client" /v Enabled /t REG_DWORD /d 1 /f
REG ADD "HKLM\System\CurrentControlSet\Control\SecurityProviders\SChannel\Protocols\TLS 1.2\Client" /v DisabledByDefault /t REG_DWORD /d 0 /f

Ceci n’a besoin d’être fait qu’une seule fois sur votre serveur - jusqu’à la prochaine faille de sécurité d’un de ces protocoles.

Déploiement automatique d'un projet ASP.net avec TFS

Nous avons vu dans un précedent article comment installer un serveur de Build et lancer des compilations automatiquement, aujourd’hui nous allons nous servir de ce serveur pour déployer une application ASP.net.

Encore une fois, cet article est basé sur mon usage personnel. Il ne faut pas voir cet article comme un “tutorial” pas à pas, mais comme un exemple des possibilités apportées par TFS Build, à vous ensuite d’en tirer le maximum pour vos projets !

Automatisation du déploiement

Pour pouvoir déployer, je me base sur Web Deploy, qui permets de déployer une application dans IIS directement depuis Visual Studio. La configuration de Web Deploy dépasse le cadre de cet article, je vous renvoie donc sur la documentation officielle si vous souhaitez plus d’informations. (Vous pouvez utilisez un autre système de déploiement, tant que vous passez par les profils de publication Visual Studio)

Maintenant, dans Visual Studio, faites un clic droit sur le projet, et “Publish”. Un assistant de publication va se lancer, qui va vous permettre de configurer le déploiement.

Assistant de publication

Mettez les bonnes valeurs, et fermez l’assistant. Un fichier .pubxml est créé dans le répertoire /Properties/PublishProfiles/ de votre projet. C’est ce fichier qui va permettre au serveur de build d’effectuer le déploiement.

Maintenant, retournez dans Team Explorer, et créez une nouvelle définition de build. Si vous ne vous souvenez plus comment faire, je vous laisse relire mon précédent article.
Il va falloir faire des changements par rapport au mode intégration continue : déjà changez le trigger (il est rare de vouloir déployer à chaque check-in), pour passer en manuel.
Ensuite, allez modifier la section Process, à Build-Advanced, vous pouvez spécifier des arguments à faire passer à MSBuild. Ajoutez la ligne suivante :

/p:DeployOnBuild=true;PublishProfile=[nom du profil de publication];Password=[mot de passe du serveur]

MSBuild arguments

Les arguments passés sont les suivants :

  • DeployOnBuild=true : demande à MS Build d’effectuer le déploiement lorsque la compilation est terminée
  • PublishProfile : le nom du profil de publication créé dans Visual Studio (le nom du fichier .pubxml, sans l’extension)
  • Password : le mot de passe de l’utilisateur pour se connecter au serveur (nécessaire ici, car il n’est pas enregistré dans le fichier .pubxml)

Et c’est bon, vous devez avoir maintenant deux définitions de Build : une qui compile le projet à chaque check-in, et une qui compile et déploie le projet à la demande.

Pour effectuer un déploiement, vu qu’on a mis un trigger manuel, il faut faire un clic droit sur la définition de build et “Queue new build” (dans Visual Studio ou sur l’interface web).
Vous pouvez aussi par exemple faire un déploiement automatique quotidien sur un serveur de test (ou de prod, si vous êtes joueur) en changeant le trigger.

Déploiement à heure fixe

Quand j’ai parlé de déploiement automatisé, on m’a posé la question suivante : est-il possible de programmer un déploiement pour qu’il se fasse pendant la nuit ?
En effet, l’application qu’ils développent utilise de nombreuses variables de sessions, qui sont détruites lors du déploiement, et provoquent des bugs chez les utilisateurs. Pour déployer, ils restent donc tard le soir jusqu’à ce qu’il n’y ait plus d’utilisateur connecté.

La bonne solution à ce problème serait de stocker les variables de session sur un serveur d’état ou dans SQL Server ; mais cela nécessite de nombreux tests et corrections sur l’appli existante (tout objet stocké en session doit être sérialisable, ce qui n’est pas forcément le cas).
En attendant de terminer tout ça, j’ai donc fait un système de programmation de déploiement.

Si vous utilisez Git ou TFVC pour stocker vos codes sources, la manoeuvre sera différente. Nous allons voir les deux.

Avec Git

Lors de la création de la définition de build, vous avez sûrement vu dans la section Source settings que vous pouviez définir la branche à compiler. On va se servir de cette option pour notre déploiement.

Créez une nouvelle branche publication, et demandez à la définition de build de récupérer le code dans cette branche-là.

Branche de publication

Et modifiez le trigger pour que la publication se fasse chaque nuit. Ne cochez pas la dernière case, afin de ne pas lancer une publication s’il n’y a pas eu de modification dans la journée.

Trigger de publication

Votre définition de build est prête, lorsque vous souhaiterez déployer l’application, il vous suffira de pousser votre code dans la branche publication de TFS. Vous pouvez continuer à travailler sur d’autres branches sans problème.

Avec TFVC

Avec Team Foundation Version Control, on ne va pas pouvoir utiliser les branches, ça va être un peu plus compliqué. Comme il n’existe pas de moyen natif pour programmer une build, on va devoir ruser.

Au début de cet article, je vous ai fait faire un profil de déploiement utilisant Web Deploy. Il va falloir le changer : changez la méthode par Web Deploy Package. Au lieu de déployer directement, on va simplement créer un package.

Assistant de publication - package

Inutile de définir l’emplacement du package, TFS ne tiendra pas compte de cette valeur, il stockera le package avec les binaires compilés sur le serveur - par défaut dans C:\Builds.

Lancez une compilation, et allez chercher le package dans le serveur de Build. Le chemin devrait ressembler à quelque chose comme ça :

C:\Builds\12345\Projet\Deploiement différé Projet\bin\_PublishedWebsites\Projet_Package

Le chemin semble compliqué, mais vous devriez retrouver facilement. Les dossiers correspondent à l’id du serveur de compilation dans TFS, le nom du projet TFS, le nom de la définition de build. Le reste représente le résultat de la compilation.

Ce dossier contient plusieurs fichiers, les plus importants sont le fichier .zip, qui contient l’application prête à publier, et le fichier .cmd qui est un script qui effectuera le déploiement. Un fichier deploy-readme.txt est aussi présent, avec la documentation nécessaire au déploiement en ligne de commande.
Pour programmer notre déploiement, on va créer un fichier .bat qui va faire le déploiement, et créer une tâche planifiée dans Windows qui l’appellera tous les jours, à un horaire où personne n’est connecté.

cd C:\Builds\12345\Projet\Deploiement différé Projet\bin\_PublishedWebsites\Projet_Package

if exist Projet.deploy.cmd (
	Projet.deploy.cmd /Y /M:http://app.lacasa.fr/msdeploy.axd /U:deployUser /P:toto1234
	del Projet.deploy.cmd
)

Dans ce fichier .bat, on appelle le fichier .cmd en lui passant les paramètres nécessaires au déploiement : l’url du service Web Deploy, et le nom d’utilisateur et mot de passe du server.
Une fois le déploiement terminé, on supprime le fichier .cmd, pour ne pas republier le même projet chaque jour.

Enregistrez ce fichier, et ouvrez le planificateur de tâches (Exécuter - taskschd.msc). Créez une nouvelle tâche planifiée, qui se déclenche tous les jours, et qui exécute le fichier .bat

Trigger tâche planifiée Action tâche planifiée

Et c’est bon, vous pouvez lancer cette définition de build, qui va compiler le projet et créer un package, qui sera déployée tout seul la nuit prochaine.