Bookmarks dans Visual Studio

icon Tags de l'article : , ,

Aout 17, 2018
Woaw, je viens de découvrir l'existence des bookmarks dans Visual Studio...

Comme vous le savez peut-être, je suis un adepte des raccourcis clavier et de l'efficacité... mais je ne m'étais jamais posé la question de l'existence de bookmarks dans Visual Studio.

Pourtant c'est un besoin fréquent, noter un emplacement pour y revenir... A l'origine je me débrouillais en pinnant un fichier ou avec des breakpoints désactivés... Sauf qu'il y a une feature pensée pour !

Son fonctionnement est méga simple :

CTRL + K + K : Mettre/enlever un bookmark
CTRL + K + N : Aller au bookmark suivant
CTRL + K + N : Aller au bookmark précédent
CTRL + K + W : Afficher la liste des bookmarks

La liste ressemble à ça, et elle permet même de nommer ses bookmarks pour s'y retrouver plus facilement !.



En bref, une feature incontournable !

Architecture d'application web : SPA ou classique (en pages)

icon Tags de l'article : , , ,

Aout 16, 2018
Hello les gens,

Aujourd'hui je vais répondre à une question que beaucoup de développeurs se posent quand ils vont commencer un nouveau projet web : dois-je tenter l'aventure SPA (Single Page Application) ou dois-je rester sur une architecture classique (en pages web).

Pour répondre à ça, j'ai décidé de faire un petit listing des points positifs et négatifs d'une application SPA par rapport à une application classique.
A noter que c'est ma vision perso, après 6 ans à travailler sur des sites en pages web et 4 ans sur une application SPA.

Positif
  • Un code plus propre, mieux organisé, qui force à la rigueur
  • Un code JS beaucoup plus clair et parfaitement intégré aux vues
  • Moins de requêtes (stockage JS, moins de données à envoyer en permanence au serveur)
  • Possibilité d'avoir énormément de choses côté client (gros traitements, calculs, process en X étapes, etc.)
  • Maintenance et évolution plus facile
  • Pour l'utilisateur : une navigation plus agréable dans l'application

Négatif
  • Quel framework choisir ? Angular ? Vue.js ? React ? Un autre ?
  • Demande plus de rigueur en permanence (découpage en fichiers, architecture, dépendances JS, etc.)
  • Un temps de dev plus long (temps d'adaptation au langage, plus de javascript, temps pour trouver quel morceau de code correspond à quoi, plus de classes à écrire, ...)
  • Un debug plus compliqué au quotidien
  • Référencement et Routing
  • Monitoring des performances et tests d'intrusion moins évidents
  • Problématiques de mise à jour (si la personne ne refresh jamais, comment forcer la mise à jour sans lui faire perdre des données en cours de saisie ?)
  • Pour l'utilisateur : un premier chargement plus long

Pour moi, si j'avais à choisir entre une architecture SPA vs design classique, je poserais deux questions :
  • L'utilisateur de l'application fera-t-il de grosses sessions dessus ? (20 minutes ou plus)
  • Ais-je le temps et les moyens d'investir dans une SPA ? (car rien que la structure SPA rajoutera des problématiques à gérer au jour le jour)

Si vous avez un OUI aux deux questions, vous pouvez partir sur une architecture de type SPA sans hésiter. Le confort d'utilisation apporté à l'utilisateur par une SPA vaut vraiment le détour pour une application sur laquelle l'utilisateur passera des heures.

Après, personnellement, j'ai tendance à préférer une approche entre les deux :
  • Un site classique, découpé en pages
  • Chaque page utilisant un modèle de vue JS et un moteur de binding (dans mon cas Knockout)

Pourquoi ?
Après 4 ans passés à travailler sur une SPA, je me suis rendu compte que revenir à un design en pages web ne me posait pas de soucis. Au contraire, le développement devient plus simple.
La vraie problématique que je retrouve... c'est le JavaScript "à l'ancienne". Avoir un peu partout des méthodes qui font des modifications dans l'interface rend le tout extrêmement compliqué à relire et à améliorer.
Garder un design en pages web auquel on ajouterait un moteur de binding JS me parait être le meilleur compromis d'entre les deux mondes.

Après, évidemment... tout dépend de la taille de votre projet, de vos moyens... et de vos goûts ;)

