Home - About me - Browse by categories

[En][ASP.NET MVC] Remote attribute and asynchronous validation

As you probably know ASP.NET MVC supports .NET Framework 4 Data Annotations to validate user’s inputs (Required, StringLength, Range, RegularExpression…)

Another attribute exists and allows asynchronous client side validation. It’s the Remote attribute. For example, it can be used to validate e-mail and username in a registration form and alert the user before the form is posted.

Here is a sample model for a registration form :

public class UserModel
{
[Required]
[Remote("CheckUsername", "RemoteValidation", ErrorMessage = "This username is already used.")]
public string Username { get; set; }

[Required]
[Remote("CheckEmail", "RemoteValidation", ErrorMessage = "This e-mail is already used.")]
public string Email { get; set; }

[StringLength(80)]
public string FirstName { get; set; }

[StringLength(80)]
public string LastName { get; set; }

public DateTime BirthDate { get; set; }
}

As you can see in the previous code snippet, the Remote attribute takes two parameters :

  • the MVC action that should validate the input

  • the controller that defines the action

The controller :

public class RemoteValidationController : Controller
{
private readonly string[] _existingUsernames = {"beedoo", "julien", "jcorioland"};
private readonly string[] _existingEmails = { "[email protected]" };

public JsonResult CheckUsername(string username) {
bool userIsAvailable = !_existingUsers.Contains(username);
return Json(userIsAvailable, JsonRequestBehavior.AllowGet);
}

public JsonResult CheckEmail(string email)
{
bool emailIsAvailable = !_existingEmails.Contains(email);
return Json(emailIsAvailable, JsonRequestBehavior.AllowGet);
}
}

These actions are very simple : they take the input to validate as a string parameter and return a Json-serialized Boolean that indicates if the input is valid or not. For this article I’ve used two arrays to represent users and e-mails databases but it’s possible to execute any code here.

The view :

@using (Html.BeginForm())
{
<div class="editor-label">@Html.LabelFor(m => m.Username)
<div class="editor-field">
@Html.TextBoxFor(m => m.Username)
@Html.ValidationMessageFor(m => m.Username)

<div class="editor-label">@Html.LabelFor(m => m.Email)
<div class="editor-field">
@Html.TextBoxFor(m => m.Email)
@Html.ValidationMessageFor(m => m.Email)

<div class="editor-label">@Html.LabelFor(m => m.FirstName)
<div class="editor-field">
@Html.TextBoxFor(m => m.FirstName)
@Html.ValidationMessageFor(m => m.FirstName)

<div class="editor-label">@Html.LabelFor(m => m.LastName)
<div class="editor-field">
@Html.TextBoxFor(m => m.LastName)
@Html.ValidationMessageFor(m => m.LastName)

<div class="editor-label">@Html.LabelFor(m => m.BirthDate)
<div class="editor-field">
@Html.TextBoxFor(m => m.BirthDate)
@Html.ValidationMessageFor(m => m.BirthDate)

<input type="submit" value="S'enregistrer" />
}

To activate the client side validation the following jQuery plugins should be loaded :

<head>
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery-ui-1.8.11.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
</head>

The validation actions are automatically called (asynchronously of course) when the user is completing the register form :

image

Hope this helps image

read more

[ASP.NET MVC] Attribut Remote et validation asynchrone

Comme vous le savez peut-être déjà, ASP.NET MVC 3 supporte les DataAnnotations du .NET Framework 4 pour tout ce qui est validation des entrées utilisateurs : Required, StringLength, Range, RegularExpression…

Il existe un attribut un peu moins connu : Remote. Celui-ci permet de lancer une validation asynchrone côté client. Par exemple, sur un formulaire d’inscription il est fréquent de vouloir valider de manière asynchrone qu’un nom d’utilisateur / e-mail n’existe pas déjà en base utilisateur.

Voilà un exemple de modèle pour parvenir à cela :

