Archive

Archive for the ‘ASP.Net MVC’ Category

Limiter l’exécution d’une action uniquement aux requêtes AJAX

En ASP.Net MVC nous manipulons entre autres des vues. Parmi ces vues certaines représentent des pages complètes et d’autres ne sont que des parties de page. Ces parties ou zones appartenant à une vue sont appelées des vues partielles et elles sont renvoyées elles aussi par des actions du contrôleur. Ces vues partielles ne devant être utilisées qu’à l’intérieur d’une vue alors le framework ASP.Net MVC nous permet de protéger l’appel à ces actions partielles en les décorant avec l’attribut ChildActionOnly. Cet attribut permet d’être sûr que l’action :

  • ne pourra pas être utilisée comme une vue entière et que les développeurs de l’application l’exécuteront toujours en passant par les méthodes HtmHelper.Action ou HtmlHelper.RenderAction.
  •  a une URL qui ne sera pas accessible via la barre d’adresse si un utilisateur, je ne sais par quel moyen, serait au courant de l’existence de cette URL.

Cependant comme tout site dynamique nous aurons des requêtes AJAX qui pourront faire elles aussi des demandes de contenu HTML sans qu’on ait besoin de charger la page de façon complète. Ce contenu aussi représente une partie puis qu’à la réception de la réponse du serveur Web nous devons incorporer ce bout de HTML quelque part dans la page. La requête AJAX envoyée au serveur invoquera certainement une action du contrôleur. Cette action comme celles marquées avec l’attribut ChildActionOnly doit possèder des restrictions soient :

  • obligation de passer par une requête faite en AJAX.
  • inaccessible via la barre d’adresse du navigateur.

Cependant le framework ASP.Net MVC n’offre aucun attribut nous permettant d’appliquer ces restrictions sur une action mais il nous fournit les outils pour en créer. Pour cela nous devons coder un filtre qui sera exécuté juste avant l’exécution de l’action concernée. Si la requête entrante respecte les conditions d’une requête faite en AJAX alors on laisse l’action continuer son chemin. Dans le cas où les conditions ne seraient pas respectées nous envoyons une page 404 (comme quoi l’URL est inexistante).

Le code du nouveau filtre que je nommerai AjaxOnlyAttribute, classe dérivée de la classe ActionFilterAttribute, est le suivant :

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class AjaxOnlyAttribute : ActionFilterAttribute
{
	public override void OnActionExecuting(ActionExecutingContext filterContext)
	{
		if (filterContext.HttpContext.Request.IsAjaxRequest())
		{
			base.OnActionExecuting(filterContext);
		}
		else
		{
			filterContext.HttpContext.Response.StatusCode = 404;
			filterContext.Result = new HttpNotFoundResult();
		}
	}
}

La nouvelle classe ne fait que surcharger la méthode qui nous intéresse. C’est à dire la méthode OnActionExecuting qui est appelée juste avant le début de l’exécution de l’action demandée par la requête entrante. L’attribut peut être posé sur le contrôleur pour atteindre l’ensemble des actions ou unitaire sur chaque action où la requête AJAX est obligatoire.

Pour éviter des soucis de pertes de quelques petites minutes qui peuvent arriver dans le cas où l’un des développeurs ayant rejoint l’équipe en cours de route et qui ne comprendrait pas pourquoi sa requête renvoie du 404 alors je pense que ce serait un avantage d’avoir un mode DEBUG activé par défaut pour les codeurs. Le code de notre classe ressemblerait finalement à celui-ci :

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class AjaxOnlyAttribute : ActionFilterAttribute
{
	public override void OnActionExecuting(ActionExecutingContext filterContext)
	{
		if (filterContext.HttpContext.Request.IsAjaxRequest())
		{
			base.OnActionExecuting(filterContext);
		}
		else
		{
			#if DEBUG
				filterContext.Result = new ViewResult { ViewName = "AjaxOnly" };
			#else
				filterContext.HttpContext.Response.StatusCode = 404;
				filterContext.Result = new HttpNotFoundResult();
			#endif
		}
	}
}

Etant donné que les développeurs sont amenés à compiler l’application la plupart du temps en mode DEBUG du coup la vue AjaxOnly contenant le texte qui va bien leur expliquera ce qu’il faut faire. Il faut noter que cette version de la classe fonctionne uniquement si une vue AjaxOnly.[cs|vb]html a été ajoutée dans le répertoire Views\Shared de l’application. Aussi on n’est pas obligé de renvoyer une vue partielle. On peut tout simplement remplacer le code suivant :

filterContext.Result = new ViewResult { ViewName = "AjaxOnly" };