Bonne journée et bon dev à tous/toutes !

Créer un template Razor utilisable en JavaScript

icon Tags de l'article : , , ,

Aout 15, 2018
Hello tout le monde,

Aujourd'hui on va voir comment créer un template Razor utilisable en JavaScript.
C'est une problématique assez courante, et la solution que je propose vient d'une implémentation sur un projet pro... et le résultat est pas si mal.

A noter qu'on est à des années lumières d'un vrai moteur de template, et je ne peux que vous recommander d'en implémenter un si c'est un vrai besoin de votre projet.
Cette solution n'est proposée que pour dépanner, ou à utiliser sur un projet où on ne peut/veut implémenter de moteur de template.


Du coup, allons-y !

Tout d'abord, on va créer une vue partielle pour notre template :

@model Projet.ObjetHtmlTemplate

<div class="objet">
    <h1>@Model.Name</h1>
    <p>@Model.Description</p>
    <span class="price">@Model.Price</span>
</div>

Nous allons maintenant créer notre ViewModel, ici ObjetHtmlTemplate.

Deux choses sont cependant à noter sur ce template :
  • Tous ces champs sont des strings
  • Le constructeur va permettre une initialisation des champs avec une chaine en dur contenant des antiquotes et le nom de la variable.

Voyons la classe avant que j'explique le "pourquoi" :

public class ObjetHtmlTemplate
{
    public string Name { get; set; }
	
	public string Description { get; set; }
	
	public string Price { get; set; }
	
	public ObjetHtmlTemplate(bool initForJs = false)
	{
		if(initForJs) 
		{
			Name = "` + Name + `";
			Description = "` + Description + `";
			Price = "` + Price + `";
		}
	}
}

Voilà. Je pense que vous commencez à voir où je veux en venir : nous allons juste créer une méthode en JS qui prendra en paramètres Name, Description et Price, et cela renverra... le HTML généré préalablement par Razor... mais avec nos valeurs JS !

La prochaine étape est donc ce petit morceau de JavaScript dans notre vue :

<script>
	var getObjetHtml = function(Name, Description, Price) {
		return `@Model.Partial("_ObjetHtmlTemplate", new ObjetHtmlTemplate(true))`;
	};
</script>

Magique non ? Maintenant on a une jolie méthode JavaScript "getObjetHtml" qui prend 3 paramètres, et qui va renvoyer le HTML généré à partir de ces 3 paramètres. <3

Et côté ASP.Net, on peut utiliser notre vue partielle comme à notre habitude :

<ul>
@foreach(var item in Model.Items)
{
	<li>@Html.RenderPartial("_ObjetHtmlTemplate", new ObjetHtmlTemplate() { Name = item.Name, Description = item.Description, Price = Math.Round(item.Price, 2) })<li>
}
</ul>

Et... c'est tout ! Simple et efficace, que demander de plus ?!

Allez, bonne journée et bon dev à tous/toutes !

Manipuler une collection de sous-objets dans une vue ASP.Net MVC

icon Tags de l'article : , , ,

Aout 13, 2018
Hello les devs !

Aujourd'hui on va parler d'une problématique assez commune : envoyer des sous-objets au serveur depuis une vue CSHTML.

En effet, quand on développe un site avec ASP.Net MVC, il peut arriver qu'on ait besoin, dans notre vue, de manipuler des objets qui sont en fait... une collection d'objets liés à notre objet principal.

Exemple : ma classe Guerrier qui a une propriété "Inventaire" contenant une collection d'"Objet".

Dans ma vue, j'aimerais donc pouvoir voir les objets dans l'inventaire, et si besoin... en ajouter/supprimer.

Comment faire ça ?