public class UserModel
{
[Required]
[Remote("CheckUsername", "RemoteValidation", ErrorMessage = "Ce nom d'utilisateur est déjà utilisé")]
public string Username { get; set; }

[Required]
[Remote("CheckEmail", "RemoteValidation", ErrorMessage = "Cet e-mail est déjà utilisé")]
public string Email { get; set; }

[StringLength(80)]
public string FirstName { get; set; }

[StringLength(80)]
public string LastName { get; set; }

public DateTime BirthDate { get; set; }
}

Comme il est possible de le constater dans l’extrait de code ci-dessus, l’attribut remote prend deux paramètres :

  • L’action MVC en charge de la validation

  • Le contrôlleur dans lequel l’action est défini (ici, RemoteValidationController)

Voilà le code de ce contrôlleur :

public class RemoteValidationController : Controller
{
private readonly string[] _existingUsernames = {"beedoo", "julien", "jcorioland"};
private readonly string[] _existingEmails = { "[email protected]" };

public JsonResult CheckUsername(string username) {
bool userIsAvailable = !_existingUsers.Contains(username);
return Json(userIsAvailable, JsonRequestBehavior.AllowGet);
}

public JsonResult CheckEmail(string email)
{
bool emailIsAvailable = !_existingEmails.Contains(email);
return Json(emailIsAvailable, JsonRequestBehavior.AllowGet);
}
}

Les actions sont ultra simples : elles prennent en paramètre le terme à valider et retourne un booléean, sérialisé en Json.

NB : ici la source d’emails/usernames existants a été simplifiée, mais un appel à une base de données est tout à fait possible.

La vue pour tester :

@using (Html.BeginForm())
{
<div class="editor-label">@Html.LabelFor(m => m.Username)
<div class="editor-field">
@Html.TextBoxFor(m => m.Username)
@Html.ValidationMessageFor(m => m.Username)

<div class="editor-label">@Html.LabelFor(m => m.Email)
<div class="editor-field">
@Html.TextBoxFor(m => m.Email)
@Html.ValidationMessageFor(m => m.Email)

<div class="editor-label">@Html.LabelFor(m => m.FirstName)
<div class="editor-field">
@Html.TextBoxFor(m => m.FirstName)
@Html.ValidationMessageFor(m => m.FirstName)

<div class="editor-label">@Html.LabelFor(m => m.LastName)
<div class="editor-field">
@Html.TextBoxFor(m => m.LastName)
@Html.ValidationMessageFor(m => m.LastName)

<div class="editor-label">@Html.LabelFor(m => m.BirthDate)
<div class="editor-field">
@Html.TextBoxFor(m => m.BirthDate)
@Html.ValidationMessageFor(m => m.BirthDate)

<input type="submit" value="S'enregistrer" />
}

Enfin, assurez vous de charger les plugins jQuery UI et jQuery validate pour que cela fonctionne :

<head>
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery-ui-1.8.11.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
</head>

Dès lors, les actions de validation sont appelées en asynchrone lors de la saisie du formulaire, permettant ainsi une aide à la saisie avancée pour l’utilisateur !

image

NB : et comme toujours, la validation côté client ne doit absolument pas remplacer la validation côté serveur !

A bientôt image

read more

[ASP.NET MVC] Authentification et utilisateurs anonymes

Lorsque l’on développe une application web nécessitant de l’authentification, plusieurs stratégies sont possibles, par exemple :

  • On autorise l’accès anonyme partout, sauf sur les pages nécessitant que l’utilisateur soit authentifié
  • On refuse l’accès anonyme partout sauf sur les pages ne nécessitant pas que l’utilisateur soit authentifié

Depuis ASP.NET MVC 2 il est possible de réaliser la première option très facilement. En effet, il suffit de placer un filtre d’authorisation sur les méthodes des contrôleurs (entendre action) pour lesquelles il faut que l’utilisateur soit authentifié. Il s’agit de l’attribut AuthorizeAttribute :

[Authorize]
public ActionResult Admin()
{
//code
return View();
}