par juste du contenu textuel :

filterContext.Result = new ContentResult
{
     Content = $"Cette action '{filterContext.HttpContext.Request.RawUrl}' a été conçue pour être utilisée uniquement via des requêtes AJAX"
};

La version utilisant la vue AjaxOnly a l’avantage de prendre en compte automatiquement le rendu _Layout par défaut défini au niveau de l’application.

Publicités
Catégories :ASP.Net MVC Étiquettes : , , ,

ASP.Net MVC : Eviter de polluer le modèle de la vue avec des messages d’erreurs

22 avril 2017 1 commentaire

J’ai récemment eu à répondre à une question posée sur le site StackOverflow. Je pose le contexte. Nous avons un modèle suivant:

public class ForgotPasswordMV
{
    [Display(Name = "Enter your email"), Required]
    public string Email { get; set; }
}

Ce modèle est utilisé dans l’action d’un contrôleur comme suit :

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Search(ForgotPasswordMV viewModel)
{
	if (Temp.Check(viewModel.Email))
		return RedirectToAction("VerifyToken", new { query = viewModel.Email });
	else
	{
		ViewBag.ErrorMessage = "Email not found or matched";
		return View();
	}
}

La question était de savoir si le fait d’utiliser la propriété dynamique ViewBag du contrôleur pour exposer le message d’erreur était une bonne pratique et que les recherches effectuées par le questionneur lui ont fait savoir qu’il fallait exposer une propriété au niveau du modèle.

Evidemment il est fortement recommandé de ne pas utiliser la propriété ViewBag étant donné qu’on ne bénéficie pas du typage fort. Si on veut communiquer avec la vue il faut toujours passer par un modèle typé. La solution proposée est donc légitime mais n’est pas une bonne pratique dans le cas de la gestion des erreurs du modèle dans le framework ASP.Net MVC.

La solution pour exposer les messages d’erreurs (comme dans l’exemple précédent qui n’utilise pas les attributs d’annotations de données) vient par l’utilisation de la méthode AddModelError de la classe ModelStateDictionary.  Nous n’avons pas besoin d’instancier cette classe étant donné qu’une propriété ModelState contenant une instance de cette classe existe déjà au niveau du contrôleur.

Du coup la bonne solution est de faire comme suit:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Search(ForgotPasswordMV viewModel)
{
    // ...
    else
    {
        this.ModelState.AddModelError("Email", "Email not found or matched");
        return View(viewModel);
    }
}

Il faut noter que cette méthode reçoit en premier paramètre le nom de la propriété du modèle à laquelle le message d’erreur est associé. Ainsi pour que pour ce message d’erreur soit affiché dans la vue à côté du champ Email il faut ajouter juste à côté de ce dernier la ligne suivante :

@Html.ValidationMessageFor(m => m.Email)

Cependant il est possible d’avoir un message d’erreur général à l’ensemble du modèle c’est à dire que ce message d’erreur n’est rattaché à aucune propriété du modèle. Pour cela il faudra utiliser une chaîne vide comme premier paramètre :

ModelState.AddModelError(String.Empty, "Email not found or matched");

Dans la vue Razor, il faudra utiliser la ligne suivante :

@Html.ValidationSummary(true, "The following error has occured:")

Le premier paramètre booléen indique qu’on ne veut pas afficher les messages d’erreur déjà rattachés à des propriétés du modèles.

Dans ce billet nous avons vu qu’on n’a pas besoin de polluer notre modèle et d’exposer des propriétés spécifiques aux messages d’erreur. Il suffit juste d’utiliser ce que nous offre le framework ASP.Net MVC pour nous faciliter la tâche.

J’espère que ce billet vous a été utile.

3 articles intéressants sur le développement ASP.Net MVC ;)

Voici 3 articles que j’ai trouvés très intéressants pour le développement ASP.Net MVC. Le deuxième peut être considéré comme une best practice ;). Ces 3 articles sont du même auteur Rachel Appel ( @RachelAppel sur Twitter ) :

=> http://goo.gl/Dh0S7 : Quand utiliser ViewBag, ViewData ou TempData vu que chacune de ces propriétés nous permet de stocker des données de notre application ?
=> http://goo.gl/AKNbV : Utilisation d’un ViewModel pour gérer et organiser les données dans une application ASP.Net MVC.
=> http://goo.gl/XDgYU : Comparaison entre les patterns MVC et MVVM suivant leur ViewModel.

Nota Béné : C’est en anglais 😉

Catégories :ASP.Net MVC Étiquettes :
%d blogueurs aiment cette page :