Et bien en fait c'est ultra simple : le moteur d'ASP.Net MVC sait tout à fait mapper des sous-objets à partir de nommages façon "tableau".

Exemple : <input type="text" name="Guerrier.Inventaire[0].Nom" value="Epée des mille vérités" />

Grâce à la syntaxe "Inventaire[0].Nom", le moteur d'ASP.Net va comprendre de lui-même qu'il s'agit du premier objet dans la liste "Inventaire" et va essayer de le créer et mapper ses propriétés.

Quelques choses à noter tout de même :
  • Attention à la numérotation, si vous avez juste un sous-objet numéroté [1], il ne sera pas mappé et récupéré.
  • Côté serveur vous ne récupérerez que ce qui a été envoyé par le client. S'il s'agit de données à sauvegarder, vous devrez faire une comparaison entre ce que vous avez en base et ce que l'utilisateur a envoyé.
  • Si vous n'avez pas de valeurs, vous n'aurez pas d'objets.
  • Si vous n'avez pas d'objets, vous ne récupérerez pas une collection vide, mais null.
  • Si jamais les champs sont disabled, ils ne seront ni récupérés, ni mappés (pratique pour activer/désactiver un objet en javascript).

Voilà,

Bonne semaine et bon dev à tous/toutes !

Ecrire des tests unitaires pour une méthode de contrôleur d'API qui attend un fichier (ou comment mocker HttpContext)

icon Tags de l'article : , ,

Juillet 02, 2018
Hello tout le monde,

Aujourd'hui un cas de dev très spécifique : comment tester unitairement une méthode de contrôleur MVC / d'API qui attend un fichier ?

Exemple de méthode :

[HttpPost]
[Route("fromDocument")]
public IHttpActionResult CreateFromDocument()
{
	if (!Request.Content.IsMimeMultipartContent())
	{
		return InternalServerError(Resource.Error_RequestNotMimeMultipartContent);
	}

	if (HttpContext.Current.Request.Files.Count != 1)
	{
		return BadRequest(Resource.Error_FileContentIncorrect);
	}

	// We will only consider the first file
	HttpPostedFile uploadedFile = HttpContext.Current.Request.Files[0];

	if (uploadedFile.ContentLength > ConstantApplicationSettings.UploadFileSizeLimitInBytes)
	{
		return BadRequest();
	}

	uploadedFile.InputStream.Position = 0;

	string originFileName = uploadedFile.FileName.Replace("\"", string.Empty);
	_itemBusinessLogic.CreateDocument(item.Id, originFileName, uploadedFile.InputStream, CurrentUserId);

	return Ok();
}

Comment tester cette méthode ? En effet, de l'extérieur le HttpContext n'est pas accessible. Du coup comment faire croire au contrôleur qu'il recoit bien un fichier alors que ce n'est pas le cas ?

Pour ça, il faut passer par plusieurs étapes.

Déjà, la première chose importante à assimiler : il ne faut PAS mocker HttpContext.
La classe est vraiment énorme et embarque énormément de choses. La mocker ou la stubber c'est prendre le risque d'avoir du code qui casse en production sans qu'on s'en rende compte, malgré des tests unitaires parfaits.

Non, la vraie bonne pratique c'est d'interfacer nos besoins d'objets liés à HttpContext. Nous allons donc créer une interface IHttpContextAdapter :

public interface IHttpContextAdapter
{
	HttpFileCollectionBase GetFiles();
}

Comme vous le voyez, nous allons renvoyer un HttpFileCollectionBase. Pas un HttpFileCollection car il s'agit d'une classe sealed, et donc impossible à mock sans passer par de l'interception.

Du coup, implémentons cette interface dans la classe qui nous servira dans nos contrôleurs d'API :

public class HttpContextAdapter : IHttpContextAdapter
{
    public HttpFileCollectionBase GetFiles()
    {
        return new HttpFileCollectionWrapper(HttpContext.Current.Request.Files);
    }
}

Voilà. Comme vous l'avez compris, le HttpFileCollectionWrapper va permettre de transformer le HttpFileCollection en HttpFileCollectionBase, classe qu'on peut mocker / stubber car non sealed.