Sur certains projets, seules quelques pages ne nécessitent pas que l’utilisateur soit activée, il devient donc contraigant d’avoir à placer l’attribut Authorize sur TOUTES les méthodes de contrôleur. ASP.NET MVC 3 introduit un nouveau concept : les filtres globaux. Il s’agit en fait de la possibilité d’appliquer des filtres sur toutes les actions de tous les contrôleurs, par exemple.

Pour cela, il suffit de venir ajouter le filtre en question dans la liste des filtres globaux. Ceci se fait dans le fichier Global.asax. Depuis la version 3 d’ASP.NET MVC, une nouvelle méthode est générée : **RegisterGlobalFilters **:

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new AuthorizeAttribute());
}

Cette méthode est ensuite appelée dans le Application_Start du même fichier :

protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();

RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}

Si vous testez ce code, vous verrez que plus rien ne s’affiche, même pas la page d’authentification puisque vous demandez que l’utilisateur soit authentifié partout.

Pour rendre le process un peu plus permissif, il suffit de créer un nouvel attribut AnonymousAttribute qui servira à marquer les actions ne nécessitant pas que l’utilisateur soit authentifié :

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class AnonymousAttribute : Attribute
{
}

A présent, il est possible de dériver le filtre AuthorizeAttribute et de surcharger la méthode OnAuthorization de celui-ci. Dans cette surcharge, il est possible de vérifier sur le contexte du filtre si l’action possède ou non l’attribut Anonymous décrit ci-dessus :

public class AuthenticationRequiredAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
bool anonymousAllowed = filterContext.ActionDescriptor.IsDefined(typeof (AnonymousAttribute), false);

if (!anonymousAllowed)
base.OnAuthorization(filterContext);
}
}

Du coup, il suffit d’enregister ce filtre plutôt que le AuthorizeAttribute dans le Global.asax

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new AuthenticationRequiredAttribute());
}

Puis de placer l’attribut Anonymous partout ou vous souhaitez conserver une accès anonyme (par exemple les actions LogOn) :

[Anonymous]
public ActionResult LogOn()
{
return View();
}

[HttpPost]
[Anonymous]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
//code
}

Du coup, le comportement désiré est bien obtenu : toutes les actions nécessitent que l’utilisateur soit authentifié, sauf celles où pour lesquelles l’accès anonyme est autorisé explicitement.

En espérant que cela serve à certains d’entre vous !

A bientôt image

read more

MishraReader Beta 1 dans les bacs !

MishraReader est un client GoogleReader écrit en WPF 4.0, respectant le design Metro et disponible sur CodePlex (sources et exécutable) : http://mishrareader.codeplex.com

image

A l’initiative de David Catuhe ce projet regroupe une dizaine de développeurs français. J’y ai d’ailleurs moi-même écrit quelques modestes lignes de codes image

De nombreuses fonctionnalités ont été inclues dans cette première béta, pour une liste complète de celles-ci je vous invite à lire le billet de David sur son blog.

Pour tester l’application, rendez-vous sur CodePlex !

N’hésitez pas à faire des retours d’utilisation / propositions de fonctionnalités.

Bonne lecture et à bientôt image

read more

[Visual Studio] Développement de règles d'archivage personnalisées pour TFS

Visual Studio permet d’exécuter du code avant chaque opération d’archivage afin de valider qu’un certain nombre de conditions soit réunies pour autoriser (ou non) le développeur à archiver son code : c’est ce qu’on appel des règles d’archivage (ou checkin policies).

Il existe déjà des règles out of the box qu’il est possible d’activer en faisant un clic droit sur le projet d’équipe pour lequel vous souhaitez activer une règle dans le Team Explorer, en sélectionnant l’entrée de menu Team Project Settings puis Source Control…

image

Il est également possible de développer ses propres règles d’archivage. Pour cela, il vous faut installer le SDK Visual Studio (disponible via l’extension manager), si ce n’est pas déjà fait :

image

Créez une projet de type Class Library et ajoutez-y les références suivantes :

  • Microsoft.TeamFoundation.VersionControl.Client
  • Microsoft.TeamFoundation.WorkItemTracking.Client