Du coup, injectons maintenant dans notre constructeur cette interface, et utilisons la :

public class ItemService : ApiControllerBase, IItemService
{
	public readonly IItemBusinessLogic _itemBusinessLogic;
	public readonly IHttpContextAdapter _httpContextAdapter;

    public ItemService(IItemBusinessLogic itemBusinessLogic, IHttpContextAdapter httpContextAdapter)
    {
		_itemBusinessLogic = itemBusinessLogic;
        _httpContextAdapter = httpContextAdapter;
    }
	
	[...]
}

Voilà. Et du coup, au lieu d'appeler HttpContext.Current, nous allons appeler notre _httpContextAdapter :

// avant modification
if (HttpContext.Current.Request.Files.Count != 1)
[...]
HttpPostedFile uploadedFile = HttpContext.Current.Request.Files[0];

// après modification
if (_httpContextAdapter.GetFiles().Count != 1)
[...]
HttpPostedFileBase uploadedFile = _httpContextAdapter.GetFiles().Get(0);

Ca avance, ça avance.

Mais on a toujours un problème : lorsque le contrôleur va vérifier le fichier censé être en entrée... il va faire un Request.Content.IsMimeMultipartContent()...
Or... le mock de notre HttpContext ne suffira pas à faire passer cette méthode.

Heureusement, j'ai trouvé une méthode d'extension qui permet de faire ça !
Elle va tout simplement réinitialiser le ControllerContext de notre ApiController en ajoutant un "faux" fichier de 100 bytes, avec les bons headers :

public static void InitializeMimeMultipartContent(this ApiController controller, string fileName = "filename.txt")
{
	var user = controller.User;
	Uri uri = controller.Request.RequestUri;

	var configuration = new HttpConfiguration();
	var request = new HttpRequestMessage(HttpMethod.Post, string.Empty);
	var content = new MultipartFormDataContent();

	var fileContent = new ByteArrayContent(new byte[100]);
	fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
	{
		FileName = fileName
	};
	content.Add(fileContent);
	request.Content = content;
	request.Properties[HttpPropertyKeys.HttpConfigurationKey] = configuration;

	controller.ControllerContext = new HttpControllerContext(new HttpConfiguration(), new HttpRouteData(new HttpRoute(string.Empty)), request);

	controller.User = user;
	controller.Request.RequestUri = uri;
}

La seule chose à noter est que, comme on va réinitialiser le ControllerContext, on perdra des données qui sont probablement actuellement utilisées (l'utilisateur courant, l'url courante, etc.), d'où le fait qu'on mette de côté l'utilisateur et l'url avant la réinitialisation, pour les remettre dans le contrôleur ensuite.

Et voilà, nous avons tous les outils pour pouvoir tester efficacement notre méthode !

Voici donc un test unitaire commenté utilisant tout ce qu'on vient de coder (avec RhinoMocks, mais vous devriez pouvoir adapter ce code à votre moteur de mocking) :

[Test]
public void ItemController_CreateFromDocument_WithValidData_Ok()
{
	string fileName = "file.png";
	
	// on va d'abord initialiser le contrôleur pour faire croire qu'il s'agit d'un MimeMultipartContent
	_itemController.InitializeMimeMultipartContent(fileName);

	// ensuite on va mocker les appels à GetFile() de IHttpContextAdapter
	HttpFileCollectionBase files = MockRepository.GenerateStub<HttpFileCollectionBase>();
    HttpPostedFileBase file = MockRepository.GenerateStub<HttpPostedFileBase>();
	_httpContextAdapter.Stub(p => p.GetFiles()).Return(files);
	files.Stub(p => p.Get(0)).Return(file);

	// ici on mocke le "nombre" de fichiers envoyés
	files.Stub(p => p.Count).Return(1);
	
	// maintenant, on mocke le fichier "envoyé", avec les propriétés checkées par notre contrôleur
	file.Stub(p => p.FileName).Return(fileName);
	file.Stub(p => p.ContentLength).Return(50000);
	
	// on pense aussi à renvoyer un stub pour le stream (pour éviter que les appels au stream ne plantent)
	file.Stub(p => p.InputStream).Return(MockRepository.GenerateStub<Stream>());

	// Une fois le test prêt, on appelle notre méthode de contrôleur
	HttpResponseMessage httpResponseMessage = _itemController.CreateFromDocument().GetResponse();

	// Et on n'a plus qu'à vérifier que la réponse est bien celle attendue, et que la couche métier a été appelé
	ApiControllerAssert.IsOk(httpResponseMessage);
	_itemBusinessLogic.AssertWasCalled(p => p.CreateDocument(
		Arg<string>.Is.Equal(fileName),
		Arg<Stream>.Is.NotNull,
		Arg<int>.Is.Equal(CurrentUserId)));
}

En espérant que ce soit utile à quelqu'un :)

Bon dev tout le monde !

Chiffrer / déchiffrer très facilement des données avec la classe MachineKey en .Net 4.5

icon Tags de l'article : ,

Mai 17, 2018
Salut à tous,

Aujourd'hui on va parler chiffrement. Et depuis .Net 4.5, c'est fichtrement simple (je vous épargnerai l'historique complet du "pourquoi c'est plus simple et mieux" depuis .Net 4.5, mais vous pouvez le trouver ici).

En effet, pour chiffrer/déchiffrer du texte, vous aurez besoin de 2 choses :
  • une entrée MachineKey dans votre app.config ou web.config.
  • la classe MachineKey

Première étape donc, vous devez ajouter une ligne dans votre fichier de config, avec vos clefs de chiffrement. Exemple :
<machineKey validationKey="78BEF3A7DF5611DF7D5A4ABF373C539B6A9E7323B13103D5F66A38653686543C084D7574F6D180C9F17AE7F4E4D72C7CD47725946589788C5A8EC916223A9E9B" decryptionKey="A8B2C28F08ADB2D18DAC0E5A642AA90B8BF6A5864499A88ADB7C459D96B885B9" validation="SHA1" decryption="AES" /> 

Vous pouvez générer ce genre d'entrée très facilement grâce à ce site : http://www.allkeysgenerator.com/Random/ASP-Net-MachineKey-Generator.aspx
(ou encore directement via IIS)

Par défaut l'algorithme sera AES, mais vous pouvez changer pour DES ou 3DES si vous préférez (même si je vous conseillerai plutôt de garder AES).

Ensuite, niveau C#... ben rien de plus simple. Vous n'avez plus qu'à appeler la classe MachineKey du namespace System.Web.Security, et ses méthodes Protect et Unprotect :

// ici on va chiffrer notre chaine de caractères
string purpose = "USER_ID"; 
byte[] bytesToEncrypt = Encoding.UTF8.GetBytes(stringToEncrypt);

byte[] encryptedBytes = MachineKey.Protect(bytesToEncrypt, purpose);
string encryptedString = Convert.ToBase64String(encryptedBytes);

// [...]
// ici on va déchiffrer notre chaine de caractères
string purpose = "USER_ID";
byte[] encryptedBytes = Convert.FromBase64String(encryptedData);

string decryptedBytes = MachineKey.Unprotect(encryptedBytes, purpose);
string decryptedString = null;

if (decryptedBytes != null)
{
    decryptedString = Encoding.UTF8.GetString(decryptedBytes);
}

Dernière précision : l'attribut purpose correspond à un tableau de chaines de caractères qui servent à décrire les raisons pour lesquelles on chiffre ces données (comme une sorte de rôle). Cet attribut, s'il est passé au chiffrement, doit être passé également au déchiffrement.
En bref : on met une string propre à la donnée ou au composant, afin de s'assurer que lui seul arrivera à déchiffrer cette donnée, et pas d'autres composants / morceaux de l'application.

Voilà, c'est tout pour moi, bonne journée et bon dev à tous et toutes !

Quelques outils que tout bon développeur .Net devrait utiliser sur ses projets

icon Tags de l'article : , , , ,

Mai 15, 2018
J'ai entendu dire, il y a longtemps, que Bill Gates avait dit :



Et c'est vrai. Quelqu'un qui n'aime pas travailler inutilement trouvera toujours des voies pour travailler plus efficacement et supprimer les tâches chiantes inutiles.

Au fil des années, je me suis rendu compte que trop peu de développeurs se posaient une question pourtant évidente : quelle tâche quotidienne et contraignante pourrais-je simplifier dans mon process de développement/test ?

La réponse est souvent : ben merde y'en a plein en fait !!!

Qu'il s'agisse de purger la base de données, de créer un compte utilisateur, de remplir la BDD avec de fausses données... il y a souvent énormément de tâches qu'un développeur pourrait automatiser pour vite gagner un maximum de temps. Et si en plus ce développement est mutualisé entre les développeurs... ben c'est un gain énorme pour toute l'équipe.

Pour ça, je voulais partager avec vous les outils que tout bon développeur .Net devrait utiliser pour ça.

1) Une extension Visual Studio