Une checkin policy est en fait une classe qui dérive de **PolicyBase **et qui soit sérialisable. Pour l’exemple, nous allons créer une règle vérifiant qu’un work item ait bien été associé au changeset (déjà existante) mais en plus que l’état du work item n’est pas Closed :

[Serializable]
public class SampleCheckinPolicy : PolicyBase
{
public override string Description
{
get { return "Cette règle d'archivage vérifie qu'aucun work item associé au changeset n'est en état Closed."; }
}

public override bool Edit(IPolicyEditArgs policyEditArgs)
{
return true;
}

public override PolicyFailure[] Evaluate()
{
throw new NotImplementedException();
}

public override string Type
{
get { return "SampleCheckinPolicy"; }
}

public override string TypeDescription
{
get { return "Cette règle d'archivage vérifie qu'aucun work item associé au changeset n'est en état Closed."; }
}
}

PolicyBase est une classe abstraite, voilà les méthodes / propriété à redéfinir :

  • Description / Type Description : information à propos de la règle (pour affichage dans les interfaces de VS)

  • Type : nom de la règle (pour affichage également)

  • Edit : permet d’afficher une fenêtre dans le cas où l’on souhaite que la règle soit configurable

  • Evaluate : vérifie les conditions de la règles et retourne une liste d’erreur, si la règle n’est pas validée

La classe PolicyBase expose une propriété PendingChecking sur laquelle il est possible de récupérer les work items associées, les notes d’archivage… Il est donc possible de vérifier que des work items sont associés et qu’aucun n’est en état Closed :

public override PolicyFailure[] Evaluate()
{
var failures = new List<PolicyFailure>();

if (PendingCheckin.WorkItems.CheckedWorkItems.Any())
{
foreach (var workItemCheckinInfo in PendingCheckin.WorkItems.CheckedWorkItems)
{
if(workItemCheckinInfo.WorkItem.State == "Closed")
{
string message = String.Format("Le workitem #{0} est en état Closed. Archivage impossible",
workItemCheckinInfo.WorkItem.Id);

failures.Add(new PolicyFailure(message,this));
}
}
}
else
{
failures.Add(new PolicyFailure("Aucun work item n'est associé au changeset !", this));
}

return failures.ToArray();
}

Tant que la méthode Evaluate retourne des erreurs, Visual Studio empêchera l’opération d’archivage (sauf si l’utilisateur contourne explicitement la règle).

La règle est prête, il ne reste plus qu’à la packager !

Pour cela ajoutez un projet d’extensibilité VSIX à la solution :

image

Ajoutez une référence vers la librairie contenant la règle à ce projet et vérifiez bien que la valeur Copy Local de cette référence est à vrai.

Il faut maintenant ajouter une fichier pkgdef au projet VSIX dans lequel sera renseigné l’enregistrement de la checkin policy dans le registre Windows. Ce fichier doit porter le nom du projet VSIX (SampleCheckinPolicyPackage.pkgdef, dans mon cas). Placez la propriété Include in VSIX à vrai dans les propriétés du fichier.

[$RootKey$\TeamFoundation\SourceControl\Checkin Policies]
"SampleCheckinPolicy"="$PackageFolder$\SampleCheckinPolicy.dll"

Attention : Le nom de la clé doit correspondre à celui de la dll !

Il ne reste plus qu’à lancer le projet VSIX en debug dans l’instance expérimentale de Visual Studio ou d’installer le VSIX produit lors de la génération du projet.

Rendez-vous alors dans les paramètres du source contrôles pour le projet d’équipe désiré : vous devriez pouvoir ajouter la règle :

image

Tentez un archivage sans associer de Work Item ou en associant un Work Item en état Closed sur ce projet. Vous devriez voir apparaître les messages d’erreur de la règle dans la fenêtre Pending Changes :

image   image

Téléchargez le code d’exemple : SampleCheckinPolicy.zip

A bientôt image

read more