Coder sa propre extension Visual Studio n'est pas forcément évident... mais ça peut vous permettre de gagner un maximum de temps en ajoutant directement dans VS les choses chiantes que vous devez faire régulièrement.

Quelques exemples (dont certains tirés de l'extension VS de mon poste actuel) :
  • purger la base de données
  • créer un compte utilisateur de test
  • démarrer le noeud Selenium
  • tuer tous les process Chrome Selenium qui restent
  • changer la configuration de la BDD (local / tests de perfs / preprod)
  • etc.

Avoir ces options directement dans votre Visual Studio peut vous faire gagner un temps précieux. Car même juste 2 minutes par jour, sur une année ça représente une journée entière de travail...

2) Le #if DEBUG

En local, on a parfois besoin d'avoir des fonctionnements différents du fonctionnement en production. Que ce soit à cause du cache, de la quantité de données, ou même de l'initialisation de la base de données... on a parfois envie d'avoir un fonctionnement différent, pour se simplifier la vie.

Voici quelques exemples d'actions que j'ai pu automatiser par le passé, grâce au #if DEBUG :
  • au lieu d'aller chercher le catalogue complet de données en BDD (ce qui prenait 2 minutes à chaque lancement de l'application), on le stocke sérialisé sur le disque dur et on l'utilise comme tel (bim, 10 secondes de chargement au lieu de 2 minutes)
  • création automatique d'un compte par défaut si aucun compte n'existe déjà
  • connexion automatique au compte par défaut
  • extraction, anonymisation et stockage sérialisé dans des fichiers de données venant de la production, afin de pouvoir faire des calculs statistiques sans avoir à requêter directement la BDD de prod
  • forcer la connexion à la BDD locale

3) Un dossier de ressources

Là on est dans l'évidence, mais parfois ça ne fait pas de mal de rappeler les évidences : prenez des notes.

Vous galérez à trouver une procédure spécifique ? Notez là quand vous avez enfin trouvé.
Un collègue vous montre une procédure compliquée pour faire quelque chose ? Notez là.
Vous découvrez gadget super cool que vous pourriez avoir à utiliser plus tard ? Mettez le de côté.

Quand on développe, il faut aussi savoir prendre des notes. Personnellement, j'ai toujours un dossier de ressources (personnelles) chiffré.

Qu'est ce qu'on trouve dans le mien ? Enormément de choses :
  • Les logins/mdp pour chaque compte partagé lié à notre application
  • Des scripts windows/powershell pour certaines actions (backup d'une base de données, restauration, création d'un certificat)
  • Comment faire certaines actions simples/complexes dans la solution
  • Des scripts SQL pour certaines actions BDD (purge et réindex Azure, identifier la fragmentation des index, batch de suppression des anciens logs, etc.)
  • Des rappels sur certains concepts qui ont du mal à rentrer (oui, même après plus de 15 ans de dev, ça doit être le grand âge)

Ca peut sembler idiot, car souvent en 2/3 minutes sur stackoverflow on peut retrouver la solution... mais :
1) Ca permet d'avoir du spécifique pour notre situation
2) Si nos fichiers sont bien triés et nommés, on gagne du temps même par rapport à stackoverflow
3) Ca peut vous sauver la vie si vous devez refaire une opération compliquée (prévue à l'origine comme un one-shot, vous savez, le célèbre "ne t'en fais pas, c'est juste pour cette fois" d'il y a 6 mois)

4) Les tests unitaires

Bon, je ne vais pas insister sur l'importance des tests unitaires dans un projet, mais sachez ceci : vous pouvez très bien utiliser des tests unitaires pour automatiser certaines actions de dev/debug.

Quelques exemples :
  • Tester la connectionstring du projet
  • Vérifier que toutes les procédures stockées appelées sont bien présentes dans la BDD
  • Tester une procédure stockée
  • Vérifier qu'une convention de codage est bien respectée sur le projet (quand on a des règles spécifiques)

Ou même encore : créer des données en dur en base. Après tout pourquoi pas ?

4) Un logiciel d'automatisation dans le navigateur

Lorsqu'on fait du dev web, on a souvent besoin de remplir des formulaires pour tester certaines choses. Et remplir 10 fois d'affilée les même 10 champs... ben ça fatigue très vite.

Or... il existe une solution simple : les extensions Chrome/Firefox d'automatisation. Par exemple iMacros.

Ces extensions permettent d'enregistrer un enchainement d'actions faites dans le navigateur pour les reproduire ensuite. Grâce à ça, vous pourrez créer un compte, vous logger, remplir en formulaire, etc... en un seul clic.

5) Les tests Selenium

Même si les tests Selenium sont, à la base, conçus pour tester votre application, vous pouvez les utiliser pour de nombreuses choses pas forcément évidentes à première vue :
  • Tester une API
  • Tester l'interconnexion entre 2 applications
  • Aller vérifier l'état d'un autre service en ligne
  • Créer une surcouche de monitoring (quand vous n'avez qu'un board figé de monitoring et pas d'API)
  • ...

6) Les macros de Notepad++

Si vous ne les avez jamais utilisé, sachez que Notepad++ permet d'enregistrer toutes les actions produites sur une ligne, pour ensuite les reproduire sur toutes les lignes d'un fichier.

Vous avez un fichier où le format ne va pas du tout, vous devez passer un bloc à gauche, supprimer quelques caractères et rajouter un morceau à la fin ? Record => modifications => Fin => Jouer en boucle jusqu'à la fin => terminé.

Voilà, je pense que ça fait déjà pas mal,

Je vous ai épargné les grands classiques, comme les environnements d'intégration continue, car si vous n'en avez pas déjà, il y a de fortes chances que ce soit indépendant de votre volonté (je voulais concentrer cet article sur ce que les développeurs peuvent faire eux-même, sur leur poste).

J'espère que cet article vous a plu, bonne journée et bon dev à tous et toutes !

Comment Dispose un HttpResponseMessage (et gérer l'erreur CA2000 de FxCop)

icon Tags de l'article : ,

Mars 29, 2018
Yo,

Petit souci sur lequel je suis tombé aujourd'hui : j'utilisais un using autour de mon HttpResponseMessage :

using(var response = new HttpResponseMessage(HttpStatusCode.OK);
{
     //...
     return response;
}

Evidemment... ça ne marchait pas. Le Dispose était fait avant que la requête ne parte chez l'utilisateur, et donc... ben j'essayais d'accéder à un objet disposed.

Sauf que si je retire mon using, j'ai une magnifique erreur FxCop CA2000 qui apparait...

Du coup comment faire ?

Après quelques recherches, j'ai trouvé plusieurs informations intéressantes :
  • Ce n'est, évidemment, pas obligatoire de dispose le HttpResponseMessage dans le cas où tout se passe bien. La requête sera dispose, et par conséquent l'objet HttpResponseMessage également. Le GarbageCollector est de plus là pour les restes ;)
  • Si jamais on veut planifier le Dispose pour s'assurer qu'il soit fait (sans attendre le GC), alors on peut utiliser Request.RegisterForDispose(response);
  • Dans tous les cas, l'erreur CA2000 apparaitra car dans le cas d'une exception, ben... notre HttpResponseMessage ne sera pas disposed.

Ce warning FxCop est un peu inutile quand on manipule des HttpResponseMessage dans un contexte de requêtes d'API.
Ce warning est là, normalement, pour forcer à utiliser des usings, mais là... on est dans une situation où un using ne marche pas, donc inadapté.

Ma suggestion ?
Désactiver l'erreur CA2000 pour cette méthode.

Et si vraiment vous ne pouvez/voulez pas... Vous pouvez toujours vous assurer qu'un Dispose sera fait, comme ceci :

var response = new HttpResponseMessage(HttpStatusCode.OK);
try
{
     Request.RegisterForDispose(response);
     //...
     return response;
}
catch
{
     response.Dispose();
     
     throw;
}

Bon dev et bonne journée tout le monde !

Ranger les using "System" avant les autres, dans Visual Studio

icon Tags de l'article : , ,

Mars 27, 2018
Salut,

Si vous êtes comme moi, vous aimez bien faire les choses.

Or, en C#, on sait qu'il faut toujours mettre les using "System" avant les autres. C'est une bonne pratique, ça permet de s'y retrouver plus facilement, et de gagner en clarté/lisibilité.

Sauf que voilà, sans resharper... c'est chiant d'avoir à déplacer les using pour les trier automatiquement.

Sauf que voilà... y'a pas à le faire à la main.

Visual Studio le gère automatiquement aujourd'hui. Il faut juste l'activer.

Allez donc dans Tools => Settings, puis dans C# => Advanced.

Là, vous n'avez plus qu'à cocher la case "Place 'System' directives first when sorting usings".



Et c'est fait !

Bon dev et bonne journée tout le monde !

S'authentifier en POST avec un WebClient

icon Tags de l'article : ,

Aout 09, 2016
Hello,

Lorsqu'on est comme moi, on a tendance à aimer faire des tools qui vont scraper des pages web à l'aide d'un WebClient C#.

Sauf que voilà, il faut parfois pouvoir s'authentifier pour accéder à des pages qu'on aimerait parser... Et pour ça. Il y a une solution ! :)

Déjà, il va vous falloir une classe dérivée de WebClient qui gère les cookies et l'authentification :

// source : http://stackoverflow.com/questions/11118712/webclient-accessing-page-with-credentials
public class CookieAwareWebClient : WebClient
{
    public CookieAwareWebClient()
    {
        CookieContainer = new CookieContainer();
    }
    public CookieContainer CookieContainer { get; private set; }

    protected override WebRequest GetWebRequest(Uri address)
    {
        var request = (HttpWebRequest)base.GetWebRequest(address);
        request.CookieContainer = CookieContainer;
        return request;
    }
}

Maintenant, vous n'avez plus qu'à utiliser cette classe pour vous authentifier avec un UploadValues (en lui passant vos paramètres POST) :

using (var client = new CookieAwareWebClient())
{                
    // on spécifie que c'est du POST
    client.Headers.Add("Content-Type", "application/x-www-form-urlencoded");

    // cette ligne est ajoutée car parfois les authentifications vérifient le referer
    client.Headers.Add("Referer", "https://referer/if/needed");

    // on crée notre table clef / valeur
    var values = new NameValueCollection
    {
        { "username", "us3rn4me" },
        { "password", "p4ssw0rd" }
    };

    // on s'authentifie
    client.UploadValues("https://url/de/la/page/d/authentification", values);

    // et voilà, on n'a plus qu'à appeler les pages qui nous intéressent, accessibles désormais vu qu'on s'est authentifiés avec le UploadValues !
    var pageContent = client.DownloadString(url);
}

Et le tour est joué !

Bon dev à tous !